Repository navigation
RFC-0041: PerfettoSQL Next: Analysis Operators Reference #6993
Replies: 3 comments
|
📝 RFC Document Updated View changes: Commit History |
0 replies
|
📝 RFC Document Updated View changes: Commit History |
0 replies
|
📝 RFC Document Updated View changes: Commit History |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
📄 RFC Doc: 0041-perfettosql-next-operator-reference.md
PerfettoSQL Next: Analysis Operators Reference
Authors: @LalitMaganti
Status: Draft
PR: N/A
Scope
This RFC is the operator reference for PerfettoSQL Next, motivated in RFC-0040.
The analysis operators: a closed set of pipe operators for relating data in
time (intervals), structure (trees), and dependency (graphs). They
extend PerfettoSQL's relational pipe operators (§4). The tree operators (§6) are
the RFC-0020 tree functions re-expressed as pipe stages.
Notation
UPPERCASE[ ]{ }|, …A pipe operator
|> OPERATOR …transforms the rows from the precedingstage. Reading order is execution order.
Contents
1. Data model
Interval and event rows. An interval row has two distinguished
INT64columns,
tsanddur, denoting[ts, ts + dur); other columns are payload.An event row has
tsbut nodur. Any relation exposingts/durparticipates in interval operators. (
dur = -1denotes an interval truncated bytrace end.)
Trees and graphs: nodes + edges. A tree and a graph are both a node
relation (rows with an
idand payload) plus an edge relation. For a treethe edges are each node's
parent_id; for a graph they are(src, dst)pairs.A tree is the acyclic single-parent case. Self-weight lives at every node;
cumulative weight is the subtree sum. Internal ids are dense and never
user-synthesized.
A
slicerow is at once an interval (ts/dur) and a tree node(
id/parent_id), so interval and tree operators compose on the same rows.Most tree operators require the complete tree in the pipe; the filters,
reshapes, and reparent may act on a filtered working set (§6.1).
Graphs reduce to trees. A graph's only role is to be reduced to a tree (§7),
carrying node payload onto the result, after which the tree operators take over.
Payload when a row is split. A sub-row inherits the original payload. A
property (a state) is carried unchanged; a measure (bytes, energy, a
count) must be rescaled by an expression in a following
EXTEND. The languagenever infers which.
2. Pipeline structure
A named, materialized result uses the existing
CREATE PERFETTO TABLE ... ASstatement with a pipeline expression as its body. Its optional schema validates
the output columns and types; it does not project or reorder them. A final stage
must therefore produce the declared columns, although their order is not
significant.
Every input carries an alias; columns are
alias.col. An alias is alwayswritten with
AS(FROM t AS x, neverFROM t x), so a keyword following arelation, such as
PER, is never taken for one.Kinds and consumption. Every pipeline expression has a kind, one of
Relation,Tree,Graph, orIntervalSet, determined by its source andstages. Typing is strict inside an expression: passing a
Graphwhere aninterval operator expects an
IntervalSetis an error. A pipeline expressioncan appear anywhere a
SELECTcan. At that boundary, a non-Relationresultflattens to its underlying rows. There is no named pipeline object or standalone
pipe statement.
A reusable pipeline with its own source is an ordinary CTE:
WITH name AS (pipeline_expr). It follows normal CTE scope and naming rules andmay reference earlier CTEs and base tables.
FORK AS nameinstead names thecurrent stream at that point so later stages can reference both that snapshot
and its subsequent transformations; the stream passes through unchanged.
For example, this preserves the unmerged intervals as
raw_states, merges thecurrent stream, and then attaches every original interval contained by each
merged interval:
A CTE cannot replace this inline snapshot without repeating the pipeline prefix.
A pipeline-valued macro (
RETURNS Pipeline) is a parameterized pipelinefragment, the one way to parameterize a pipeline by an input relation. Its
parameters may be relations (
TableOrSubQuery), columns (ColumnName) orscalar expressions (
Expr), referenced in the body as$name. Its body iseither a full
pipeline_expr(with a source), invoked as a source,name!(args), or a tail of|>stages, invoked mid-pipe,… |> name!(args).It expands in place. This is how operations that take a relation argument (clip
to a caller-supplied window, an N-ary aggregation over a passed-in window) are
written without leaving the pipe surface.
3. Shared grammar
PER colsnames the lane: rows interact only with rows of the samelane, and each lane is processed independently. For the operators that collapse
rows (
FLATTEN,MERGE), the lane is also part of the collapse key: rowscollapse only within a lane. (
BY, below, is a different thing: a key the rowsmust additionally agree on.)
PER ROLLUP(a, b, …)runs the operator once per prefix of the key list,(a, b, …), …,(a),(), and unions the results. It is the intervalcounterpart of
GROUP BY ROLLUP(§4) and is allowed only on the collapsingoperators (
FLATTEN,MERGE); elsewhere it is an error. Rolled-up key columnsare null;
GROUPING(col)is 1 wherecolis rolled up, telling a rolled-upnull from a genuine null key. Because the aggregate vocabulary is decomposable,
each level can be computed from the level below rather than from the input.
Anchor (closed), an endpoint or extent of the current row or a named
relation:
Matching key (closed), shared key columns, assumed equal on both sides,
never a predicate:
Aggregate vocabulary (closed), decomposable-only; carried by every operator
that collapses rows (
FLATTEN,MERGE,MERGE SIBLINGS,MERGE INTO PARENT)and by
ACCUMULATE:4. Relational operators
PerfettoSQL's relational pipe operators; syntax inspired by the GoogleSQL pipe
and query syntax, engine is SQLite. Each is a stage
|> OPERATOR …, exceptFROM.FROM relWHERE exprexpris trueSELECT colsEXTEND expr AS nameSET col = exprDROP col, …RENAME col AS nameDISTINCTAGGREGATE agg AS name [ GROUP BY cols ]ORDER BY cols [ ASC | DESC ]LIMIT n [ OFFSET m ]JOIN rel ON expr/USING (cols)UNION/INTERSECT/EXCEPT[ ALL ] (…)WINDOW expr AS nameOVER)AGGREGATEandOVERare SQLite aggregation, distinct from the closedagg_clause(§3) of the collapsing analysis operators.AGGREGATE … GROUP BY colsoutputs theGROUP BYcolumns first (in order),then the named aggregates (in order), the GoogleSQL-pipe contract. So a
following
|> SELECT <those same columns, same order>is a no-op; aSELECTisonly needed to rename, reorder, compute, or drop columns.
GROUP BY ROLLUP(cols)groups by each prefix ofcolsand unions the results,with
GROUPING(col)marking rolled-up columns, as in GoogleSQL.GROUPING SETSand
CUBEare not supported: every known use is a hierarchy, whichROLLUPcovers and which keeps the result a tree.
A
GROUP BYentry isexpr [ AS name ]. A key appears only inGROUP BY,never restated in the body; anything functionally determined by a key is itself
a key (
GROUP BY c.cpu, c.idle + 1 AS state).Every body expression must be an aggregate, no bare columns. For a value
constant within the group use
ANY_VALUE(col); for a value from the row at anextremum use
ARG_MAX(ordering, value)/ARG_MIN(ordering, value)(notANY_VALUE, which picks an arbitrary row). These are available wherever SQLiteaggregation is.
Raw SQL stage
When no operator expresses what is needed, a raw-SQL stage transforms the
current stream like any other stage, reading its input through the
@inputhandle. This is the boundary with no cliff edge: dropping to SQL is an ordinary
stage, not a mode switch.
@inputis the only sigil in the language and is available only inside a rawSQL stage. The exact keyword for the stage is not yet fixed.
5. Interval operators
5.1 Sources
INTERVAL INTERSECTION OF/UNION OFINTERSECTION OFemits a fragment over each region where all inputsoverlap;
UNION OFover each region where any input covers. They are theinner and full-outer forms of one operation: both co-fragment the operands side
by side, cutting at every operand's boundaries, and each output fragment carries
every operand's columns.
UNION OF, an operand absent from a fragment hasits columns null, including its own
alias.ts. A row without atscoversno time, so
alias.ts IS NOT NULLis exactly where the operand is present,telling a genuine null apart from absence.
fragment is emitted per combination of rows, as in a join. A region covered
by
a1anda2and no row ofbis(a1, null)and(a2, null).PER,INTERSECTION OFemits nothing for a lane missingfrom any operand;
UNION OFemits fragments for a lane present in any.UNION OFis not relationalUNION, which stacks rows, nor the union ofcoverage, which is
UNION ALLfollowed byINTERVAL MERGE OVERLAPPING.INTERVALS FROM EVENTSEach event opens an interval closed by the next event in its lane (or a
STOPevent). The final open interval is retained with
dur = -1(
KEEPING LAST OPEN) or closed atCLOSING LAST AT (expr); theexprmayreference the lane's columns (e.g. a per-process end).
Events are not coalesced, a run of equal-valued samples yields one interval
each; collapse them with a following
MERGE CONSECUTIVE BY value(§5.2), whichcarries the keyed columns through. Each interval carries its opening event's
payload; the closing event's columns (a transition reason, the next value, a
delta) are the next event's, reached with §4 windowing over lane order.
5.2 Reshaping
INTERVAL SPLITCuts each current row at the operand intervals' boundaries, one sub-interval per
piece, attaching the operand's columns (null where no operand is present).
Lossless, the sub-intervals tile the original.
INTERVAL SUBTRACTRemoves the operand's coverage; the pieces outside the operand survive.
INTERVAL FILL GAPSLays the current stream over a background
rel: every input row passes throughunchanged, and each span of
relthe stream does not cover becomes a fillerrow carrying that
relrow's payload.relis both the extent to cover and thevalue to cover it with; there is no built-in extent, so the caller names it
(
trace_bounds, a UI window, a per-lane span).column absent from
relis null on fillers; arelcolumn absent from thestream is added, null on input rows (so
TRUE AS filledinrelmarks thefillers). A type mismatch is an error.
relhas thePERcolumns, the lanes arerel's, so a lanewith no input rows is still filled. Otherwise every
relrow applies to everylane of the stream.
relmay hold several intervals per lane with differentpayloads; a gap takes whichever background is live there.
relmust notself-overlap within a lane.
dur = -1row covers to the end ofrel. No zero-width fillers are emitted.Input outside
relpasses through: filling never clips.For the gaps alone, subtract the stream from the background with
INTERVAL SUBTRACT.INTERVAL FLATTENCuts a self-overlapping set at every internal boundary into disjoint segments
and collapses the rows live in each segment into one row via
agg_clause. Emitsnothing over uncovered time (follow with
FILL GAPSfor an explicit zero).Output is
ts,dur, thePERcolumns, then the aggregates; every othercolumn is dropped. This is the §4
AGGREGATE … GROUP BYcontract with thesegment as an implicit extra key: FLATTEN is a self-
SPLITwithin each lanefollowed by that aggregation, fused so the fragments are never materialized.
INTERVAL MERGEOVERLAPPINGcoalesces overlapping and abutting intervals into coverage;CONSECUTIVE BY colsmerges only adjacent intervals agreeing oncols. Themerged payload is combined by
agg_clause.INTERVAL SET BOUNDSReplaces
ts/durwith new bounds from payload expressions.INTERVAL QUANTIZECuts rows into fixed-width buckets (splitting at boundaries) and tags each piece
with a
bucketcolumn.ALIGNED TOsets the origin (defaultTRACE START).5.3 Matching
The relationship phrase:
COVERING, the operand covers my point/extent;WITHIN, the operand liesinside my extent;
OVERLAPPING, the operand overlaps my extent. A dur-lessevent row is a zero-width point, so
COVERING BEGINmatches the operandcontaining it.
INTERVAL JOINAttaches matched operand columns without changing the current rows' bounds;
multiplies per match.
LEFTkeeps unmatched rows.INTERVAL FINDAttaches the single operand row nearest in the given direction. At most one
matches;
LEFTyields nulls when none does.WITHIN durbounds the search, thenearest within the tolerance, otherwise no match.
INTERVAL KEEP IF/DROP IFThe temporal semijoin / antijoin. Matching is against the operand's merged
coverage, so a row related to several operand intervals survives once.
6. Tree operators
The tree operators are RFC-0020's tree functions re-expressed as pipe stages.
TREE KEEP IFistree_filter;TREE ACCUMULATE UP/DOWNistree_propagate_up/down;TREE MERGE SIBLINGSistree_merge_siblings[_ordered];TREE MERGE INTO PARENTistree_merge_into_parent;TREE INVERTistree_invert.CONTRACT,ABSORB,PRUNE,REPARENT,FLATTEN RECURSION,KEY SIBLINGS, andEXPANDare new.6.1 Structure and weight
The filters (§6.2), reshapes (§6.3), and reparent (§6.4) may act on
a filtered working set, naming the full structure with
OVER rel.KEY SIBLINGSand the folds (§6.5-6.6),ACCUMULATE(§6.8), andEXPAND(§6.9) require the complete tree in the pipe.Measures and properties. Columns named in a fold's
agg_clausearemeasures: they relocate to the operator's destination (parent, self, or
merge target) and combine by the stated agg. Every unnamed column is a
property: it stays unchanged with its node. Inserts (
EXPAND) carry noagg_clause, new nodes are zero unless valued by their source relation, andCHARGEmoves one measure to the far end. A scalar can be folded together butnever split apart, so any operation that fans a measure out happens at
construction, before aggregation.
6.2 Filters
The structural semijoin / antijoin.
relis a set of rows (often a filteredset), not a predicate.
KEEP IF DESCENDANT OF selkeeps subtrees undersel(focus);
KEEP IF ANCESTOR OF selkeeps paths tosel;DROP IF DESCENDANT OF selprunes them.6.3 Reshapes
AT relselects the nodes (byid).CONTRACTremoves each, reparents itschildren up, and charges its measures to the parent.
ABSORBkeeps each, dropsits subtree, and folds the descendant measures into self.
PRUNEremoves eachnode and its whole subtree; its measures are discarded, so it takes no
agg_clause.6.4 Reparent
Row-preserving: every node survives with its payload and only
parent_idchanges.
REPARENT TO NEAREST ANCESTOR IN rel: each node's parent becomes its neareststrict ancestor that is in
rel, the marker set, yielding a forest with onesubtree per marker. Passing the boundary markers of a key column recovers a
partition-by-group.
FLATTEN RECURSION BY cols: a node that agrees with its parent oncolsbecomes its parent's sibling, keeping its own subtree. The rule applies
repeatedly, so a chain
A → A → Abecomes three siblings, and a same-key childof a root becomes a root. The recursive nodes end up as siblings with the same
key, so a following
KEY SIBLINGSorMERGE SIBLINGSon the samecols(§6.5)puts them in one group.
6.5 Key and merge siblings
Both stages key every node by its root-to-node path of
cols: two nodes get thesame key when their parents have the same key and they agree on
cols. Rootsare keyed by
colsalone.KEY SIBLINGS BY cols AS (key, parent_key)is row-preserving. It adds twoproperties to every node: its own key as
keyand its parent's key asparent_key, null for a root. Keys are dense ids scoped to the stage's output.The key is an ordinary column: later stages can filter on it, join on it, or
aggregate by it.
MERGE SIBLINGS BY colsunifies nodes with the same key, combining payloads viaagg_clause, taking a tree of callstacks to a tree keyed by name path (aflamegraph).
ORDEREDunifies only consecutive same-key runs.6.6 Merge into parent
Collapses a node into a same-key parent, combining payloads via
agg_clause.INDIRECTalso folds intermediary frames between two occurrences.6.7 Invert
Flips leaves and roots.
6.8 Accumulate
Row-preserving; one value per node.
UPaggregates each node's subtree(cumulative) using the decomposable aggregates of §3.
DOWNaggregates eachnode's root-path, the ordered chain root→node, and so additionally admits
the ordered-path reductions
FIRST/LAST(e.g. inherit the nearest non-nullancestor's value);
UPcannot, because a subtree has no order.6.9 Tree expansion
Inserts a keyed sub-structure
relabove (UP) or below (DOWN) each matchednode;
relcarries its own internalparent_id, so its shape is free, a chain,a flat layer (the former
ATTACH), or any forest. The matched node survives atits end (leaf-ward for
UP, root-ward forDOWN) and its existing subtreereconnects at the far end. Inserted nodes are zero unless
relvalues them.CHARGE cols TO ( LEAF | ROOT )migrates the named measures from the matchednode to the far end the insertion created:
DOWNadmits onlyTO LEAF,UPonly
TO ROOT, the other pairing is an error. It is well-defined only when thefar end is a single node (a chain); a fan-out has no single target, so those
measures must be valued in
relinstead.7. Graph operators
A graph is reduced to a tree, which the tree operators then analyze.
GRAPH DFS TREEGRAPH BFS TREEGRAPH DOMINATOR TREENODES relcarries node payload onto the tree;EDGES relis the(src, dst)relation;
FROM relis the seeds.DOMINATORaccepts a multi-rowFROM(aforest of roots, e.g. GC roots): a virtual super-root is inserted over them and
stripped from the result, so no id is user-synthesized.
DFSandBFSgive thesame node-set, different trees; that shared node-set is the set reachable
from the seeds. All three dedup; ties are broken by node
idso the tree isdeterministic.
A single-hop neighbour lookup over an edge relation is a plain §4
JOINonEDGES, not a graph operator; only transitive reduction lives here.8. Operator summary
INTERVAL INTERSECTION OF/UNION OFINTERVALS FROM EVENTSINTERVAL FILL GAPSINTERVAL SPLITINTERVAL SUBTRACTINTERVAL FLATTENINTERVAL MERGEINTERVAL SET BOUNDSts/durfrom payloadINTERVAL QUANTIZEINTERVAL JOININTERVAL FINDINTERVAL KEEP IF/DROP IFTREE KEEP IF/DROP IFTREE CONTRACT/ABSORBTREE PRUNETREE REPARENTTREE FLATTEN RECURSIONTREE KEY SIBLINGSTREE MERGE SIBLINGSTREE MERGE INTO PARENTTREE INVERTTREE ACCUMULATE UP/DOWNTREE EXPAND UP/DOWNGRAPH DFS / BFS / DOMINATOR TREE9. Future operator candidates
specialized stdlib primitive, not a
GRAPH … TREEoperator.FIND, attach the nearest descendant/ancestor matching a setonto a node (the tree twin of
INTERVAL FIND).OVERLAY, priority/z-order compositing (a higher lane replaces alower one on overlap, both survive outside);
MERGEcannot hole-punch. Thetwo-layer case, a stream over a background, is
FILL GAPS(§5.2).INTERSECTION OFis a sourceonly); cross-lane bounds and greedy start↔end stream pairing.
10. Out of scope
These belong to §4 relational operators or the host language, not the analysis
families:
battery,runnable): §4 conditional aggregation; SQLite has noPIVOT.LAG/LEAD … OVER (PARTITION BY lane ORDER BY ts).JOINon the edge relation (§7).pipelines, and native
__intrinsic_*escape hatches.💬 Discussion Guidelines:
All reactions