Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1202,11 +1202,13 @@ base::StatusOr<std::string> GeneratorImpl::CreateSlices(

// Use _interval_create! macro which delegates to
// __intrinsic_interval_create, an O(n+m) two-pointer C++ implementation.
// The macro expects inputs with a `ts` column, so we rename if needed.
// The macro expects inputs with `id` and `ts` columns. We pass a dummy id
// since structured queries don't need start_id/end_id, and exclude those
// columns from the output.
return base::StackString<1024>(
"(SELECT * FROM _interval_create!("
"(SELECT %s AS ts FROM %s), "
"(SELECT %s AS ts FROM %s)))",
"(SELECT ts, dur FROM _interval_create!("
"(SELECT 1 AS id, %s AS ts FROM %s), "
"(SELECT 1 AS id, %s AS ts FROM %s)))",
starts_ts_col.c_str(), starts_table.c_str(), ends_ts_col.c_str(),
ends_table.c_str())
.ToStdString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4150,7 +4150,7 @@ TEST(StructuredQueryGeneratorTest, ExperimentalCreateSlicesBasic) {
sq_2 AS (SELECT * FROM end_events),
sq_1 AS (SELECT * FROM start_events),
sq_0 AS (
SELECT * FROM (SELECT * FROM _interval_create!((SELECT ts AS ts FROM sq_1), (SELECT ts AS ts FROM sq_2)))
SELECT * FROM (SELECT ts, dur FROM _interval_create!((SELECT 1 AS id, ts AS ts FROM sq_1), (SELECT 1 AS id, ts AS ts FROM sq_2)))
)
SELECT * FROM sq_0
)"));
Expand Down Expand Up @@ -4200,7 +4200,7 @@ TEST(StructuredQueryGeneratorTest,
sq_2 AS (SELECT * FROM slice WHERE name GLOB '*_end'),
sq_1 AS (SELECT * FROM slice WHERE name GLOB '*_begin'),
sq_0 AS (
SELECT * FROM (SELECT * FROM _interval_create!((SELECT ts AS ts FROM sq_1), (SELECT ts AS ts FROM sq_2)))
SELECT * FROM (SELECT ts, dur FROM _interval_create!((SELECT 1 AS id, ts AS ts FROM sq_1), (SELECT 1 AS id, ts AS ts FROM sq_2)))
)
SELECT * FROM sq_0
)"));
Expand Down Expand Up @@ -4229,9 +4229,10 @@ TEST(StructuredQueryGeneratorTest,
)");
auto ret = gen.Generate(proto.data(), proto.size());
ASSERT_OK_AND_ASSIGN(std::string res, ret);
EXPECT_THAT(
res, testing::HasSubstr("_interval_create!((SELECT acquire_ts AS ts FROM "
"sq_1), (SELECT release_ts AS ts FROM sq_2))"));
EXPECT_THAT(res,
testing::HasSubstr(
"_interval_create!((SELECT 1 AS id, acquire_ts AS ts FROM "
"sq_1), (SELECT 1 AS id, release_ts AS ts FROM sq_2))"));
}

