• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  SqlType,
27  TableAndColumn,
28  createTableColumnFromPerfettoSql,
29} from './sql_modules';
30import {SqlTableDescription} from '../../components/widgets/sql/table/table_description';
31import {TableColumn} from '../../components/widgets/sql/table/table_column';
32
33export class SqlModulesImpl implements SqlModules {
34  readonly packages: SqlPackage[];
35
36  constructor(docs: SqlModulesDocsSchema) {
37    this.packages = docs.map((json) => new StdlibPackageImpl(json));
38  }
39
40  findAllTablesWithLinkedId(tableAndColumn: TableAndColumn): SqlTable[] {
41    const linkedIdTables: SqlTable[] = [];
42    for (const t of this.listTables()) {
43      const allLinkedCols = t.linkedIdColumns;
44      if (
45        allLinkedCols.find(
46          (c) =>
47            c.type.tableAndColumn &&
48            c.type.tableAndColumn.isEqual(tableAndColumn),
49        )
50      ) {
51        linkedIdTables.push(t);
52      }
53    }
54    return linkedIdTables;
55  }
56
57  getTable(tableName: string): SqlTable | undefined {
58    for (const p of this.packages) {
59      const t = p.getTable(tableName);
60      if (t !== undefined) {
61        return t;
62      }
63    }
64    return;
65  }
66
67  listTables(): SqlTable[] {
68    return this.packages.flatMap((p) => p.listTables());
69  }
70
71  listTablesNames(): string[] {
72    return this.packages.flatMap((p) => p.listTablesNames());
73  }
74
75  getModuleForTable(tableName: string): SqlModule | undefined {
76    for (const stdlibPackage of this.packages) {
77      const maybeTable = stdlibPackage.getModuleForTable(tableName);
78      if (maybeTable) {
79        return maybeTable;
80      }
81    }
82    return undefined;
83  }
84}
85
86export class StdlibPackageImpl implements SqlPackage {
87  readonly name: string;
88  readonly modules: SqlModule[];
89
90  constructor(docs: DocsPackageSchemaType) {
91    this.name = docs.name;
92    this.modules = [];
93    for (const moduleJson of docs.modules) {
94      this.modules.push(new StdlibModuleImpl(moduleJson));
95    }
96  }
97
98  getTable(tableName: string): SqlTable | undefined {
99    for (const module of this.modules) {
100      for (const dataObj of module.dataObjects) {
101        if (dataObj.name == tableName) {
102          return dataObj;
103        }
104      }
105    }
106    return undefined;
107  }
108
109  listTables(): SqlTable[] {
110    return this.modules.flatMap((module) => module.dataObjects);
111  }
112
113  listTablesNames(): string[] {
114    return this.listTables().map((t) => t.name);
115  }
116
117  getModuleForTable(tableName: string): SqlModule | undefined {
118    for (const module of this.modules) {
119      for (const dataObj of module.dataObjects) {
120        if (dataObj.name == tableName) {
121          return module;
122        }
123      }
124    }
125    return undefined;
126  }
127
128  getSqlTableDescription(tableName: string): SqlTableDescription | undefined {
129    for (const module of this.modules) {
130      for (const dataObj of module.dataObjects) {
131        if (dataObj.name == tableName) {
132          return module.getSqlTableDescription(tableName);
133        }
134      }
135    }
136    return undefined;
137  }
138}
139
140export class StdlibModuleImpl implements SqlModule {
141  readonly includeKey: string;
142  readonly dataObjects: SqlTable[];
143  readonly functions: SqlFunction[];
144  readonly tableFunctions: SqlTableFunction[];
145  readonly macros: SqlMacro[];
146
147  constructor(docs: DocsModuleSchemaType) {
148    this.includeKey = docs.module_name;
149
150    const neededInclude = this.includeKey.startsWith('prelude')
151      ? undefined
152      : this.includeKey;
153    this.dataObjects = docs.data_objects.map(
154      (json) => new SqlTableImpl(json, neededInclude),
155    );
156
157    this.functions = docs.functions.map((json) => new StdlibFunctionImpl(json));
158    this.tableFunctions = docs.table_functions.map(
159      (json) => new StdlibTableFunctionImpl(json),
160    );
161    this.macros = docs.macros.map((json) => new StdlibMacroImpl(json));
162  }
163
164  getTable(tableName: string): SqlTable | undefined {
165    for (const obj of this.dataObjects) {
166      if (obj.name == tableName) {
167        return obj;
168      }
169    }
170    return undefined;
171  }
172
173  getSqlTableDescription(tableName: string): SqlTableDescription | undefined {
174    const sqlTable = this.getTable(tableName);
175    if (sqlTable === undefined) {
176      return undefined;
177    }
178    return {
179      imports: [this.includeKey],
180      name: sqlTable.name,
181      columns: sqlTable.getTableColumns(),
182    };
183  }
184}
185
186class StdlibMacroImpl implements SqlMacro {
187  readonly name: string;
188  readonly summaryDesc: string;
189  readonly description: string;
190  readonly args: SqlArgument[];
191  readonly returnType: string;
192
193  constructor(docs: DocsMacroSchemaType) {
194    this.name = docs.name;
195    this.summaryDesc = docs.summary_desc;
196    this.description = docs.desc;
197    this.returnType = docs.return_type;
198    this.args = [];
199    this.args = docs.args.map((json) => new StdlibFunctionArgImpl(json));
200  }
201}
202
203class StdlibTableFunctionImpl implements SqlTableFunction {
204  readonly name: string;
205  readonly summaryDesc: string;
206  readonly description: string;
207  readonly args: SqlArgument[];
208  readonly returnCols: SqlColumn[];
209
210  constructor(docs: DocsTableFunctionSchemaType) {
211    this.name = docs.name;
212    this.summaryDesc = docs.summary_desc;
213    this.description = docs.desc;
214    this.args = docs.args.map((json) => new StdlibFunctionArgImpl(json));
215    this.returnCols = docs.cols.map((json) => new StdlibColumnImpl(json));
216  }
217}
218
219class StdlibFunctionImpl implements SqlFunction {
220  readonly name: string;
221  readonly summaryDesc: string;
222  readonly description: string;
223  readonly args: SqlArgument[];
224  readonly returnType: string;
225  readonly returnDesc: string;
226
227  constructor(docs: DocsFunctionSchemaType) {
228    this.name = docs.name;
229    this.summaryDesc = docs.summary_desc;
230    this.description = docs.desc;
231    this.returnType = docs.return_type;
232    this.returnDesc = docs.return_desc;
233    this.args = docs.args.map((json) => new StdlibFunctionArgImpl(json));
234  }
235}
236
237class SqlTableImpl implements SqlTable {
238  name: string;
239  includeKey?: string;
240  description: string;
241  type: string;
242  columns: SqlColumn[];
243  idColumn: SqlColumn | undefined;
244  linkedIdColumns: SqlColumn[];
245  joinIdColumns: SqlColumn[];
246
247  constructor(docs: DocsDataObjectSchemaType, includeKey: string | undefined) {
248    this.name = docs.name;
249    this.includeKey = includeKey;
250    this.description = docs.desc;
251    this.type = docs.type;
252    this.columns = docs.cols.map((json) => new StdlibColumnImpl(json));
253
254    this.linkedIdColumns = [];
255    this.joinIdColumns = [];
256    for (const c of this.columns) {
257      if (c.type.name === 'id') {
258        this.idColumn = c;
259        continue;
260      }
261      if (c.type.shortName === 'id') {
262        this.linkedIdColumns.push(c);
263        continue;
264      }
265      if (c.type.shortName === 'joinid') {
266        this.joinIdColumns.push(c);
267        continue;
268      }
269    }
270  }
271
272  getIdColumns(): SqlColumn[] {
273    return this.columns.filter((c) => c.type.shortName === 'id');
274  }
275
276  getJoinIdColumns(): SqlColumn[] {
277    return this.columns.filter((c) => c.type.shortName === 'joinid');
278  }
279
280  getIdTables(): TableAndColumn[] {
281    return this.getIdColumns()
282      .map((c) => c.type.tableAndColumn)
283      .filter((tAndC) => tAndC !== undefined) as TableAndColumn[];
284  }
285
286  getJoinIdTables(): TableAndColumn[] {
287    return this.getJoinIdColumns()
288      .map((c) => c.type.tableAndColumn)
289      .filter((tAndC) => tAndC !== undefined) as TableAndColumn[];
290  }
291
292  getTableColumns(): TableColumn[] {
293    return this.columns.map((col) =>
294      createTableColumnFromPerfettoSql(col, this.name),
295    );
296  }
297}
298
299class StdlibColumnImpl implements SqlColumn {
300  name: string;
301  type: SqlType;
302  description: string;
303
304  constructor(docs: DocsArgOrColSchemaType) {
305    this.type = {
306      name: docs.type.toLowerCase(),
307      shortName: docs.type.split('(')[0].toLowerCase(),
308      tableAndColumn:
309        docs.table && docs.column
310          ? new TableAndColumnImpl(
311              docs.table.toLowerCase(),
312              docs.column.toLowerCase(),
313            )
314          : undefined,
315    };
316    this.description = docs.desc;
317    this.name = docs.name;
318  }
319}
320
321class StdlibFunctionArgImpl implements SqlArgument {
322  name: string;
323  description: string;
324  type: string;
325
326  constructor(docs: DocsArgOrColSchemaType) {
327    this.type = docs.type;
328    this.description = docs.desc;
329    this.name = docs.name;
330  }
331}
332
333export class TableAndColumnImpl implements TableAndColumn {
334  table: string;
335  column: string;
336  constructor(table: string, column: string) {
337    this.table = table;
338    this.column = column;
339  }
340  isEqual(o: TableAndColumn): boolean {
341    return o.table === this.table && o.column === this.column;
342  }
343}
344
345const ARG_OR_COL_SCHEMA = z.object({
346  name: z.string(),
347  type: z.string(),
348  desc: z.string(),
349  table: z.string().nullable(),
350  column: z.string().nullable(),
351});
352type DocsArgOrColSchemaType = z.infer<typeof ARG_OR_COL_SCHEMA>;
353
354const DATA_OBJECT_SCHEMA = z.object({
355  name: z.string(),
356  desc: z.string(),
357  summary_desc: z.string(),
358  type: z.string(),
359  cols: z.array(ARG_OR_COL_SCHEMA),
360});
361type DocsDataObjectSchemaType = z.infer<typeof DATA_OBJECT_SCHEMA>;
362
363const FUNCTION_SCHEMA = z.object({
364  name: z.string(),
365  desc: z.string(),
366  summary_desc: z.string(),
367  args: z.array(ARG_OR_COL_SCHEMA),
368  return_type: z.string(),
369  return_desc: z.string(),
370});
371type DocsFunctionSchemaType = z.infer<typeof FUNCTION_SCHEMA>;
372
373const TABLE_FUNCTION_SCHEMA = z.object({
374  name: z.string(),
375  desc: z.string(),
376  summary_desc: z.string(),
377  args: z.array(ARG_OR_COL_SCHEMA),
378  cols: z.array(ARG_OR_COL_SCHEMA),
379});
380type DocsTableFunctionSchemaType = z.infer<typeof TABLE_FUNCTION_SCHEMA>;
381
382const MACRO_SCHEMA = z.object({
383  name: z.string(),
384  desc: z.string(),
385  summary_desc: z.string(),
386  return_desc: z.string(),
387  return_type: z.string(),
388  args: z.array(ARG_OR_COL_SCHEMA),
389});
390type DocsMacroSchemaType = z.infer<typeof MACRO_SCHEMA>;
391
392const MODULE_SCHEMA = z.object({
393  module_name: z.string(),
394  data_objects: z.array(DATA_OBJECT_SCHEMA),
395  functions: z.array(FUNCTION_SCHEMA),
396  table_functions: z.array(TABLE_FUNCTION_SCHEMA),
397  macros: z.array(MACRO_SCHEMA),
398});
399type DocsModuleSchemaType = z.infer<typeof MODULE_SCHEMA>;
400
401const PACKAGE_SCHEMA = z.object({
402  name: z.string(),
403  modules: z.array(MODULE_SCHEMA),
404});
405type DocsPackageSchemaType = z.infer<typeof PACKAGE_SCHEMA>;
406
407export const SQL_MODULES_DOCS_SCHEMA = z.array(PACKAGE_SCHEMA);
408export type SqlModulesDocsSchema = z.infer<typeof SQL_MODULES_DOCS_SCHEMA>;
409