xref: /aosp_15_r20/external/perfetto/docs/analysis/perfetto-sql-syntax.md (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker# PerfettoSQL Syntax
2*6dbdd20aSAndroid Build Coastguard Worker*This page documents the syntax of PerfettoSQL, a dialect of SQL used in trace
3*6dbdd20aSAndroid Build Coastguard Workerprocessor and other Perfetto analysis tools to query traces.*
4*6dbdd20aSAndroid Build Coastguard Worker
5*6dbdd20aSAndroid Build Coastguard WorkerPerfettoSQL is a direct descendent of the
6*6dbdd20aSAndroid Build Coastguard Worker[dialect of SQL implemented by SQLite](https://www.sqlite.org/lang.html).
7*6dbdd20aSAndroid Build Coastguard WorkerSpecifically, any SQL valid in SQLite is also valid in PerfettoSQL.
8*6dbdd20aSAndroid Build Coastguard Worker
9*6dbdd20aSAndroid Build Coastguard WorkerUnfortunately, the SQLite syntax alone is not sufficient for two reasons:
10*6dbdd20aSAndroid Build Coastguard Worker1. It is quite basic e.g. it does not support creating functions or macros
11*6dbdd20aSAndroid Build Coastguard Worker2. It cannot be used to access features which are only available in Perfetto
12*6dbdd20aSAndroid Build Coastguard Workertooling e.g. it cannot be used to create efficient analytic tables, import
13*6dbdd20aSAndroid Build Coastguard Workermodules from the PerfettoSQL standard library etc.
14*6dbdd20aSAndroid Build Coastguard Worker
15*6dbdd20aSAndroid Build Coastguard WorkerFor this reason, PerfettoSQL adds new pieces of syntax which make the experience
16*6dbdd20aSAndroid Build Coastguard Workerof writing SQL queries better. All such additons include the keyword `PERFETTO`
17*6dbdd20aSAndroid Build Coastguard Workerto make it clear that they are PerfettoSQL-only.
18*6dbdd20aSAndroid Build Coastguard Worker
19*6dbdd20aSAndroid Build Coastguard Worker<!-- TODO(b/290185551): we should really talk about our "recommendations" (e.g.
20*6dbdd20aSAndroid Build Coastguard Workerusing CREATE PERFETTO TABLE instead of CREATE TABLE) somewhere and reference it
21*6dbdd20aSAndroid Build Coastguard Workerhere. -->
22*6dbdd20aSAndroid Build Coastguard Worker
23*6dbdd20aSAndroid Build Coastguard Worker## Including PerfettoSQL modules
24*6dbdd20aSAndroid Build Coastguard Worker`INCLUDE PERFETTO MODULE` is used to import all tables/views/functions/macros
25*6dbdd20aSAndroid Build Coastguard Workerdefined in a PerfettoSQL module (e.g. from the
26*6dbdd20aSAndroid Build Coastguard Worker[PerfettoSQL standard library](/docs/analysis/stdlib-docs.autogen)).
27*6dbdd20aSAndroid Build Coastguard Worker
28*6dbdd20aSAndroid Build Coastguard WorkerNote that this statement acts more similar to `#include` statements in C++
29*6dbdd20aSAndroid Build Coastguard Workerrather than `import` statements from Java/Python. Specifically, all objects
30*6dbdd20aSAndroid Build Coastguard Workerin the module become available in the global namespace without being qualified
31*6dbdd20aSAndroid Build Coastguard Workerby the module name.
32*6dbdd20aSAndroid Build Coastguard Worker
33*6dbdd20aSAndroid Build Coastguard WorkerExample:
34*6dbdd20aSAndroid Build Coastguard Worker```sql
35*6dbdd20aSAndroid Build Coastguard Worker-- Include all tables/views/functions from the android.startup.startups module
36*6dbdd20aSAndroid Build Coastguard Worker-- in the standard library.
37*6dbdd20aSAndroid Build Coastguard WorkerINCLUDE PERFETTO MODULE android.startup.startups;
38*6dbdd20aSAndroid Build Coastguard Worker
39*6dbdd20aSAndroid Build Coastguard Worker-- Use the android_startups table defined in the android.startup.startups
40*6dbdd20aSAndroid Build Coastguard Worker-- module.
41*6dbdd20aSAndroid Build Coastguard WorkerSELECT *
42*6dbdd20aSAndroid Build Coastguard WorkerFROM android_startups;
43*6dbdd20aSAndroid Build Coastguard Worker```
44*6dbdd20aSAndroid Build Coastguard Worker
45*6dbdd20aSAndroid Build Coastguard WorkerFor interactive development, the key can contain a wildcards:
46*6dbdd20aSAndroid Build Coastguard Worker```sql
47*6dbdd20aSAndroid Build Coastguard Worker-- Include all modules under android/.
48*6dbdd20aSAndroid Build Coastguard WorkerINCLUDE PERFETTO MODULE android.*;
49*6dbdd20aSAndroid Build Coastguard Worker
50*6dbdd20aSAndroid Build Coastguard Worker-- Or all stdlib modules:
51*6dbdd20aSAndroid Build Coastguard WorkerINCLUDE PERFETTO MODULE *;
52*6dbdd20aSAndroid Build Coastguard Worker
53*6dbdd20aSAndroid Build Coastguard Worker-- However, note, that both patterns are not allowed in stdlib.
54*6dbdd20aSAndroid Build Coastguard Worker```
55*6dbdd20aSAndroid Build Coastguard Worker
56*6dbdd20aSAndroid Build Coastguard Worker## Defining functions
57*6dbdd20aSAndroid Build Coastguard Worker`CREATE PEFETTO FUNCTION` allows functions to be defined in SQL. The syntax is
58*6dbdd20aSAndroid Build Coastguard Workersimilar to the syntax in PostgreSQL or GoogleSQL.
59*6dbdd20aSAndroid Build Coastguard Worker
60*6dbdd20aSAndroid Build Coastguard Worker<!-- TODO(b/290185551): talk about different possible argument/return types. -->
61*6dbdd20aSAndroid Build Coastguard Worker
62*6dbdd20aSAndroid Build Coastguard WorkerExample:
63*6dbdd20aSAndroid Build Coastguard Worker```sql
64*6dbdd20aSAndroid Build Coastguard Worker-- Create a scalar function with no arguments.
65*6dbdd20aSAndroid Build Coastguard WorkerCREATE PERFETTO FUNCTION constant_fn() RETURNS INT AS SELECT 1;
66*6dbdd20aSAndroid Build Coastguard Worker
67*6dbdd20aSAndroid Build Coastguard Worker-- Create a scalar function taking two arguments.
68*6dbdd20aSAndroid Build Coastguard WorkerCREATE PERFETTO FUNCTION add(x INT, y INT) RETURNS INT AS SELECT $x + $y;
69*6dbdd20aSAndroid Build Coastguard Worker
70*6dbdd20aSAndroid Build Coastguard Worker-- Create a table function with no arguments
71*6dbdd20aSAndroid Build Coastguard WorkerCREATE PERFETTO FUNCTION constant_tab_fn()
72*6dbdd20aSAndroid Build Coastguard WorkerRETURNS TABLE(ts LONG, dur LONG) AS
73*6dbdd20aSAndroid Build Coastguard WorkerSELECT column1 as ts, column2 as dur
74*6dbdd20aSAndroid Build Coastguard WorkerFROM (
75*6dbdd20aSAndroid Build Coastguard Worker  VALUES
76*6dbdd20aSAndroid Build Coastguard Worker  (100, 10),
77*6dbdd20aSAndroid Build Coastguard Worker  (200, 20)
78*6dbdd20aSAndroid Build Coastguard Worker);
79*6dbdd20aSAndroid Build Coastguard Worker
80*6dbdd20aSAndroid Build Coastguard Worker-- Create a table function with one argument
81*6dbdd20aSAndroid Build Coastguard WorkerCREATE PERFETTO FUNCTION sched_by_utid(utid INT)
82*6dbdd20aSAndroid Build Coastguard WorkerRETURNS TABLE(ts LONG, dur LONG, utid INT) AS
83*6dbdd20aSAndroid Build Coastguard WorkerSELECT ts, dur, utid
84*6dbdd20aSAndroid Build Coastguard WorkerFROM sched
85*6dbdd20aSAndroid Build Coastguard WorkerWHERE utid = $utid;
86*6dbdd20aSAndroid Build Coastguard Worker```
87*6dbdd20aSAndroid Build Coastguard Worker
88*6dbdd20aSAndroid Build Coastguard Worker## Creating efficient tables
89*6dbdd20aSAndroid Build Coastguard Worker`CREATE PERFETTO TABLE` allows defining tables optimized for analytic queries
90*6dbdd20aSAndroid Build Coastguard Workeron traces. These tables are both more performant and more memory efficient than
91*6dbdd20aSAndroid Build Coastguard WorkerSQLite native tables created with `CREATE TABLE`.
92*6dbdd20aSAndroid Build Coastguard Worker
93*6dbdd20aSAndroid Build Coastguard WorkerNote however the full feature set of `CREATE TABLE` is not supported:
94*6dbdd20aSAndroid Build Coastguard Worker1. Perfetto tables cannot be inserted into and are read-only after creation
95*6dbdd20aSAndroid Build Coastguard Worker2. Perfetto tables must be defined and populated using a `SELECT` statement.
96*6dbdd20aSAndroid Build Coastguard Worker  They cannot be defined by column names and types.
97*6dbdd20aSAndroid Build Coastguard Worker
98*6dbdd20aSAndroid Build Coastguard WorkerExample:
99*6dbdd20aSAndroid Build Coastguard Worker```sql
100*6dbdd20aSAndroid Build Coastguard Worker-- Create a Perfetto table with constant values.
101*6dbdd20aSAndroid Build Coastguard WorkerCREATE PERFETTO TABLE constant_table AS
102*6dbdd20aSAndroid Build Coastguard WorkerSELECT column1 as ts, column2 as dur
103*6dbdd20aSAndroid Build Coastguard WorkerFROM (
104*6dbdd20aSAndroid Build Coastguard Worker  VALUES
105*6dbdd20aSAndroid Build Coastguard Worker  (100, 10),
106*6dbdd20aSAndroid Build Coastguard Worker  (200, 20)
107*6dbdd20aSAndroid Build Coastguard Worker);
108*6dbdd20aSAndroid Build Coastguard Worker
109*6dbdd20aSAndroid Build Coastguard Worker-- Create a Perfetto table with a query on another table.
110*6dbdd20aSAndroid Build Coastguard WorkerCREATE PERFETTO TABLE slice_sub_table AS
111*6dbdd20aSAndroid Build Coastguard WorkerSELECT *
112*6dbdd20aSAndroid Build Coastguard WorkerFROM slice
113*6dbdd20aSAndroid Build Coastguard WorkerWHERE name = 'foo';
114*6dbdd20aSAndroid Build Coastguard Worker```
115*6dbdd20aSAndroid Build Coastguard Worker
116*6dbdd20aSAndroid Build Coastguard Worker### Schema
117*6dbdd20aSAndroid Build Coastguard Worker
118*6dbdd20aSAndroid Build Coastguard WorkerPerfetto tables can have an optional explicit schema. The schema syntax is the
119*6dbdd20aSAndroid Build Coastguard Workersame as the function argument or returned-from-a-function table,
120*6dbdd20aSAndroid Build Coastguard Workeri.e. a comma-separated list of (column name, colum type) pairs in parenthesis
121*6dbdd20aSAndroid Build Coastguard Workerafter table or view name.
122*6dbdd20aSAndroid Build Coastguard Worker
123*6dbdd20aSAndroid Build Coastguard Worker```sql
124*6dbdd20aSAndroid Build Coastguard WorkerCREATE PERFETTO TABLE foo(x INT, y STRING) AS
125*6dbdd20aSAndroid Build Coastguard WorkerSELECT 1 as x, 'test' as y
126*6dbdd20aSAndroid Build Coastguard Worker```
127*6dbdd20aSAndroid Build Coastguard Worker
128*6dbdd20aSAndroid Build Coastguard Worker### Index
129*6dbdd20aSAndroid Build Coastguard Worker
130*6dbdd20aSAndroid Build Coastguard Worker`CREATE PERFETTO INDEX` lets you create indexes on Perfetto tables, similar to
131*6dbdd20aSAndroid Build Coastguard Workerhow you create indexes in SQLite databases. These indexes are built on specific
132*6dbdd20aSAndroid Build Coastguard Workercolumns, and Perfetto internally maintains these columns in a sorted order.
133*6dbdd20aSAndroid Build Coastguard WorkerThis means operations benefiting from sorting on an indexed column (or group of
134*6dbdd20aSAndroid Build Coastguard Workercolumns) will be significantly faster, as if you were operating on a column
135*6dbdd20aSAndroid Build Coastguard Workerthat's already sorted.
136*6dbdd20aSAndroid Build Coastguard Worker
137*6dbdd20aSAndroid Build Coastguard WorkerNOTE: Indexes have non-trivial memory cost, so it's important to only use them
138*6dbdd20aSAndroid Build Coastguard Workerwhen there is a need for performance improvement.
139*6dbdd20aSAndroid Build Coastguard Worker
140*6dbdd20aSAndroid Build Coastguard WorkerNOTE: Indexes will be used by views created on the indexed table, but they will
141*6dbdd20aSAndroid Build Coastguard Workernot be inherited by any child tables, as shown in the below SQL.
142*6dbdd20aSAndroid Build Coastguard Worker
143*6dbdd20aSAndroid Build Coastguard WorkerNOTE: If the query filters/joins on `id` column of the table (one that is a
144*6dbdd20aSAndroid Build Coastguard Workerprimary key of the table) there is no need to add a Perfetto index, as Perfetto
145*6dbdd20aSAndroid Build Coastguard Workertables already have special performance optimizations for operations that can
146*6dbdd20aSAndroid Build Coastguard Workerbenefit from sorting.
147*6dbdd20aSAndroid Build Coastguard Worker
148*6dbdd20aSAndroid Build Coastguard WorkerExample of usage:
149*6dbdd20aSAndroid Build Coastguard Worker```sql
150*6dbdd20aSAndroid Build Coastguard WorkerCREATE PERFETTO TABLE foo AS
151*6dbdd20aSAndroid Build Coastguard WorkerSELECT * FROM slice;
152*6dbdd20aSAndroid Build Coastguard Worker
153*6dbdd20aSAndroid Build Coastguard Worker-- Creates and stores an index `foo_track` on column `track_id` of table foo.
154*6dbdd20aSAndroid Build Coastguard WorkerCREATE PERFETTO INDEX foo_track ON foo(track_id);
155*6dbdd20aSAndroid Build Coastguard Worker-- Creates or replaces an index created on two columns. It will be used for
156*6dbdd20aSAndroid Build Coastguard Worker-- operations on `track_id` and can be used on operations on `name` only if
157*6dbdd20aSAndroid Build Coastguard Worker-- there has been an equality constraint on `track_id` too.
158*6dbdd20aSAndroid Build Coastguard WorkerCREATE OR REPLACE PERFETTO INDEX foo_track_and_name ON foo(track_id, name);
159*6dbdd20aSAndroid Build Coastguard Worker```
160*6dbdd20aSAndroid Build Coastguard Worker
161*6dbdd20aSAndroid Build Coastguard WorkerThe performance of those two queries should be very different now:
162*6dbdd20aSAndroid Build Coastguard Worker```sql
163*6dbdd20aSAndroid Build Coastguard Worker-- This doesn't have an index so it will have to linearily scan whole column.
164*6dbdd20aSAndroid Build Coastguard WorkerSELECT * FROM slice WHERE track_id = 10 AND name > "b";
165*6dbdd20aSAndroid Build Coastguard Worker
166*6dbdd20aSAndroid Build Coastguard Worker-- This has an index and can use binary search.
167*6dbdd20aSAndroid Build Coastguard WorkerSELECT * FROM foo WHERE track_id = 10 AND name > "b";
168*6dbdd20aSAndroid Build Coastguard Worker
169*6dbdd20aSAndroid Build Coastguard Worker-- The biggest difference should be noticeable on joins:
170*6dbdd20aSAndroid Build Coastguard Worker-- This join:
171*6dbdd20aSAndroid Build Coastguard WorkerSELECT * FROM slice JOIN track WHERE slice.track_id = track.id;
172*6dbdd20aSAndroid Build Coastguard Worker-- will be noticeably slower than this:
173*6dbdd20aSAndroid Build Coastguard WorkerSELECT * FROM foo JOIN track WHERE slice.track_id = track.id;
174*6dbdd20aSAndroid Build Coastguard Worker```
175*6dbdd20aSAndroid Build Coastguard Worker
176*6dbdd20aSAndroid Build Coastguard WorkerIndexes can be dropped:
177*6dbdd20aSAndroid Build Coastguard Worker```sql
178*6dbdd20aSAndroid Build Coastguard WorkerDROP PERFETTO INDEX foo_track ON foo;
179*6dbdd20aSAndroid Build Coastguard Worker```
180*6dbdd20aSAndroid Build Coastguard Worker
181*6dbdd20aSAndroid Build Coastguard Worker
182*6dbdd20aSAndroid Build Coastguard Worker## Creating views with a schema
183*6dbdd20aSAndroid Build Coastguard Worker
184*6dbdd20aSAndroid Build Coastguard WorkerViews can be created via `CREATE PERFETTO VIEW`, taking an optional schema.
185*6dbdd20aSAndroid Build Coastguard WorkerWith the exception of the schema, they behave exactly the same as regular
186*6dbdd20aSAndroid Build Coastguard WorkerSQLite views.
187*6dbdd20aSAndroid Build Coastguard Worker
188*6dbdd20aSAndroid Build Coastguard WorkerNOTE: the use of `CREATE PERFETTO VIEW` instead of `CREATE VIEW` is required in
189*6dbdd20aSAndroid Build Coastguard Workerthe standard library where each column must be documented.
190*6dbdd20aSAndroid Build Coastguard Worker
191*6dbdd20aSAndroid Build Coastguard Worker```sql
192*6dbdd20aSAndroid Build Coastguard WorkerCREATE PERFETTO VIEW foo(x INT, y STRING) AS
193*6dbdd20aSAndroid Build Coastguard WorkerSELECT 1 as x, 'test' as y
194*6dbdd20aSAndroid Build Coastguard Worker```
195*6dbdd20aSAndroid Build Coastguard Worker
196*6dbdd20aSAndroid Build Coastguard Worker## Defining macros
197*6dbdd20aSAndroid Build Coastguard Worker`CREATE PEFETTO MACRO` allows macros to be defined in SQL. The design of macros
198*6dbdd20aSAndroid Build Coastguard Workeris inspired by the macros in Rust.
199*6dbdd20aSAndroid Build Coastguard Worker
200*6dbdd20aSAndroid Build Coastguard WorkerThe following are recommended uses of macros:
201*6dbdd20aSAndroid Build Coastguard Worker- Passing tables as arguments to a "function-like" snippet of SQL.
202*6dbdd20aSAndroid Build Coastguard Worker
203*6dbdd20aSAndroid Build Coastguard WorkerMacros are powerful but also dangerous if used incorrectly, making debugging
204*6dbdd20aSAndroid Build Coastguard Workerextremely difficult. For this reason, it's recommended that they are used
205*6dbdd20aSAndroid Build Coastguard Workersparingly when they are needed and only for the recommended uses described
206*6dbdd20aSAndroid Build Coastguard Workerabove. If only passing around scalar SQL values, use functions as discussed
207*6dbdd20aSAndroid Build Coastguard Workerabove.
208*6dbdd20aSAndroid Build Coastguard Worker
209*6dbdd20aSAndroid Build Coastguard WorkerNOTE: Macros are expanded with a pre-processing step *before* any execution
210*6dbdd20aSAndroid Build Coastguard Workerhappens. Expansion is a purely syntatic operation involves replacing the macro
211*6dbdd20aSAndroid Build Coastguard Workerinvocation with the SQL tokens in the macro definition.
212*6dbdd20aSAndroid Build Coastguard Worker
213*6dbdd20aSAndroid Build Coastguard WorkerAs macros are syntactic, the types of arguments and return types in macros are
214*6dbdd20aSAndroid Build Coastguard Workerdifferent to the types used in functions and correspond to parts of the SQL
215*6dbdd20aSAndroid Build Coastguard Workerparse tree. The following are the supported types:
216*6dbdd20aSAndroid Build Coastguard Worker
217*6dbdd20aSAndroid Build Coastguard Worker| Type name         | Description                                       |
218*6dbdd20aSAndroid Build Coastguard Worker| ---------         | -----------                                       |
219*6dbdd20aSAndroid Build Coastguard Worker| `Expr`            | Corresponds to any SQL scalar expression.         |
220*6dbdd20aSAndroid Build Coastguard Worker| `TableOrSubquery` | Corresponds to either an SQL table or a subquery  |
221*6dbdd20aSAndroid Build Coastguard Worker| `ColumnName`      | Corresponds to a column name of a table           |
222*6dbdd20aSAndroid Build Coastguard Worker
223*6dbdd20aSAndroid Build Coastguard WorkerExample:
224*6dbdd20aSAndroid Build Coastguard Worker```sql
225*6dbdd20aSAndroid Build Coastguard Worker-- Create a macro taking no arguments. Note how the returned SQL fragment needs
226*6dbdd20aSAndroid Build Coastguard Worker-- to be wrapped in brackets to make it a valid SQL expression.
227*6dbdd20aSAndroid Build Coastguard Worker--
228*6dbdd20aSAndroid Build Coastguard Worker-- Note: this is a strongly discouraged use of macros as a simple SQL
229*6dbdd20aSAndroid Build Coastguard Worker-- function would also work here.
230*6dbdd20aSAndroid Build Coastguard WorkerCREATE PERFETTO MACRO constant_macro() RETURNS Expr AS (SELECT 1);
231*6dbdd20aSAndroid Build Coastguard Worker
232*6dbdd20aSAndroid Build Coastguard Worker-- Using the above macro. Macros are invoked by suffixing their names with !.
233*6dbdd20aSAndroid Build Coastguard Worker-- This is similar to how macros are invoked in Rust.
234*6dbdd20aSAndroid Build Coastguard WorkerSELECT constant_macro!();
235*6dbdd20aSAndroid Build Coastguard Worker
236*6dbdd20aSAndroid Build Coastguard Worker-- This causes the following SQL to be actually executed:
237*6dbdd20aSAndroid Build Coastguard Worker-- SELECT (SELECT 1);
238*6dbdd20aSAndroid Build Coastguard Worker
239*6dbdd20aSAndroid Build Coastguard Worker-- A variant of the above. Again, strongly discouraged.
240*6dbdd20aSAndroid Build Coastguard WorkerCREATE PERFETTO MACRO constant_macro_no_bracket() RETURNS Expr AS 2;
241*6dbdd20aSAndroid Build Coastguard Worker
242*6dbdd20aSAndroid Build Coastguard Worker-- Using the above macro.
243*6dbdd20aSAndroid Build Coastguard WorkerSELECT constant_macro_no_bracket!();
244*6dbdd20aSAndroid Build Coastguard Worker
245*6dbdd20aSAndroid Build Coastguard Worker-- This causes the following SQL to be actually executed:
246*6dbdd20aSAndroid Build Coastguard Worker-- SELECT 2;
247*6dbdd20aSAndroid Build Coastguard Worker
248*6dbdd20aSAndroid Build Coastguard Worker-- Creating a macro taking a single scalar argument and returning a scalar.
249*6dbdd20aSAndroid Build Coastguard Worker-- Note: again this is a strongly discouraged use of macros as functions can
250*6dbdd20aSAndroid Build Coastguard Worker-- also do this.
251*6dbdd20aSAndroid Build Coastguard WorkerCREATE PERFETTO MACRO single_arg_macro(x Expr) RETURNS Expr AS (SELECT $x);
252*6dbdd20aSAndroid Build Coastguard WorkerSELECT constant_macro!() + single_arg_macro!(100);
253*6dbdd20aSAndroid Build Coastguard Worker
254*6dbdd20aSAndroid Build Coastguard Worker-- Creating a macro taking both a table and a scalar expression as an argument
255*6dbdd20aSAndroid Build Coastguard Worker-- and returning a table. Note again how the returned SQL statement is wrapped
256*6dbdd20aSAndroid Build Coastguard Worker-- in brackets to make it a subquery. This allows it to be used anywhere a
257*6dbdd20aSAndroid Build Coastguard Worker-- table or subquery is allowed.
258*6dbdd20aSAndroid Build Coastguard Worker--
259*6dbdd20aSAndroid Build Coastguard Worker-- Note: if tables are reused multiple times, it's recommended that they be
260*6dbdd20aSAndroid Build Coastguard Worker-- "cached" with a common-table expression (CTE) for performance reasons.
261*6dbdd20aSAndroid Build Coastguard WorkerCREATE PERFETTO MACRO multi_arg_macro(x TableOrSubquery, y Expr)
262*6dbdd20aSAndroid Build Coastguard WorkerRETURNS TableOrSubquery AS
263*6dbdd20aSAndroid Build Coastguard Worker(
264*6dbdd20aSAndroid Build Coastguard Worker  SELECT input_tab.input_col + $y
265*6dbdd20aSAndroid Build Coastguard Worker  FROM $x AS input_tab;
266*6dbdd20aSAndroid Build Coastguard Worker)
267*6dbdd20aSAndroid Build Coastguard Worker```