• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1--
2-- Copyright 2024 The Android Open Source Project
3--
4-- Licensed under the Apache License, Version 2.0 (the "License");
5-- you may not use this file except in compliance with the License.
6-- You may obtain a copy of the License at
7--
8--     https://www.apache.org/licenses/LICENSE-2.0
9--
10-- Unless required by applicable law or agreed to in writing, software
11-- distributed under the License is distributed on an "AS IS" BASIS,
12-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-- See the License for the specific language governing permissions and
14-- limitations under the License.
15
16-- sqlformat file off
17
18CREATE PERFETTO MACRO _ii_df_agg(x ColumnName, y ColumnName)
19RETURNS _ProjectionFragment AS __intrinsic_stringify!($x), input.$y;
20
21CREATE PERFETTO MACRO _ii_df_bind(x Expr, y Expr)
22RETURNS Expr AS __intrinsic_table_ptr_bind($x, __intrinsic_stringify!($y));
23
24CREATE PERFETTO MACRO _ii_df_select(x ColumnName, y Expr)
25RETURNS _ProjectionFragment AS $x AS $y;
26
27CREATE PERFETTO MACRO __first_arg(x Expr, y Expr)
28RETURNS Expr AS $x;
29
30CREATE PERFETTO MACRO _interval_agg(
31  tab TableOrSubquery,
32  agg_columns _ColumnNameList
33)
34RETURNS TableOrSubquery AS
35(
36  SELECT __intrinsic_interval_tree_intervals_agg(
37    input.id,
38    input.ts,
39    input.dur
40    __intrinsic_token_apply_prefix!(
41      _ii_df_agg,
42      $agg_columns,
43      $agg_columns
44    )
45  )
46  FROM (SELECT * FROM $tab ORDER BY ts) input
47);
48
49CREATE PERFETTO MACRO _interval_intersect(
50  tabs _TableNameList,
51  agg_columns _ColumnNameList
52)
53RETURNS TableOrSubquery AS
54(
55  SELECT
56    c0 AS ts,
57    c1 AS dur,
58    -- Columns for tables ids, in the order of provided tables.
59    __intrinsic_token_apply!(
60      __first_arg,
61      (c2 AS id_0, c3 AS id_1, c4 AS id_2, c5 AS id_3, c6 AS id_4),
62      $tabs
63    )
64    -- Columns for partitions, one for each column with partition.
65    __intrinsic_token_apply_prefix!(
66      _ii_df_select,
67      (c7, c8, c9, c10),
68      $agg_columns
69    )
70  -- Interval intersect result table.
71  FROM __intrinsic_table_ptr(
72    __intrinsic_interval_intersect(
73      __intrinsic_token_apply!(
74        _interval_agg,
75        $tabs,
76        ($agg_columns, $agg_columns, $agg_columns, $agg_columns, $agg_columns)
77      ),
78      __intrinsic_stringify!($agg_columns)
79    )
80  )
81
82  -- Bind the resulting columns
83  WHERE __intrinsic_table_ptr_bind(c0, 'ts')
84    AND __intrinsic_table_ptr_bind(c1, 'dur')
85    -- Id columns
86    AND __intrinsic_table_ptr_bind(c2, 'id_0')
87    AND __intrinsic_table_ptr_bind(c3, 'id_1')
88    AND __intrinsic_table_ptr_bind(c4, 'id_2')
89    AND __intrinsic_table_ptr_bind(c5, 'id_3')
90    AND __intrinsic_table_ptr_bind(c6, 'id_4')
91
92    -- Partition columns.
93    __intrinsic_token_apply_and_prefix!(
94      _ii_df_bind,
95      (c7, c8, c9, c10),
96      $agg_columns
97    )
98);
99
100CREATE PERFETTO MACRO _interval_intersect_single(
101  ts Expr,
102  dur Expr,
103  t TableOrSubquery
104)
105RETURNS TableOrSubquery AS
106(
107  SELECT
108  id_0 AS id,
109  ts,
110  dur
111  FROM _interval_intersect!(
112    ($t, (SELECT 0 AS id, $ts AS ts, $dur AS dur)),
113    ()
114  )
115);
116
117-- Given a list of intervals (ts, dur), this macro generates a list of interval
118-- end points as well as the intervals that intersect with those points.
119--
120-- e.g. input (10, 20), (20, 25)
121--
122--         10      30
123--        A |-------|
124--             B|----------|
125--             20         45
126--
127-- would generate the output
128--
129--   ts,dur,group_id,id,interval_ends_at_ts
130--   10,10,1,A,0
131--   20,10,2,A,0
132--   20,10,2,B,0
133--   30,15,3,A,1
134--   30,15,3,B,0
135--   45,0,4,B,1
136--
137-- Runtime is O(n log n + m), where n is the number of intervals and m
138-- is the size of the output.
139CREATE PERFETTO MACRO interval_self_intersect(
140  -- Table or subquery containing interval data.
141  intervals TableOrSubquery)
142RETURNS TableOrSubquery
143AS
144(
145  WITH RECURSIVE
146    _end_points AS (
147      SELECT
148        ts,
149        id,
150        TRUE AS is_start
151      FROM $intervals
152      UNION ALL
153      SELECT
154        ts + dur AS ts,
155        id,
156        FALSE AS is_start
157      FROM $intervals
158    ),
159    _with_next_ts AS (
160      SELECT
161        *,
162        LEAD(ts, 1, NULL) OVER (ORDER BY ts) AS next_ts
163      FROM _end_points
164      ORDER BY ts
165    ),
166    _group_by_ts AS (
167       SELECT
168         ts,
169         MAX(next_ts) AS next_group_ts,
170         ROW_NUMBER() OVER (ORDER BY ts) AS group_id
171       FROM _with_next_ts
172       GROUP BY ts
173    ),
174    _end_points_w_group_info AS (
175      SELECT *
176      FROM _with_next_ts
177      JOIN _group_by_ts USING (ts)
178    ),
179    -- Algorithm: Consider endpoints from left to right (increasing group_id).
180    -- As we scan, we keep a set of open intervals:
181    --    + if a new interval opens at ts, add it to the set
182    --    + if a current interval closes at ts, remove it from the set
183    -- At each timestamp (start or end), we record this set of open intervals
184    scan(group_id, ts, dur, id) AS (
185      -- Base case: we open intervals
186      SELECT
187        group_id,
188        ts,
189        IFNULL(next_group_ts - ts, 0) AS dur,
190        id
191      FROM _end_points_w_group_info
192      WHERE is_start = 1
193      UNION ALL
194      -- Recursive: look at intervals from previous sequence number
195      -- and keep all that remain open
196      SELECT
197        cur.group_id,
198        cur.ts,
199        IFNULL(next_group_ts - cur.ts, 0) AS dur,
200        prev.id
201      FROM
202        _end_points_w_group_info cur
203      JOIN
204        scan prev ON (cur.group_id = prev.group_id + 1)
205      WHERE
206        prev.id <> cur.id
207      -- this order by makes the join more efficient
208      ORDER BY group_id ASC
209  )
210  SELECT ts, dur, group_id, id, FALSE AS interval_ends_at_ts FROM scan
211  UNION ALL
212  SELECT
213    ts,
214    IFNULL(next_ts - ts, 0) AS dur,
215    group_id,
216    id,
217    TRUE AS interval_ends_at_ts
218  FROM _end_points_w_group_info WHERE is_start = 0
219);
220