|  | /* | 
|  | * Copyright (C) 2018 The Android Open Source Project | 
|  | * | 
|  | * 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. | 
|  | */ | 
|  |  | 
|  | #include "src/trace_processor/sqlite/query_constraints.h" | 
|  |  | 
|  | #include <sqlite3.h> | 
|  |  | 
|  | #include <string> | 
|  |  | 
|  | #include "perfetto/ext/base/string_splitter.h" | 
|  | #include "perfetto/ext/base/string_utils.h" | 
|  |  | 
|  | namespace perfetto { | 
|  | namespace trace_processor { | 
|  |  | 
|  | QueryConstraints::QueryConstraints(uint64_t cols_used) | 
|  | : cols_used_(cols_used) {} | 
|  | QueryConstraints::~QueryConstraints() = default; | 
|  | QueryConstraints::QueryConstraints(QueryConstraints&&) noexcept = default; | 
|  | QueryConstraints& QueryConstraints::operator=(QueryConstraints&&) noexcept = | 
|  | default; | 
|  |  | 
|  | int QueryConstraints::FreeSqliteString(char* resource) { | 
|  | sqlite3_free(resource); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | bool QueryConstraints::operator==(const QueryConstraints& other) const { | 
|  | if ((other.constraints().size() != constraints().size()) || | 
|  | (other.order_by().size() != order_by().size()) || | 
|  | other.cols_used() != cols_used()) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | for (size_t i = 0; i < constraints().size(); ++i) { | 
|  | if ((constraints()[i].column != other.constraints()[i].column) || | 
|  | (constraints()[i].op != other.constraints()[i].op)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (size_t i = 0; i < order_by().size(); ++i) { | 
|  | if ((order_by()[i].iColumn != other.order_by()[i].iColumn) || | 
|  | (order_by()[i].desc != other.order_by()[i].desc)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void QueryConstraints::AddConstraint(int column, | 
|  | unsigned char op, | 
|  | int aconstraint_idx) { | 
|  | Constraint c{}; | 
|  | c.column = column; | 
|  | c.op = op; | 
|  | c.a_constraint_idx = aconstraint_idx; | 
|  | constraints_.emplace_back(c); | 
|  | } | 
|  |  | 
|  | void QueryConstraints::AddOrderBy(int column, unsigned char desc) { | 
|  | OrderBy ob{}; | 
|  | ob.iColumn = column; | 
|  | ob.desc = desc; | 
|  | order_by_.emplace_back(ob); | 
|  | } | 
|  |  | 
|  | QueryConstraints::SqliteString QueryConstraints::ToNewSqlite3String() const { | 
|  | std::string str_result; | 
|  | str_result.reserve(512); | 
|  |  | 
|  | // Add all the constraints. | 
|  | str_result.append("C"); | 
|  | str_result.append(std::to_string(constraints_.size())); | 
|  | str_result.append(","); | 
|  | for (const auto& cs : constraints_) { | 
|  | str_result.append(std::to_string(cs.column)); | 
|  | str_result.append(","); | 
|  | str_result.append(std::to_string(cs.op)); | 
|  | str_result.append(","); | 
|  | } | 
|  | str_result.back() = ';'; | 
|  |  | 
|  | // Add all the clauses. | 
|  | str_result.append("O"); | 
|  | str_result.append(std::to_string(order_by_.size())); | 
|  | str_result.append(","); | 
|  | for (const auto& ob : order_by_) { | 
|  | str_result.append(std::to_string(ob.iColumn)); | 
|  | str_result.append(","); | 
|  | str_result.append(std::to_string(ob.desc)); | 
|  | str_result.append(","); | 
|  | } | 
|  | str_result.back() = ';'; | 
|  |  | 
|  | // Add the columns used. | 
|  | str_result.append("U"); | 
|  | str_result.append(std::to_string(cols_used_)); | 
|  |  | 
|  | SqliteString result(static_cast<char*>( | 
|  | sqlite3_malloc(static_cast<int>(str_result.size() + 1)))); | 
|  | base::StringCopy(result.get(), str_result.c_str(), str_result.size() + 1); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | QueryConstraints QueryConstraints::FromString(const char* idxStr) { | 
|  | QueryConstraints qc; | 
|  |  | 
|  | base::StringSplitter outer_splitter(std::string(idxStr), ';'); | 
|  |  | 
|  | // Handle the CONSTRAINT section of the string. | 
|  | PERFETTO_CHECK(outer_splitter.Next() && outer_splitter.cur_token_size() > 1); | 
|  | { | 
|  | base::StringSplitter splitter(&outer_splitter, ','); | 
|  | PERFETTO_CHECK(splitter.Next() && splitter.cur_token_size() > 1); | 
|  |  | 
|  | // The '[1]' skips the letter 'C' in the first token. | 
|  | int64_t num_constraints = *base::CStringToInt64(&splitter.cur_token()[1]); | 
|  | for (int i = 0; i < num_constraints; ++i) { | 
|  | PERFETTO_CHECK(splitter.Next()); | 
|  | int col = static_cast<int>(*base::CStringToInt32(splitter.cur_token())); | 
|  | PERFETTO_CHECK(splitter.Next()); | 
|  | unsigned char op = static_cast<unsigned char>( | 
|  | *base::CStringToUInt32(splitter.cur_token())); | 
|  | qc.AddConstraint(col, op, 0); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Handle the ORDER BY section of the string. | 
|  | PERFETTO_CHECK(outer_splitter.Next() && outer_splitter.cur_token_size() > 1); | 
|  | { | 
|  | base::StringSplitter splitter(&outer_splitter, ','); | 
|  | PERFETTO_CHECK(splitter.Next() && splitter.cur_token_size() > 1); | 
|  |  | 
|  | // The '[1]' skips the letter 'O' in the current token. | 
|  | int64_t num_order_by = *base::CStringToInt64(&splitter.cur_token()[1]); | 
|  | for (int i = 0; i < num_order_by; ++i) { | 
|  | PERFETTO_CHECK(splitter.Next()); | 
|  | int col = static_cast<int>(*base::CStringToInt32(splitter.cur_token())); | 
|  | PERFETTO_CHECK(splitter.Next()); | 
|  | unsigned char desc = static_cast<unsigned char>( | 
|  | *base::CStringToUInt32(splitter.cur_token())); | 
|  | qc.AddOrderBy(col, desc); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Handle the COLS USED section of the string. | 
|  | PERFETTO_CHECK(outer_splitter.Next() && outer_splitter.cur_token_size() > 1); | 
|  | { | 
|  | // The '[1]' skips the letter 'U' in the current token. | 
|  | qc.cols_used_ = *base::CStringToUInt64(&outer_splitter.cur_token()[1]); | 
|  | } | 
|  |  | 
|  | PERFETTO_DCHECK(!outer_splitter.Next()); | 
|  | return qc; | 
|  | } | 
|  |  | 
|  | }  // namespace trace_processor | 
|  | }  // namespace perfetto |