blob: 0a3f158d33bba123ba92400da7d8c57cd60433eb [file] [log] [blame]
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SRC_TRACE_PROCESSOR_STORAGE_COLUMNS_H_
#define SRC_TRACE_PROCESSOR_STORAGE_COLUMNS_H_
#include <deque>
#include <memory>
#include <string>
#include "src/trace_processor/filtered_row_index.h"
#include "src/trace_processor/sqlite_utils.h"
#include "src/trace_processor/trace_storage.h"
namespace perfetto {
namespace trace_processor {
// A column of data backed by data storage.
class StorageColumn {
public:
struct Bounds {
uint32_t min_idx = 0;
uint32_t max_idx = std::numeric_limits<uint32_t>::max();
bool consumed = false;
};
using Predicate = std::function<bool(uint32_t)>;
using Comparator = std::function<int(uint32_t, uint32_t)>;
StorageColumn(std::string col_name, bool hidden);
virtual ~StorageColumn();
// Implements StorageCursor::ColumnReporter.
virtual void ReportResult(sqlite3_context*, uint32_t row) const = 0;
// Given a SQLite operator and value for the comparision, returns a
// predicate which takes in a row index and returns whether the row should
// be returned.
virtual void Filter(int op, sqlite3_value*, FilteredRowIndex*) const = 0;
// Given a order by constraint for this column, returns a comparator
// function which compares data in this column at two indices.
virtual Comparator Sort(const QueryConstraints::OrderBy& ob) const = 0;
// Returns the type of this column.
virtual Table::ColumnType GetType() const = 0;
// Bounds a filter on this column between a minimum and maximum index.
// Generally this is only possible if the column is sorted.
virtual Bounds BoundFilter(int, sqlite3_value*) const { return Bounds{}; }
// Returns whether this column is sorted in the storage.
virtual bool IsNaturallyOrdered() const { return false; }
const std::string& name() const { return col_name_; }
bool hidden() const { return hidden_; }
private:
std::string col_name_;
bool hidden_ = false;
};
// A column of numeric data backed by a deque.
template <typename T>
class NumericColumn : public StorageColumn {
public:
// |index| is an optional multimap which maps the values in deque
// to the rows they are located at.
NumericColumn(std::string col_name,
const std::deque<T>* deque,
const std::deque<std::vector<uint32_t>>* index,
bool hidden,
bool is_naturally_ordered)
: StorageColumn(col_name, hidden),
deque_(deque),
index_(index),
is_naturally_ordered_(is_naturally_ordered) {}
void ReportResult(sqlite3_context* ctx, uint32_t row) const override {
sqlite_utils::ReportSqliteResult(ctx, (*deque_)[row]);
}
Bounds BoundFilter(int op, sqlite3_value* sqlite_val) const override {
Bounds bounds;
bounds.max_idx = static_cast<uint32_t>(deque_->size());
if (!is_naturally_ordered_)
return bounds;
// Makes the below code much more readable.
using namespace sqlite_utils;
T min = kTMin;
T max = kTMax;
if (IsOpGe(op) || IsOpGt(op)) {
min = FindGtBound<T>(IsOpGe(op), sqlite_val);
} else if (IsOpLe(op) || IsOpLt(op)) {
max = FindLtBound<T>(IsOpLe(op), sqlite_val);
} else if (IsOpEq(op)) {
auto val = FindEqBound<T>(sqlite_val);
min = val;
max = val;
}
if (min <= kTMin && max >= kTMax)
return bounds;
// Convert the values into indices into the deque.
auto min_it = std::lower_bound(deque_->begin(), deque_->end(), min);
bounds.min_idx =
static_cast<uint32_t>(std::distance(deque_->begin(), min_it));
auto max_it = std::upper_bound(min_it, deque_->end(), max);
bounds.max_idx =
static_cast<uint32_t>(std::distance(deque_->begin(), max_it));
bounds.consumed = true;
return bounds;
}
void Filter(int op,
sqlite3_value* value,
FilteredRowIndex* index) const override {
auto type = sqlite3_value_type(value);
// If we are doing equality filtering on integers, try and use the index.
bool is_int_compare = std::is_integral<T>::value && type == SQLITE_INTEGER;
if (sqlite_utils::IsOpEq(op) && is_int_compare && index_ != nullptr) {
FilterIntegerIndexEq(value, index);
return;
}
bool is_null = type == SQLITE_NULL;
if (std::is_integral<T>::value && (type == SQLITE_INTEGER || is_null)) {
FilterWithCast<int64_t>(op, value, index);
} else if (type == SQLITE_INTEGER || type == SQLITE_FLOAT || is_null) {
FilterWithCast<double>(op, value, index);
} else {
PERFETTO_FATAL("Unexpected sqlite value to compare against");
}
}
Comparator Sort(const QueryConstraints::OrderBy& ob) const override {
if (ob.desc) {
return [this](uint32_t f, uint32_t s) {
return sqlite_utils::CompareValuesDesc((*deque_)[f], (*deque_)[s]);
};
}
return [this](uint32_t f, uint32_t s) {
return sqlite_utils::CompareValuesAsc((*deque_)[f], (*deque_)[s]);
};
}
bool IsNaturallyOrdered() const override { return is_naturally_ordered_; }
Table::ColumnType GetType() const override {
if (std::is_same<T, int32_t>::value) {
return Table::ColumnType::kInt;
} else if (std::is_same<T, uint8_t>::value ||
std::is_same<T, uint32_t>::value) {
return Table::ColumnType::kUint;
} else if (std::is_same<T, int64_t>::value) {
return Table::ColumnType::kLong;
} else if (std::is_same<T, double>::value) {
return Table::ColumnType::kDouble;
}
PERFETTO_FATAL("Unexpected column type");
}
protected:
const std::deque<T>* deque_ = nullptr;
const std::deque<std::vector<uint32_t>>* index_ = nullptr;
private:
T kTMin = std::numeric_limits<T>::lowest();
T kTMax = std::numeric_limits<T>::max();
void FilterIntegerIndexEq(sqlite3_value* value,
FilteredRowIndex* index) const {
auto raw = sqlite_utils::ExtractSqliteValue<int64_t>(value);
if (raw < 0 || raw >= static_cast<int64_t>(index_->size())) {
index->IntersectRows({});
return;
}
index->IntersectRows((*index_)[static_cast<size_t>(raw)]);
}
template <typename C>
void FilterWithCast(int op,
sqlite3_value* value,
FilteredRowIndex* index) const {
auto predicate = sqlite_utils::CreateNumericPredicate<C>(op, value);
index->FilterRows([this, &predicate](uint32_t row) {
return predicate(static_cast<C>((*deque_)[row]));
});
}
bool is_naturally_ordered_ = false;
};
template <typename Id>
class StringColumn final : public StorageColumn {
public:
StringColumn(std::string col_name,
const std::deque<Id>* deque,
const std::deque<std::string>* string_map,
bool hidden = false)
: StorageColumn(col_name, hidden),
deque_(deque),
string_map_(string_map) {}
void ReportResult(sqlite3_context* ctx, uint32_t row) const override {
const auto& str = (*string_map_)[(*deque_)[row]];
if (str.empty()) {
sqlite3_result_null(ctx);
} else {
sqlite3_result_text(ctx, str.c_str(), -1, sqlite_utils::kSqliteStatic);
}
}
Bounds BoundFilter(int, sqlite3_value*) const override {
Bounds bounds;
bounds.max_idx = static_cast<uint32_t>(deque_->size());
return bounds;
}
void Filter(int, sqlite3_value*, FilteredRowIndex*) const override {}
Comparator Sort(const QueryConstraints::OrderBy& ob) const override {
if (ob.desc) {
return [this](uint32_t f, uint32_t s) {
const std::string& a = (*string_map_)[(*deque_)[f]];
const std::string& b = (*string_map_)[(*deque_)[s]];
return sqlite_utils::CompareValuesDesc(a, b);
};
}
return [this](uint32_t f, uint32_t s) {
const std::string& a = (*string_map_)[(*deque_)[f]];
const std::string& b = (*string_map_)[(*deque_)[s]];
return sqlite_utils::CompareValuesAsc(a, b);
};
}
Table::ColumnType GetType() const override {
return Table::ColumnType::kString;
}
bool IsNaturallyOrdered() const override { return false; }
private:
const std::deque<Id>* deque_ = nullptr;
const std::deque<std::string>* string_map_ = nullptr;
};
// Column which represents the "ts_end" column present in all time based
// tables. It is computed by adding together the values in two deques.
class TsEndColumn final : public StorageColumn {
public:
TsEndColumn(std::string col_name,
const std::deque<int64_t>* ts_start,
const std::deque<int64_t>* dur);
virtual ~TsEndColumn() override;
void ReportResult(sqlite3_context*, uint32_t) const override;
Bounds BoundFilter(int op, sqlite3_value* value) const override;
void Filter(int op, sqlite3_value* value, FilteredRowIndex*) const override;
Comparator Sort(const QueryConstraints::OrderBy& ob) const override;
// Returns the type of this column.
Table::ColumnType GetType() const override {
return Table::ColumnType::kLong;
}
bool IsNaturallyOrdered() const override { return false; }
private:
const std::deque<int64_t>* ts_start_;
const std::deque<int64_t>* dur_;
};
// Column which is used to reference the args table in other tables. That is,
// it acts as a "foreign key" into the args table.
class IdColumn final : public StorageColumn {
public:
IdColumn(std::string column_name, TableId table_id);
virtual ~IdColumn() override;
void ReportResult(sqlite3_context* ctx, uint32_t row) const override {
auto id = TraceStorage::CreateRowId(table_id_, row);
sqlite_utils::ReportSqliteResult(ctx, id);
}
Bounds BoundFilter(int, sqlite3_value*) const override { return Bounds{}; }
void Filter(int op,
sqlite3_value* value,
FilteredRowIndex* index) const override {
auto predicate = sqlite_utils::CreateNumericPredicate<RowId>(op, value);
index->FilterRows([this, &predicate](uint32_t row) {
return predicate(TraceStorage::CreateRowId(table_id_, row));
});
}
Comparator Sort(const QueryConstraints::OrderBy& ob) const override {
if (ob.desc) {
return [this](uint32_t f, uint32_t s) {
auto a = TraceStorage::CreateRowId(table_id_, f);
auto b = TraceStorage::CreateRowId(table_id_, s);
return sqlite_utils::CompareValuesDesc(a, b);
};
}
return [this](uint32_t f, uint32_t s) {
auto a = TraceStorage::CreateRowId(table_id_, f);
auto b = TraceStorage::CreateRowId(table_id_, s);
return sqlite_utils::CompareValuesAsc(a, b);
};
}
Table::ColumnType GetType() const override {
return Table::ColumnType::kLong;
}
bool IsNaturallyOrdered() const override { return false; }
private:
TableId table_id_;
};
} // namespace trace_processor
} // namespace perfetto
#endif // SRC_TRACE_PROCESSOR_STORAGE_COLUMNS_H_