1 /*
2 * Copyright (C) 2022 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 * http://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
17 #ifndef SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_FUNCTIONS_WINDOW_FUNCTIONS_H_
18 #define SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_FUNCTIONS_WINDOW_FUNCTIONS_H_
19
20 #include <sqlite3.h>
21 #include <cstdint>
22 #include <type_traits>
23
24 #include "perfetto/base/logging.h"
25 #include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h"
26 #include "src/trace_processor/sqlite/bindings/sqlite_result.h"
27 #include "src/trace_processor/sqlite/bindings/sqlite_window_function.h"
28
29 namespace perfetto::trace_processor {
30
31 // Keeps track of the latest non null value and its position withing the
32 // window. Every time the window shrinks (`xInverse` is called) the window size
33 // is reduced by one and the position of the value moves one back, if it gets
34 // out of the window the value is discarded.
35 class LastNonNullAggregateContext {
36 public:
Get(sqlite3_context * ctx)37 static LastNonNullAggregateContext* Get(sqlite3_context* ctx) {
38 return reinterpret_cast<LastNonNullAggregateContext*>(
39 sqlite3_aggregate_context(ctx, 0));
40 }
41
GetOrCreate(sqlite3_context * ctx)42 static LastNonNullAggregateContext* GetOrCreate(sqlite3_context* ctx) {
43 return reinterpret_cast<LastNonNullAggregateContext*>(
44 sqlite3_aggregate_context(ctx, sizeof(LastNonNullAggregateContext)));
45 }
46
PopFront()47 inline void PopFront() {
48 PERFETTO_CHECK(window_size_ > 0);
49 --window_size_;
50 if (!last_non_null_value_) {
51 return;
52 }
53 if (value_index_ == 0) {
54 sqlite3_value_free(last_non_null_value_);
55 last_non_null_value_ = nullptr;
56 return;
57 }
58 PERFETTO_CHECK(value_index_ > 0);
59 --value_index_;
60 }
61
PushBack(sqlite3_value * value)62 inline void PushBack(sqlite3_value* value) {
63 ++window_size_;
64 if (sqlite3_value_type(value) == SQLITE_NULL) {
65 return;
66 }
67
68 Destroy();
69 last_non_null_value_ = sqlite3_value_dup(value);
70 value_index_ = window_size_ - 1;
71 }
72
Destroy()73 inline void Destroy() {
74 if (last_non_null_value_) {
75 sqlite3_value_free(last_non_null_value_);
76 }
77 }
78
last_non_null_value()79 sqlite3_value* last_non_null_value() const { return last_non_null_value_; }
80
81 private:
82 int64_t window_size_;
83 // Index within the window of the last non null value. Only valid if `value`
84 // is set.
85 int64_t value_index_;
86 // Actual value
87 sqlite3_value* last_non_null_value_;
88 };
89
90 static_assert(std::is_standard_layout_v<LastNonNullAggregateContext>,
91 "Must be able to be initialized by sqlite3_aggregate_context "
92 "(similar to calloc, i.e. no constructor called)");
93 static_assert(std::is_trivial_v<LastNonNullAggregateContext>,
94 "Must be able to be destroyed by just calling free (i.e. no "
95 "destructor called)");
96
97 class LastNonNull : public SqliteWindowFunction {
98 public:
Step(sqlite3_context * ctx,int argc,sqlite3_value ** argv)99 static void Step(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
100 if (argc != 1) {
101 return sqlite::result::Error(
102 ctx, "Unsupported number of args passed to LAST_NON_NULL");
103 }
104
105 auto* ptr = LastNonNullAggregateContext::GetOrCreate(ctx);
106 if (!ptr) {
107 return sqlite::result::Error(ctx,
108 "LAST_NON_NULL: Failed to allocate context");
109 }
110
111 ptr->PushBack(argv[0]);
112 }
113
Inverse(sqlite3_context * ctx,int,sqlite3_value **)114 static void Inverse(sqlite3_context* ctx, int, sqlite3_value**) {
115 auto* ptr = LastNonNullAggregateContext::GetOrCreate(ctx);
116 PERFETTO_CHECK(ptr != nullptr);
117 ptr->PopFront();
118 }
119
Value(sqlite3_context * ctx)120 static void Value(sqlite3_context* ctx) {
121 auto* ptr = LastNonNullAggregateContext::GetOrCreate(ctx);
122 if (!ptr || !ptr->last_non_null_value()) {
123 return sqlite::result::Null(ctx);
124 }
125 sqlite3_result_value(ctx, ptr->last_non_null_value());
126 }
127
Final(sqlite3_context * ctx)128 static void Final(sqlite3_context* ctx) {
129 auto* ptr = LastNonNullAggregateContext::Get(ctx);
130 if (!ptr || !ptr->last_non_null_value()) {
131 return sqlite::result::Null(ctx);
132 }
133 sqlite::result::Value(ctx, ptr->last_non_null_value());
134 ptr->Destroy();
135 }
136 };
137
RegisterLastNonNullFunction(PerfettoSqlEngine & engine)138 inline base::Status RegisterLastNonNullFunction(PerfettoSqlEngine& engine) {
139 return engine.RegisterSqliteWindowFunction<LastNonNull>("LAST_NON_NULL", 1,
140 nullptr);
141 }
142
143 } // namespace perfetto::trace_processor
144
145 #endif // SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_FUNCTIONS_WINDOW_FUNCTIONS_H_
146