• 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 m from 'mithril';
16import {SqlColumn} from './sql_column';
17import {MenuItem, PopupMenu} from '../../../../widgets/menu';
18import {SqlValue} from '../../../../trace_processor/query_result';
19import {isString} from '../../../../base/object_utils';
20import {sqliteString} from '../../../../base/string_utils';
21import {Icons} from '../../../../base/semantic_icons';
22import {copyToClipboard} from '../../../../base/clipboard';
23import {sqlValueToReadableString} from '../../../../trace_processor/sql_utils';
24import {Anchor} from '../../../../widgets/anchor';
25import {TableManager} from './table_column';
26
27export interface LegacySqlTableFilterOp {
28  op: string; // string representation of the operation (to be injected to SQL)
29  label: LegacySqlTableFilterLabel; // human readable name for operation
30  requiresParam?: boolean; // Denotes if the operator acts on an input value
31}
32
33export type LegacySqlTableFilterLabel =
34  | 'glob'
35  | 'equals to'
36  | 'not equals to'
37  | 'greater than'
38  | 'greater or equals than'
39  | 'less than'
40  | 'less or equals than'
41  | 'is null'
42  | 'is not null';
43
44export const LegacySqlTableFilterOptions: Record<
45  LegacySqlTableFilterLabel,
46  LegacySqlTableFilterOp
47> = {
48  'glob': {op: 'glob', label: 'glob', requiresParam: true},
49  'equals to': {op: '=', label: 'equals to', requiresParam: true},
50  'not equals to': {op: '!=', label: 'not equals to', requiresParam: true},
51  'greater than': {op: '>', label: 'greater than', requiresParam: true},
52  'greater or equals than': {
53    op: '>=',
54    label: 'greater or equals than',
55    requiresParam: true,
56  },
57  'less than': {op: '<', label: 'less than', requiresParam: true},
58  'less or equals than': {
59    op: '<=',
60    label: 'less or equals than',
61    requiresParam: true,
62  },
63  'is null': {op: 'IS NULL', label: 'is null', requiresParam: false},
64  'is not null': {
65    op: 'IS NOT NULL',
66    label: 'is not null',
67    requiresParam: false,
68  },
69};
70
71export const NUMERIC_FILTER_OPTIONS: LegacySqlTableFilterLabel[] = [
72  'equals to',
73  'not equals to',
74  'greater than',
75  'greater or equals than',
76  'less than',
77  'less or equals than',
78];
79
80export const STRING_FILTER_OPTIONS: LegacySqlTableFilterLabel[] = [
81  'equals to',
82  'not equals to',
83];
84
85export const NULL_FILTER_OPTIONS: LegacySqlTableFilterLabel[] = [
86  'is null',
87  'is not null',
88];
89
90function filterOptionMenuItem(
91  label: string,
92  column: SqlColumn,
93  filterOp: (cols: string[]) => string,
94  tableManager: TableManager,
95): m.Child {
96  return m(MenuItem, {
97    label,
98    onclick: () => {
99      tableManager.filters.addFilter({op: filterOp, columns: [column]});
100    },
101  });
102}
103
104// Return a list of "standard" menu items, adding corresponding filters to the given cell.
105export function getStandardFilters(
106  value: SqlValue,
107  c: SqlColumn,
108  tableManager: TableManager,
109): m.Child[] {
110  if (value === null) {
111    return NULL_FILTER_OPTIONS.map((label) =>
112      filterOptionMenuItem(
113        label,
114        c,
115        (cols) => `${cols[0]} ${LegacySqlTableFilterOptions[label].op}`,
116        tableManager,
117      ),
118    );
119  }
120  if (isString(value)) {
121    return STRING_FILTER_OPTIONS.map((label) =>
122      filterOptionMenuItem(
123        label,
124        c,
125        (cols) =>
126          `${cols[0]} ${LegacySqlTableFilterOptions[label].op} ${sqliteString(value)}`,
127        tableManager,
128      ),
129    );
130  }
131  if (typeof value === 'bigint' || typeof value === 'number') {
132    return NUMERIC_FILTER_OPTIONS.map((label) =>
133      filterOptionMenuItem(
134        label,
135        c,
136        (cols) =>
137          `${cols[0]} ${LegacySqlTableFilterOptions[label].op} ${value}`,
138        tableManager,
139      ),
140    );
141  }
142  return [];
143}
144
145function copyMenuItem(label: string, value: string): m.Child {
146  return m(MenuItem, {
147    icon: Icons.Copy,
148    label,
149    onclick: () => {
150      copyToClipboard(value);
151    },
152  });
153}
154
155// Return a list of "standard" menu items for the given cell.
156export function getStandardContextMenuItems(
157  value: SqlValue,
158  column: SqlColumn,
159  tableManager: TableManager,
160): m.Child[] {
161  const result: m.Child[] = [];
162
163  if (isString(value)) {
164    result.push(copyMenuItem('Copy', value));
165  }
166
167  const filters = getStandardFilters(value, column, tableManager);
168  if (filters.length > 0) {
169    result.push(
170      m(MenuItem, {label: 'Add filter', icon: Icons.Filter}, ...filters),
171    );
172  }
173
174  return result;
175}
176
177export function displayValue(value: SqlValue): m.Child {
178  if (value === null) {
179    return m('i', 'NULL');
180  }
181  return sqlValueToReadableString(value);
182}
183
184export function renderStandardCell(
185  value: SqlValue,
186  column: SqlColumn,
187  tableManager: TableManager | undefined,
188): m.Children {
189  if (tableManager === undefined) {
190    return displayValue(value);
191  }
192  const contextMenuItems: m.Child[] = getStandardContextMenuItems(
193    value,
194    column,
195    tableManager,
196  );
197  return m(
198    PopupMenu,
199    {
200      trigger: m(Anchor, displayValue(value)),
201    },
202    ...contextMenuItems,
203  );
204}
205