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 #include "src/trace_processor/util/sql_argument.h"
18
19 #include <algorithm>
20 #include <cctype>
21 #include <optional>
22 #include <string>
23 #include <vector>
24
25 #include "perfetto/base/logging.h"
26 #include "perfetto/base/status.h"
27 #include "perfetto/ext/base/string_utils.h"
28 #include "perfetto/ext/base/string_view.h"
29 #include "perfetto/trace_processor/basic_types.h"
30
31 namespace perfetto::trace_processor::sql_argument {
32
IsValidName(base::StringView str)33 bool IsValidName(base::StringView str) {
34 if (str.empty()) {
35 return false;
36 }
37 auto pred = [](char c) { return !(isalnum(c) || c == '_'); };
38 return std::find_if(str.begin(), str.end(), pred) == str.end();
39 }
40
ParseType(base::StringView str)41 std::optional<Type> ParseType(base::StringView str) {
42 if (str.CaseInsensitiveEq("bool")) {
43 return Type::kBool;
44 }
45 if (str.CaseInsensitiveOneOf(
46 {"long", "timestamp", "duration", "id", "joinid", "argsetid"})) {
47 return Type::kLong;
48 }
49 if (str.CaseInsensitiveEq("double")) {
50 return Type::kDouble;
51 }
52 if (str.CaseInsensitiveEq("string")) {
53 return Type::kString;
54 }
55 if (str.CaseInsensitiveEq("bytes")) {
56 return Type::kBytes;
57 }
58
59 // Deprecated types.
60 // TODO(b/380259828): Remove.
61 if (str.CaseInsensitiveEq("int")) {
62 return Type::kInt;
63 }
64 if (str.CaseInsensitiveEq("uint")) {
65 return Type::kUint;
66 }
67 if (str.CaseInsensitiveEq("float")) {
68 return Type::kFloat;
69 }
70 if (str.CaseInsensitiveEq("proto")) {
71 return Type::kProto;
72 }
73 return std::nullopt;
74 }
75
TypeToHumanFriendlyString(sql_argument::Type type)76 const char* TypeToHumanFriendlyString(sql_argument::Type type) {
77 using Type = sql_argument::Type;
78 switch (type) {
79 case Type::kBool:
80 return "BOOL";
81 case Type::kInt:
82 return "INT";
83 case Type::kUint:
84 return "UINT";
85 case Type::kLong:
86 return "LONG";
87 case Type::kFloat:
88 return "FLOAT";
89 case Type::kDouble:
90 return "DOUBLE";
91 case Type::kString:
92 return "STRING";
93 case Type::kProto:
94 return "PROTO";
95 case Type::kBytes:
96 return "BYTES";
97 }
98 PERFETTO_FATAL("For GCC");
99 }
100
TypeToSqlValueType(sql_argument::Type type)101 SqlValue::Type TypeToSqlValueType(sql_argument::Type type) {
102 using Type = sql_argument::Type;
103 switch (type) {
104 case Type::kBool:
105 case Type::kInt:
106 case Type::kUint:
107 case Type::kLong:
108 return SqlValue::kLong;
109 case Type::kFloat:
110 case Type::kDouble:
111 return SqlValue::kDouble;
112 case Type::kString:
113 return SqlValue::kString;
114 case Type::kProto:
115 case Type::kBytes:
116 return SqlValue::kBytes;
117 }
118 PERFETTO_FATAL("For GCC");
119 }
120
ParseArgumentDefinitions(const std::string & args,std::vector<ArgumentDefinition> & out)121 base::Status ParseArgumentDefinitions(const std::string& args,
122 std::vector<ArgumentDefinition>& out) {
123 std::string trimmed_args = base::TrimWhitespace(args);
124 for (const auto& arg : base::SplitString(trimmed_args, ",")) {
125 const auto& arg_name_and_type =
126 (base::SplitString(base::TrimWhitespace(arg), " "));
127 if (arg_name_and_type.size() != 2) {
128 return base::ErrStatus(
129 "argument '%s' in function prototype should be of the form `name "
130 "TYPE`",
131 arg.c_str());
132 }
133
134 const auto& arg_name = arg_name_and_type[0];
135 const auto& arg_type_str = arg_name_and_type[1];
136 if (!IsValidName(base::StringView(arg_name)))
137 return base::ErrStatus("argument '%s' is not alphanumeric", arg.c_str());
138
139 auto opt_arg_type = ParseType(base::StringView(arg_type_str));
140 if (!opt_arg_type) {
141 return base::ErrStatus("unknown argument type in argument '%s'",
142 arg.c_str());
143 }
144 out.emplace_back("$" + arg_name, *opt_arg_type);
145 }
146 return base::OkStatus();
147 }
148
SerializeArguments(const std::vector<ArgumentDefinition> & args)149 std::string SerializeArguments(const std::vector<ArgumentDefinition>& args) {
150 bool comma = false;
151 std::string serialized;
152 for (const auto& arg : args) {
153 if (comma) {
154 serialized.append(", ");
155 }
156 comma = true;
157 serialized.append(arg.name().c_str());
158 serialized.push_back(' ');
159 serialized.append(TypeToHumanFriendlyString(arg.type()));
160 }
161 return serialized;
162 }
163
164 } // namespace perfetto::trace_processor::sql_argument
165