TEST(StructuredQueryGeneratorTest, ExperimentalCreateSlicesWithFilters) {
Expand Down Expand Up @@ -4496,8 +4497,9 @@ TEST(StructuredQueryGeneratorTest,
auto ret = gen.Generate(proto.data(), proto.size());
ASSERT_OK_AND_ASSIGN(std::string res, ret);
// Should default starts_ts_column to "ts"
EXPECT_THAT(res, testing::HasSubstr("_interval_create!((SELECT ts AS ts FROM "
"sq_1), (SELECT ts AS ts FROM sq_2))"));
EXPECT_THAT(res, testing::HasSubstr(
"_interval_create!((SELECT 1 AS id, ts AS ts FROM "
"sq_1), (SELECT 1 AS id, ts AS ts FROM sq_2))"));
}

TEST(StructuredQueryGeneratorTest,
Expand All @@ -4523,8 +4525,9 @@ TEST(StructuredQueryGeneratorTest,
auto ret = gen.Generate(proto.data(), proto.size());
ASSERT_OK_AND_ASSIGN(std::string res, ret);
// Should default ends_ts_column to "ts"
EXPECT_THAT(res, testing::HasSubstr("_interval_create!((SELECT ts AS ts FROM "
"sq_1), (SELECT ts AS ts FROM sq_2))"));
EXPECT_THAT(res, testing::HasSubstr(
"_interval_create!((SELECT 1 AS id, ts AS ts FROM "
"sq_1), (SELECT 1 AS id, ts AS ts FROM sq_2))"));
}

TEST(StructuredQueryGeneratorTest,
Expand Down Expand Up @@ -4554,7 +4557,7 @@ TEST(StructuredQueryGeneratorTest,
sq_2 AS (SELECT * FROM end_events),
sq_1 AS (SELECT * FROM start_events),
sq_0 AS (
SELECT * FROM (SELECT * FROM _interval_create!((SELECT ts AS ts FROM sq_1), (SELECT ts AS ts FROM sq_2)))
SELECT * FROM (SELECT ts, dur FROM _interval_create!((SELECT 1 AS id, ts AS ts FROM sq_1), (SELECT 1 AS id, ts AS ts FROM sq_2)))
)
SELECT * FROM sq_0
)"));
Expand Down Expand Up @@ -4642,8 +4645,9 @@ TEST(StructuredQueryGeneratorTest,
auto ret = gen.Generate(proto.data(), proto.size());
ASSERT_OK_AND_ASSIGN(std::string res, ret);
// Empty string should default to "ts"
EXPECT_THAT(res, testing::HasSubstr("_interval_create!((SELECT ts AS ts FROM "
"sq_1), (SELECT ts AS ts FROM sq_2))"));
EXPECT_THAT(res, testing::HasSubstr(
"_interval_create!((SELECT 1 AS id, ts AS ts FROM "
"sq_1), (SELECT 1 AS id, ts AS ts FROM sq_2))"));
}

TEST(StructuredQueryGeneratorTest, ExperimentalCreateSlicesEmptyEndsTsColumn) {
Expand All @@ -4669,8 +4673,9 @@ TEST(StructuredQueryGeneratorTest, ExperimentalCreateSlicesEmptyEndsTsColumn) {
auto ret = gen.Generate(proto.data(), proto.size());
ASSERT_OK_AND_ASSIGN(std::string res, ret);
// Empty string should default to "ts"
EXPECT_THAT(res, testing::HasSubstr("_interval_create!((SELECT ts AS ts FROM "
"sq_1), (SELECT ts AS ts FROM sq_2))"));
EXPECT_THAT(res, testing::HasSubstr(
"_interval_create!((SELECT 1 AS id, ts AS ts FROM "
"sq_1), (SELECT 1 AS id, ts AS ts FROM sq_2))"));
}

TEST(StructuredQueryGeneratorTest,
Expand Down Expand Up @@ -4714,7 +4719,7 @@ TEST(StructuredQueryGeneratorTest,
sq_2 AS (SELECT * FROM end_events WHERE ts < 0),
sq_1 AS (SELECT * FROM start_events WHERE ts < 0),
sq_0 AS (
SELECT * FROM (SELECT * FROM _interval_create!((SELECT ts AS ts FROM sq_1), (SELECT ts AS ts FROM sq_2)))
SELECT * FROM (SELECT ts, dur FROM _interval_create!((SELECT 1 AS id, ts AS ts FROM sq_1), (SELECT 1 AS id, ts AS ts FROM sq_2)))
)
SELECT * FROM sq_0
)"));
Expand Down Expand Up @@ -4759,7 +4764,7 @@ TEST(StructuredQueryGeneratorTest, ExperimentalCreateSlicesNoMatchingEnds) {
sq_2 AS (SELECT * FROM events WHERE ts > 10000),
sq_1 AS (SELECT * FROM events WHERE ts < 1000),
sq_0 AS (
SELECT * FROM (SELECT * FROM _interval_create!((SELECT ts AS ts FROM sq_1), (SELECT ts AS ts FROM sq_2)))
SELECT * FROM (SELECT ts, dur FROM _interval_create!((SELECT 1 AS id, ts AS ts FROM sq_1), (SELECT 1 AS id, ts AS ts FROM sq_2)))
)
SELECT * FROM sq_0
)"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ struct IntervalCreate : public sqlite::Function<IntervalCreate> {
auto* ends = sqlite::value::Pointer<SortedTimestamps>(
argv[1], SortedTimestamps::kName);

std::vector<std::string> col_names{"ts", "dur"};
std::vector<ColType> col_types{ColType::kInt64, ColType::kInt64};
std::vector<std::string> col_names{"ts", "dur", "start_id", "end_id"};
std::vector<ColType> col_types{ColType::kInt64, ColType::kInt64,
ColType::kInt64, ColType::kInt64};

dataframe::AdhocDataframeBuilder builder(
col_names, GetUserData(ctx)->pool,
Expand All @@ -80,7 +81,9 @@ struct IntervalCreate : public sqlite::Function<IntervalCreate> {
}

const auto& start_ts = starts->timestamps;
const auto& start_ids = starts->ids;
const auto& end_ts = ends->timestamps;
const auto& end_ids = ends->ids;

// Two-pointer matching: O(n + m).
// Both arrays are already sorted (guaranteed by ORDER BY in the SQL macro).
Expand All @@ -96,6 +99,8 @@ struct IntervalCreate : public sqlite::Function<IntervalCreate> {
}
builder.PushNonNullUnchecked(0, start_ts[i]);
builder.PushNonNullUnchecked(1, end_ts[end_idx] - start_ts[i]);
builder.PushNonNullUnchecked(2, start_ids[i]);
builder.PushNonNullUnchecked(3, end_ids[end_idx]);
}

SQLITE_ASSIGN_OR_RETURN(ctx, auto ret_table, std::move(builder).Build());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -436,21 +436,22 @@ struct IntervalTreeIntervalsAgg
}
};

// An SQL aggregate function which collects timestamps into a vector.
// An SQL aggregate function which collects (id, ts) pairs into vectors.
// Used as input to __intrinsic_interval_create. The caller is responsible
// for ensuring timestamps are passed in sorted order (e.g. via ORDER BY).
// for ensuring rows are passed in sorted order by ts (e.g. via ORDER BY).
struct TimestampSetAgg
: public sqlite::AggregateFunction<perfetto_sql::SortedTimestamps> {
static constexpr char kName[] = "__intrinsic_timestamp_set_agg";
static constexpr int kArgCount = 1;
static constexpr int kArgCount = 2;
struct AggCtx : sqlite::AggregateContext<AggCtx> {
perfetto_sql::SortedTimestamps data;
};

static void Step(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
PERFETTO_DCHECK(argc == kArgCount);
auto& data = AggCtx::GetOrCreateContextForStep(ctx).data;
data.timestamps.push_back(sqlite::value::Int64(argv[0]));
data.ids.push_back(sqlite::value::Int64(argv[0]));
data.timestamps.push_back(sqlite::value::Int64(argv[1]));
}

static void Final(sqlite3_context* ctx) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@

namespace perfetto::trace_processor::perfetto_sql {

// A sorted collection of timestamps used as an intermediate type for the
// interval_create intrinsic function. Timestamps are collected via an
// aggregate function and must be passed in ascending order (via ORDER BY)
// to the scalar function.
// A sorted collection of timestamps (with associated IDs) used as an
// intermediate type for the interval_create intrinsic function. Timestamps
// are collected via an aggregate function and must be passed in ascending
// order (via ORDER BY) to the scalar function.
struct SortedTimestamps {
static constexpr char kName[] = "SORTED_TIMESTAMPS";
std::vector<int64_t> timestamps;
// Row IDs corresponding 1:1 with timestamps. These are the original row
// IDs from the input table, used to produce start_id/end_id in output.
std::vector<int64_t> ids;
};

} // namespace perfetto::trace_processor::perfetto_sql
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,48 @@
-- intervals by matching each start with the next end timestamp strictly greater
-- than it.
--
-- Both input tables must have a column named `ts`. Uses an efficient O(n+m)
-- two-pointer algorithm implemented in C++.
-- Both input tables must have columns named `id` and `ts`. Uses an efficient
-- O(n+m) two-pointer algorithm implemented in C++.
--
-- Example:
-- ```
-- SELECT * FROM _interval_create!(
-- (SELECT ts FROM starts_table),
-- (SELECT ts FROM ends_table)
-- (SELECT id, ts FROM starts_table),
-- (SELECT id, ts FROM ends_table)
-- )
-- ```
CREATE PERFETTO MACRO _interval_create(
-- Table or subquery containing start timestamps (must have a `ts` column).
-- Table or subquery containing start timestamps (must have `id` and `ts`
-- columns).
starts_table TableOrSubquery,
-- Table or subquery containing end timestamps (must have a `ts` column).
-- Table or subquery containing end timestamps (must have `id` and `ts`
-- columns).
ends_table TableOrSubquery
)
-- Table with the schema:
-- ts TIMESTAMP,
-- The start timestamp.
-- dur DURATION,
-- The duration from start to the matched end.
-- start_id LONG,
-- The id of the matched start row.
-- end_id LONG,
-- The id of the matched end row.
RETURNS TableOrSubquery AS
(
SELECT
c0 AS ts,
c1 AS dur
c1 AS dur,
c2 AS start_id,
c3 AS end_id
FROM __intrinsic_table_ptr(
__intrinsic_interval_create(
(
SELECT
__intrinsic_timestamp_set_agg(ordered_s.ts)
__intrinsic_timestamp_set_agg(ordered_s.id, ordered_s.ts)
FROM (
SELECT
id,
ts
FROM $starts_table
ORDER BY
Expand All @@ -58,9 +67,10 @@ RETURNS TableOrSubquery AS
),
(
SELECT
__intrinsic_timestamp_set_agg(ordered_e.ts)
__intrinsic_timestamp_set_agg(ordered_e.id, ordered_e.ts)
FROM (
SELECT
id,
ts
FROM $ends_table
ORDER BY
Expand All @@ -70,5 +80,8 @@ RETURNS TableOrSubquery AS
)
)
WHERE
__intrinsic_table_ptr_bind(c0, 'ts') AND __intrinsic_table_ptr_bind(c1, 'dur')
__intrinsic_table_ptr_bind(c0, 'ts')
AND __intrinsic_table_ptr_bind(c1, 'dur')
AND __intrinsic_table_ptr_bind(c2, 'start_id')
AND __intrinsic_table_ptr_bind(c3, 'end_id')
);
Loading
Loading