/Users/deen/code/yugabyte-db/src/yb/yql/cql/ql/ptree/pt_table_property.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) YugaByte, Inc. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
4 | | // in compliance with the License. You may obtain a copy of the License at |
5 | | // |
6 | | // http://www.apache.org/licenses/LICENSE-2.0 |
7 | | // |
8 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
9 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
10 | | // or implied. See the License for the specific language governing permissions and limitations |
11 | | // under the License. |
12 | | // |
13 | | |
14 | | #include "yb/yql/cql/ql/ptree/pt_table_property.h" |
15 | | |
16 | | #include <set> |
17 | | |
18 | | #include <boost/algorithm/string/predicate.hpp> |
19 | | |
20 | | #include "yb/client/schema.h" |
21 | | #include "yb/client/table.h" |
22 | | |
23 | | #include "yb/common/schema.h" |
24 | | #include "yb/common/table_properties_constants.h" |
25 | | |
26 | | #include "yb/gutil/casts.h" |
27 | | |
28 | | #include "yb/util/stol_utils.h" |
29 | | #include "yb/util/string_case.h" |
30 | | #include "yb/util/string_util.h" |
31 | | |
32 | | #include "yb/yql/cql/ql/ptree/column_desc.h" |
33 | | #include "yb/yql/cql/ql/ptree/pt_alter_table.h" |
34 | | #include "yb/yql/cql/ql/ptree/pt_column_definition.h" |
35 | | #include "yb/yql/cql/ql/ptree/pt_create_table.h" |
36 | | #include "yb/yql/cql/ql/ptree/pt_expr.h" |
37 | | #include "yb/yql/cql/ql/ptree/pt_option.h" |
38 | | #include "yb/yql/cql/ql/ptree/sem_context.h" |
39 | | #include "yb/yql/cql/ql/ptree/yb_location.h" |
40 | | |
41 | | namespace yb { |
42 | | namespace ql { |
43 | | |
44 | | namespace { |
45 | | |
46 | | const std::string kCompactionClassPrefix = "org.apache.cassandra.db.compaction."; |
47 | | |
48 | | } |
49 | | |
50 | | using strings::Substitute; |
51 | | using client::YBColumnSchema; |
52 | | |
53 | | // These property names need to be lowercase, since identifiers are converted to lowercase by the |
54 | | // scanner phase and as a result if we're doing string matching everything should be lowercase. |
55 | | const std::map<std::string, PTTableProperty::KVProperty> PTTableProperty::kPropertyDataTypes |
56 | | = { |
57 | | {"bloom_filter_fp_chance", KVProperty::kBloomFilterFpChance}, |
58 | | {"caching", KVProperty::kCaching}, |
59 | | {"comment", KVProperty::kComment}, |
60 | | {"compaction", KVProperty::kCompaction}, |
61 | | {"compression", KVProperty::kCompression}, |
62 | | {"crc_check_chance", KVProperty::kCrcCheckChance}, |
63 | | {"dclocal_read_repair_chance", KVProperty::kDclocalReadRepairChance}, |
64 | | {"default_time_to_live", KVProperty::kDefaultTimeToLive}, |
65 | | {"gc_grace_seconds", KVProperty::kGcGraceSeconds}, |
66 | | {"index_interval", KVProperty::kIndexInterval}, |
67 | | {"memtable_flush_period_in_ms", KVProperty::kMemtableFlushPeriodInMs}, |
68 | | {"min_index_interval", KVProperty::kMinIndexInterval}, |
69 | | {"max_index_interval", KVProperty::kMaxIndexInterval}, |
70 | | {"read_repair_chance", KVProperty::kReadRepairChance}, |
71 | | {"speculative_retry", KVProperty::kSpeculativeRetry}, |
72 | | {"transactions", KVProperty::kTransactions}, |
73 | | {"tablets", KVProperty::kNumTablets} |
74 | | }; |
75 | | |
76 | | PTTableProperty::PTTableProperty(MemoryContext *memctx, |
77 | | YBLocationPtr loc, |
78 | | const MCSharedPtr<MCString>& lhs, |
79 | | const PTExprPtr& rhs) |
80 | | : PTProperty(memctx, loc, lhs, rhs), |
81 | 1.52k | property_type_(PropertyType::kTableProperty) {} |
82 | | |
83 | | PTTableProperty::PTTableProperty(MemoryContext *memctx, |
84 | | YBLocationPtr loc, |
85 | | const PTExprPtr& expr, |
86 | | const PTOrderBy::Direction direction) |
87 | | : PTProperty(memctx, loc), order_expr_(expr), direction_(direction), |
88 | 331 | property_type_(PropertyType::kClusteringOrder) {} |
89 | | |
90 | | PTTableProperty::PTTableProperty(MemoryContext *memctx, |
91 | | YBLocation::SharedPtr loc, |
92 | | const PTQualifiedName::SharedPtr tname) |
93 | | : PTProperty(memctx, loc), property_type_(PropertyType::kCoPartitionTable), |
94 | 0 | copartition_table_name_(tname) {} |
95 | | |
96 | | PTTableProperty::PTTableProperty(MemoryContext *memctx, |
97 | | YBLocation::SharedPtr loc) |
98 | 529 | : PTProperty(memctx, loc) { |
99 | 529 | } |
100 | | |
101 | 2.06k | PTTableProperty::~PTTableProperty() { |
102 | 2.06k | } |
103 | | |
104 | | |
105 | 30 | Status PTTableProperty::AnalyzeSpeculativeRetry(const string &val) { |
106 | 30 | string generic_error = Substitute("Invalid value $0 for option 'speculative_retry'", val); |
107 | | |
108 | | // Accepted values: ALWAYS, Xpercentile, Nms, NONE. |
109 | 30 | if (val == common::kSpeculativeRetryAlways || val == common::kSpeculativeRetryNone28 ) { |
110 | 4 | return Status::OK(); |
111 | 4 | } |
112 | | |
113 | 26 | string numeric_val; |
114 | 26 | if (StringEndsWith(val, common::kSpeculativeRetryMs, common::kSpeculativeRetryMsLen, |
115 | 26 | &numeric_val)) { |
116 | 3 | RETURN_NOT_OK(CheckedStold(numeric_val)); |
117 | 1 | return Status::OK(); |
118 | 3 | } |
119 | | |
120 | 23 | if (StringEndsWith(val, common::kSpeculativeRetryPercentile, |
121 | 23 | common::kSpeculativeRetryPercentileLen, &numeric_val)) { |
122 | 21 | auto percentile = CheckedStold(numeric_val); |
123 | 21 | RETURN_NOT_OK(percentile); |
124 | | |
125 | 19 | if (*percentile < 0.0 || *percentile > 100.0) { |
126 | 0 | return STATUS(InvalidArgument, Substitute( |
127 | 0 | "Invalid value $0 for PERCENTILE option 'speculative_retry': " |
128 | 0 | "must be between 0.0 and 100.0", numeric_val)); |
129 | 0 | } |
130 | 19 | return Status::OK(); |
131 | 19 | } |
132 | 2 | return STATUS(InvalidArgument, generic_error); |
133 | 23 | } |
134 | | |
135 | 331 | string PTTableProperty::name() const { |
136 | 331 | DCHECK_EQ(property_type_, PropertyType::kClusteringOrder); |
137 | 331 | return order_expr_->QLName(); |
138 | 331 | } |
139 | | |
140 | 532 | CHECKED_STATUS PTTableProperty::Analyze(SemContext *sem_context) { |
141 | | |
142 | 532 | if (property_type_ == PropertyType::kCoPartitionTable) { |
143 | 0 | RETURN_NOT_OK(copartition_table_name_->AnalyzeName(sem_context, ObjectType::TABLE)); |
144 | | |
145 | 0 | bool is_system; // ignored |
146 | 0 | MCVector<ColumnDesc> copartition_table_columns(sem_context->PTempMem()); |
147 | | |
148 | | // Permissions check happen in LookupTable if flag use_cassandra_authentication is enabled. |
149 | | // TODO(hector): We need to revisit this once this feature is supported so we can decided |
150 | | // which privileges will be needed. |
151 | 0 | RETURN_NOT_OK(sem_context->LookupTable(copartition_table_name_->ToTableName(), |
152 | 0 | copartition_table_name_->loc(), /* write_table = */ true, |
153 | 0 | PermissionType::CREATE_PERMISSION, |
154 | 0 | &copartition_table_, &is_system, |
155 | 0 | &copartition_table_columns)); |
156 | 0 | if (sem_context->current_create_table_stmt()->hash_columns().size() != |
157 | 0 | copartition_table_->schema().num_hash_key_columns()) { |
158 | 0 | return sem_context->Error(this, Substitute("The number of hash keys in the current table " |
159 | 0 | "differ from the number of hash keys in '$0'.", |
160 | 0 | copartition_table_name_->ToTableName().table_name()).c_str(), |
161 | 0 | ErrorCode::INCOMPATIBLE_COPARTITION_SCHEMA); |
162 | 0 | } |
163 | | |
164 | 0 | int index = 0; |
165 | 0 | for (auto hash_col : sem_context->current_create_table_stmt()->hash_columns()) { |
166 | 0 | auto type = hash_col->ql_type(); |
167 | 0 | auto base_type = copartition_table_columns[index].ql_type(); |
168 | 0 | if (type != base_type) { |
169 | 0 | return sem_context->Error(this, Substitute("The hash key '$0' in the current table has a " |
170 | 0 | "different datatype from the corresponding hash key in '$1'", |
171 | 0 | hash_col->yb_name(), |
172 | 0 | copartition_table_name_->ToTableName().table_name()).c_str(), |
173 | 0 | ErrorCode::INCOMPATIBLE_COPARTITION_SCHEMA); |
174 | 0 | } |
175 | 0 | index++; |
176 | 0 | } |
177 | | |
178 | 0 | return Status::OK(); |
179 | 0 | } |
180 | | |
181 | | // Verify we have a valid property name in the lhs. |
182 | 532 | const auto& table_property_name = lhs_->c_str(); |
183 | 532 | auto iterator = kPropertyDataTypes.find(table_property_name); |
184 | 532 | if (iterator == kPropertyDataTypes.end()) { |
185 | 0 | return sem_context->Error(this, Substitute("Unknown property '$0'", lhs_->c_str()).c_str(), |
186 | 0 | ErrorCode::INVALID_TABLE_PROPERTY); |
187 | 0 | } |
188 | | |
189 | 532 | long double double_val; |
190 | 532 | int64_t int_val; |
191 | 532 | string str_val; |
192 | | |
193 | 532 | switch (iterator->second) { |
194 | 34 | case KVProperty::kBloomFilterFpChance: |
195 | 34 | RETURN_SEM_CONTEXT_ERROR_NOT_OK(GetDoubleValueFromExpr(rhs_, table_property_name, |
196 | 34 | &double_val)); |
197 | 26 | if (double_val <= 0.0 || double_val > 1.025 ) { |
198 | 3 | return sem_context->Error(this, |
199 | 3 | Substitute("$0 must be larger than 0 and less than or equal to 1.0 (got $1)", |
200 | 3 | table_property_name, std::to_string(double_val)).c_str(), |
201 | 3 | ErrorCode::INVALID_ARGUMENTS); |
202 | 3 | } |
203 | 23 | break; |
204 | 35 | case KVProperty::kCrcCheckChance: FALLTHROUGH_INTENDED; |
205 | 69 | case KVProperty::kDclocalReadRepairChance: FALLTHROUGH_INTENDED; |
206 | 103 | case KVProperty::kReadRepairChance: |
207 | 103 | RETURN_SEM_CONTEXT_ERROR_NOT_OK(GetDoubleValueFromExpr(rhs_, table_property_name, |
208 | 103 | &double_val)); |
209 | 78 | if (double_val < 0.0 || double_val > 1.075 ) { |
210 | 9 | return sem_context->Error(this, |
211 | 9 | Substitute( |
212 | 9 | "$0 must be larger than or equal to 0 and smaller than or equal to 1.0 (got $1)", |
213 | 9 | table_property_name, std::to_string(double_val)).c_str(), |
214 | 9 | ErrorCode::INVALID_ARGUMENTS); |
215 | 9 | } |
216 | 69 | break; |
217 | 214 | case KVProperty::kDefaultTimeToLive: |
218 | 214 | RETURN_SEM_CONTEXT_ERROR_NOT_OK(GetIntValueFromExpr(rhs_, table_property_name, &int_val)); |
219 | | // TTL value is entered by user in seconds, but we store internally in milliseconds. |
220 | 208 | if (!common::IsValidTTLSeconds(int_val)) { |
221 | 1 | return sem_context->Error(this, Substitute("Valid ttl range : [$0, $1]", |
222 | 1 | common::kCassandraMinTtlSeconds, |
223 | 1 | common::kCassandraMaxTtlSeconds).c_str(), |
224 | 1 | ErrorCode::INVALID_ARGUMENTS); |
225 | 1 | } |
226 | 207 | break; |
227 | 207 | case KVProperty::kGcGraceSeconds: 23 FALLTHROUGH_INTENDED23 ; |
228 | 40 | case KVProperty::kMemtableFlushPeriodInMs: |
229 | 40 | RETURN_SEM_CONTEXT_ERROR_NOT_OK(GetIntValueFromExpr(rhs_, table_property_name, &int_val)); |
230 | 35 | if (int_val < 0) { |
231 | 0 | return sem_context->Error(this, |
232 | 0 | Substitute("$0 must be greater than or equal to 0 (got $1)", |
233 | 0 | table_property_name, std::to_string(int_val)).c_str(), |
234 | 0 | ErrorCode::INVALID_ARGUMENTS); |
235 | 0 | } |
236 | 35 | break; |
237 | 35 | case KVProperty::kIndexInterval: 7 FALLTHROUGH_INTENDED7 ; |
238 | 31 | case KVProperty::kMinIndexInterval: FALLTHROUGH_INTENDED; |
239 | 55 | case KVProperty::kMaxIndexInterval: |
240 | | // TODO(hector): Check that kMaxIndexInterval is greater than kMinIndexInterval. |
241 | 55 | RETURN_SEM_CONTEXT_ERROR_NOT_OK(GetIntValueFromExpr(rhs_, table_property_name, &int_val)); |
242 | 40 | if (int_val < 1) { |
243 | 3 | return sem_context->Error(this, |
244 | 3 | Substitute("$0 must be greater than or equal to 1 (got $1)", |
245 | 3 | table_property_name, std::to_string(int_val)).c_str(), |
246 | 3 | ErrorCode::INVALID_ARGUMENTS); |
247 | 3 | } |
248 | 37 | break; |
249 | 37 | case KVProperty::kSpeculativeRetry: |
250 | 31 | RETURN_SEM_CONTEXT_ERROR_NOT_OK(GetStringValueFromExpr(rhs_, true, table_property_name, |
251 | 31 | &str_val)); |
252 | 27 | RETURN_SEM_CONTEXT_ERROR_NOT_OK(AnalyzeSpeculativeRetry(str_val)); |
253 | 24 | break; |
254 | 24 | case KVProperty::kComment: |
255 | 23 | RETURN_SEM_CONTEXT_ERROR_NOT_OK(GetStringValueFromExpr(rhs_, true, table_property_name, |
256 | 23 | &str_val)); |
257 | 19 | break; |
258 | 19 | case KVProperty::kCompaction: 5 FALLTHROUGH_INTENDED5 ; |
259 | 10 | case KVProperty::kCaching: FALLTHROUGH_INTENDED; |
260 | 10 | case KVProperty::kCompression: FALLTHROUGH_INTENDED; |
261 | 12 | case KVProperty::kTransactions: |
262 | 12 | return sem_context->Error(this, |
263 | 12 | Substitute("Invalid value for option '$0'. Value must be a map", |
264 | 12 | table_property_name).c_str(), |
265 | 12 | ErrorCode::DATATYPE_MISMATCH); |
266 | 20 | case KVProperty::kNumTablets: |
267 | 20 | RETURN_SEM_CONTEXT_ERROR_NOT_OK(GetIntValueFromExpr(rhs_, table_property_name, &int_val)); |
268 | 20 | if (int_val < 0) { |
269 | 0 | return sem_context->Error( |
270 | 0 | this, "Number of tablets cannot be less zero", ErrorCode::INVALID_ARGUMENTS); |
271 | 0 | } |
272 | 20 | if (int_val > FLAGS_max_num_tablets_for_table) { |
273 | 2 | return sem_context->Error( |
274 | 2 | this, "Number of tablets exceeds system limit", ErrorCode::INVALID_ARGUMENTS); |
275 | 2 | } |
276 | 18 | break; |
277 | 532 | } |
278 | | |
279 | 432 | PTAlterTable *alter_table = sem_context->current_alter_table(); |
280 | 432 | if (alter_table != nullptr) { |
281 | | // Some table properties are not supported in ALTER TABLE. |
282 | 8 | if (iterator->second == KVProperty::kNumTablets) { |
283 | 1 | return sem_context->Error(this, |
284 | 1 | "Changing the number of tablets is not supported yet", |
285 | 1 | ErrorCode::FEATURE_NOT_SUPPORTED); |
286 | 1 | } |
287 | | |
288 | 7 | RETURN_NOT_OK(alter_table->AppendAlterProperty(sem_context, this)); |
289 | 7 | } |
290 | 431 | return Status::OK(); |
291 | 432 | } |
292 | | |
293 | 0 | std::ostream& operator<<(ostream& os, const PropertyType& property_type) { |
294 | 0 | switch(property_type) { |
295 | 0 | case PropertyType::kTableProperty: |
296 | 0 | os << "kTableProperty"; |
297 | 0 | break; |
298 | 0 | case PropertyType::kClusteringOrder: |
299 | 0 | os << "kClusteringOrder"; |
300 | 0 | break; |
301 | 0 | case PropertyType::kTablePropertyMap: |
302 | 0 | os << "kTablePropertyMap"; |
303 | 0 | break; |
304 | 0 | case PropertyType::kCoPartitionTable: |
305 | 0 | os << "kCoPartitionTable"; |
306 | 0 | break; |
307 | 0 | } |
308 | 0 | return os; |
309 | 0 | } |
310 | | |
311 | 0 | void PTTableProperty::PrintSemanticAnalysisResult(SemContext *sem_context) { |
312 | 0 | VLOG(3) << "SEMANTIC ANALYSIS RESULT (" << *loc_ << "):\n" << "Not yet avail"; |
313 | 0 | } |
314 | | |
315 | 939 | CHECKED_STATUS PTTablePropertyListNode::Analyze(SemContext *sem_context) { |
316 | | // Set to ensure we don't have duplicate table properties. |
317 | 939 | std::set<string> table_properties; |
318 | 939 | std::unordered_map<string, PTTableProperty::SharedPtr> order_tnodes; |
319 | 939 | vector<string> order_columns; |
320 | 1.37k | for (PTTableProperty::SharedPtr tnode : node_list()) { |
321 | 1.37k | if (tnode == nullptr) { |
322 | | // This shouldn't happen because AppendList ignores null nodes. |
323 | 0 | LOG(ERROR) << "Invalid null property"; |
324 | 0 | continue; |
325 | 0 | } |
326 | 1.37k | switch(tnode->property_type()) { |
327 | 524 | case PropertyType::kTableProperty: FALLTHROUGH_INTENDED; |
328 | 1.04k | case PropertyType::kTablePropertyMap: { |
329 | 1.04k | string table_property_name = tnode->lhs()->c_str(); |
330 | 1.04k | if (table_properties.find(table_property_name) != table_properties.end()) { |
331 | 0 | return sem_context->Error(this, ErrorCode::DUPLICATE_TABLE_PROPERTY); |
332 | 0 | } |
333 | 1.04k | RETURN_NOT_OK(tnode->Analyze(sem_context)); |
334 | 895 | table_properties.insert(table_property_name); |
335 | 895 | break; |
336 | 1.04k | } |
337 | 0 | case PropertyType ::kCoPartitionTable: { |
338 | 0 | RETURN_NOT_OK(tnode->Analyze(sem_context)); |
339 | 0 | break; |
340 | 0 | } |
341 | 331 | case PropertyType::kClusteringOrder: { |
342 | 331 | const MCString column_name(tnode->name().c_str(), sem_context->PTempMem()); |
343 | 331 | const PTColumnDefinition *col = sem_context->GetColumnDefinition(column_name); |
344 | 331 | if (col == nullptr || !col->is_primary_key() || col->is_hash_key()) { |
345 | 0 | return sem_context->Error(tnode, "Not a clustering key column", |
346 | 0 | ErrorCode::INVALID_TABLE_PROPERTY); |
347 | 0 | } |
348 | | // Insert column_name only the first time we see it. |
349 | 331 | if (order_tnodes.find(column_name.c_str()) == order_tnodes.end()) { |
350 | 331 | order_columns.push_back(column_name.c_str()); |
351 | 331 | } |
352 | | // If a column ordering was set more than once, we use the last order provided. |
353 | 331 | order_tnodes[column_name.c_str()] = tnode; |
354 | 331 | break; |
355 | 331 | } |
356 | 1.37k | } |
357 | 1.37k | } |
358 | | |
359 | 793 | auto order_column_iter = order_columns.begin(); |
360 | 793 | for (auto &pc : sem_context->current_create_table_stmt()->primary_columns()) { |
361 | 602 | if (order_column_iter == order_columns.end()) { |
362 | 271 | break; |
363 | 271 | } |
364 | 331 | const auto &tnode = order_tnodes[*order_column_iter]; |
365 | 331 | if (strcmp(pc->yb_name(), order_column_iter->c_str()) != 0) { |
366 | 3 | string msg; |
367 | | // If we can find pc->yb_name() in the order-by list, it means the order of the columns is |
368 | | // incorrect. |
369 | 3 | if (order_tnodes.find(pc->yb_name()) != order_tnodes.end()) { |
370 | 0 | msg = Substitute("Columns in the CLUSTERING ORDER directive must be in same order as " |
371 | 0 | "the clustering key columns order ($0 must appear before $1)", |
372 | 0 | pc->yb_name(), *order_column_iter); |
373 | 3 | } else { |
374 | 3 | msg = Substitute("Missing CLUSTERING ORDER for column $0", pc->yb_name()); |
375 | 3 | } |
376 | 3 | return sem_context->Error(tnode, msg.c_str(), ErrorCode::INVALID_TABLE_PROPERTY); |
377 | 3 | } |
378 | 328 | if (tnode->direction() == PTOrderBy::Direction::kASC) { |
379 | 51 | pc->set_sorting_type(SortingType::kAscending); |
380 | 277 | } else if (tnode->direction() == PTOrderBy::Direction::kDESC) { |
381 | 277 | pc->set_sorting_type(SortingType::kDescending); |
382 | 277 | } |
383 | 328 | ++order_column_iter; |
384 | 328 | } |
385 | 790 | if (order_column_iter != order_columns.end()) { |
386 | 0 | const auto &tnode = order_tnodes[*order_column_iter]; |
387 | 0 | return sem_context->Error(tnode, |
388 | 0 | "Only clustering key columns can be defined in " |
389 | 0 | "CLUSTERING ORDER directive", ErrorCode::INVALID_TABLE_PROPERTY); |
390 | 0 | } |
391 | 790 | return Status::OK(); |
392 | 790 | } |
393 | | |
394 | 558 | Status PTTableProperty::SetTableProperty(yb::TableProperties *table_property) const { |
395 | | // TODO: Also reject properties that cannot be changed during alter table (like clustering order) |
396 | | |
397 | | // Clustering order not handled here. |
398 | 558 | if (property_type_ == PropertyType::kClusteringOrder) { |
399 | 234 | return Status::OK(); |
400 | 234 | } |
401 | | |
402 | 324 | if (property_type_ == PropertyType::kCoPartitionTable) { |
403 | 0 | table_property->SetCopartitionTableId(copartition_table_id()); |
404 | 0 | return Status::OK(); |
405 | 0 | } |
406 | | |
407 | 324 | string table_property_name; |
408 | 324 | ToLowerCase(lhs_->c_str(), &table_property_name); |
409 | 324 | auto iterator = kPropertyDataTypes.find(table_property_name); |
410 | 324 | if (iterator == kPropertyDataTypes.end()) { |
411 | 0 | return STATUS(InvalidArgument, Substitute("$0 is not a valid table property", lhs_->c_str())); |
412 | 0 | } |
413 | 324 | switch (iterator->second) { |
414 | 103 | case KVProperty::kDefaultTimeToLive: { |
415 | | // TTL value is entered by user in seconds, but we store internally in milliseconds. |
416 | 103 | int64_t val; |
417 | 103 | if (!GetIntValueFromExpr(rhs_, table_property_name, &val).ok()) { |
418 | 0 | return STATUS(InvalidArgument, Substitute("Invalid value for default_time_to_live")); |
419 | 0 | } |
420 | 103 | table_property->SetDefaultTimeToLive(val * MonoTime::kMillisecondsPerSecond); |
421 | 103 | break; |
422 | 103 | } |
423 | 22 | case KVProperty::kBloomFilterFpChance: FALLTHROUGH_INTENDED; |
424 | 40 | case KVProperty::kComment: FALLTHROUGH_INTENDED; |
425 | 62 | case KVProperty::kCrcCheckChance: FALLTHROUGH_INTENDED; |
426 | 84 | case KVProperty::kDclocalReadRepairChance: FALLTHROUGH_INTENDED; |
427 | 101 | case KVProperty::kGcGraceSeconds: FALLTHROUGH_INTENDED; |
428 | 102 | case KVProperty::kIndexInterval: FALLTHROUGH_INTENDED; |
429 | 118 | case KVProperty::kMemtableFlushPeriodInMs: FALLTHROUGH_INTENDED; |
430 | 135 | case KVProperty::kMinIndexInterval: FALLTHROUGH_INTENDED; |
431 | 152 | case KVProperty::kMaxIndexInterval: FALLTHROUGH_INTENDED; |
432 | 174 | case KVProperty::kReadRepairChance: FALLTHROUGH_INTENDED; |
433 | 197 | case KVProperty::kSpeculativeRetry: |
434 | 197 | LOG(WARNING) << "Ignoring table property " << table_property_name; |
435 | 197 | break; |
436 | 0 | case KVProperty::kCaching: FALLTHROUGH_INTENDED; |
437 | 0 | case KVProperty::kCompaction: FALLTHROUGH_INTENDED; |
438 | 0 | case KVProperty::kCompression: FALLTHROUGH_INTENDED; |
439 | 0 | case KVProperty::kTransactions: |
440 | 0 | LOG(ERROR) << "Not primitive table property " << table_property_name; |
441 | 0 | break; |
442 | 24 | case KVProperty::kNumTablets: |
443 | 24 | int64_t val; |
444 | 24 | auto status = GetIntValueFromExpr(rhs_, table_property_name, &val); |
445 | 24 | if (!status.ok()) { |
446 | 0 | return status.CloneAndAppend("Invalid value for tablets"); |
447 | 0 | } |
448 | 24 | table_property->SetNumTablets(trim_cast<int32_t>(val)); |
449 | 24 | break; |
450 | 324 | } |
451 | 324 | return Status::OK(); |
452 | 324 | } |
453 | | |
454 | 0 | TableId PTTableProperty::copartition_table_id() const { |
455 | 0 | DCHECK_EQ(property_type_, PropertyType::kCoPartitionTable); |
456 | 0 | return copartition_table_->id(); |
457 | 0 | } |
458 | | |
459 | | const std::map<string, PTTablePropertyMap::PropertyMapType> PTTablePropertyMap::kPropertyDataTypes |
460 | | = { |
461 | | {"caching", PTTablePropertyMap::PropertyMapType::kCaching}, |
462 | | {"compaction", PTTablePropertyMap::PropertyMapType::kCompaction}, |
463 | | {"compression", PTTablePropertyMap::PropertyMapType::kCompression}, |
464 | | {"transactions", PTTablePropertyMap::PropertyMapType::kTransactions} |
465 | | }; |
466 | | |
467 | | PTTablePropertyMap::PTTablePropertyMap(MemoryContext *memctx, |
468 | | YBLocationPtr loc) |
469 | 529 | : PTTableProperty(memctx, loc) { |
470 | 529 | property_type_ = PropertyType::kTablePropertyMap; |
471 | 529 | map_elements_ = TreeListNode<PTTableProperty>::MakeShared(memctx, loc); |
472 | 529 | } |
473 | | |
474 | 529 | PTTablePropertyMap::~PTTablePropertyMap() { |
475 | 529 | } |
476 | | |
477 | 517 | CHECKED_STATUS PTTablePropertyMap::Analyze(SemContext *sem_context) { |
478 | | // Verify we have a valid property name in the lhs. |
479 | 517 | const auto &property_name = lhs_->c_str(); |
480 | 517 | auto iterator = kPropertyDataTypes.find(property_name); |
481 | 517 | if (iterator == kPropertyDataTypes.end()) { |
482 | 15 | if (IsValidProperty(property_name)) { |
483 | 15 | return sem_context->Error(this, Substitute("Invalid map value for property '$0'", |
484 | 15 | property_name).c_str(), |
485 | 15 | ErrorCode::DATATYPE_MISMATCH); |
486 | 15 | } |
487 | 0 | return sem_context->Error(this, Substitute("Unknown property '$0'", property_name).c_str(), |
488 | 0 | ErrorCode::INVALID_TABLE_PROPERTY); |
489 | 15 | } |
490 | | |
491 | 502 | const auto &property_type = iterator->second; |
492 | | |
493 | 502 | switch (property_type) { |
494 | 42 | case PropertyMapType::kCaching: |
495 | 42 | RETURN_SEM_CONTEXT_ERROR_NOT_OK(AnalyzeCaching()); |
496 | 33 | break; |
497 | 60 | case PropertyMapType::kCompaction: |
498 | 60 | RETURN_SEM_CONTEXT_ERROR_NOT_OK(AnalyzeCompaction()); |
499 | 41 | break; |
500 | 41 | case PropertyMapType::kCompression: |
501 | 19 | RETURN_SEM_CONTEXT_ERROR_NOT_OK(AnalyzeCompression()); |
502 | 18 | break; |
503 | 381 | case PropertyMapType::kTransactions: |
504 | 381 | RETURN_SEM_CONTEXT_ERROR_NOT_OK(AnalyzeTransactions(sem_context)); |
505 | 379 | break; |
506 | 502 | } |
507 | 471 | return Status::OK(); |
508 | 502 | } |
509 | | |
510 | 0 | void PTTablePropertyMap::PrintSemanticAnalysisResult(SemContext *sem_context) { |
511 | 0 | VLOG(3) << "SEMANTIC ANALYSIS RESULT (" << *loc_ << "):\n" << "Not yet avail"; |
512 | 0 | } |
513 | | |
514 | 590 | Status PTTablePropertyMap::SetTableProperty(yb::TableProperties *table_property) const { |
515 | 590 | string table_property_name; |
516 | 590 | ToLowerCase(lhs_->c_str(), &table_property_name); |
517 | 590 | auto iterator = kPropertyDataTypes.find(table_property_name); |
518 | 590 | if (iterator == kPropertyDataTypes.end()) { |
519 | 0 | return STATUS(InvalidArgument, Substitute("$0 is not a valid table property", lhs_->c_str())); |
520 | 0 | } |
521 | 590 | switch (iterator->second) { |
522 | 32 | case PropertyMapType::kCaching: FALLTHROUGH_INTENDED; |
523 | 72 | case PropertyMapType::kCompaction: FALLTHROUGH_INTENDED; |
524 | 89 | case PropertyMapType::kCompression: |
525 | 89 | LOG(WARNING) << "Ignoring table property " << table_property_name; |
526 | 89 | break; |
527 | 501 | case PropertyMapType::kTransactions: |
528 | 769 | for (const auto& subproperty : map_elements_->node_list()) { |
529 | 769 | string subproperty_name; |
530 | 769 | ToLowerCase(subproperty->lhs()->c_str(), &subproperty_name); |
531 | 769 | auto iter = Transactions::kSubpropertyDataTypes.find(subproperty_name); |
532 | 769 | DCHECK(iter != Transactions::kSubpropertyDataTypes.end()); |
533 | 769 | bool bool_val; |
534 | 769 | string str_val; |
535 | 769 | switch(iter->second) { |
536 | 489 | case Transactions::Subproperty::kEnabled: |
537 | 489 | RETURN_NOT_OK(GetBoolValueFromExpr(subproperty->rhs(), subproperty_name, &bool_val)); |
538 | 489 | table_property->SetTransactional(bool_val); |
539 | 489 | break; |
540 | 280 | case Transactions::Subproperty::kConsistencyLevel: |
541 | 280 | RETURN_NOT_OK( |
542 | 280 | GetStringValueFromExpr(subproperty->rhs(), true, subproperty_name, &str_val)); |
543 | 280 | if (str_val == Transactions::kConsistencyLevelUserEnforced) { |
544 | 280 | table_property->SetConsistencyLevel(YBConsistencyLevel::USER_ENFORCED); |
545 | 280 | } |
546 | 280 | break; |
547 | 769 | } |
548 | 769 | } |
549 | 501 | break; |
550 | 590 | } |
551 | 590 | return Status::OK(); |
552 | 590 | } |
553 | | |
554 | 79 | Status PTTablePropertyMap::AnalyzeCompaction() { |
555 | 79 | vector<string> invalid_subproperties; |
556 | 79 | vector<PTTableProperty::SharedPtr> subproperties; |
557 | 79 | auto class_subproperties_iter = Compaction::kClassSubproperties.end(); |
558 | 79 | string class_name; |
559 | 284 | for (PTTableProperty::SharedPtr tnode : map_elements_->node_list()) { |
560 | 284 | string subproperty_name; |
561 | 284 | ToLowerCase(tnode->lhs()->c_str(), &subproperty_name); |
562 | | // 'class' is a special compaction subproperty. It tell us which other subproperties are valid. |
563 | | // We intentionally don't check if class_name is non_empty. If several 'class' subproperties |
564 | | // have been set, we use the last one. |
565 | 284 | if (subproperty_name == "class") { |
566 | 75 | if (tnode->rhs()->ql_type_id() != DataType::STRING) { |
567 | 0 | return STATUS(InvalidArgument, |
568 | 0 | "Property value for property 'class' has and invalid data type"); |
569 | 0 | } |
570 | 75 | class_name = std::dynamic_pointer_cast<PTConstText>(tnode->rhs())->Eval()->c_str(); |
571 | 75 | if (class_name.find('.') == string::npos) { |
572 | 74 | if (!boost::starts_with(class_name, kCompactionClassPrefix)) { |
573 | 74 | LOG(INFO) << "Inserting prefix into class name"; |
574 | 74 | class_name.insert(0, kCompactionClassPrefix); |
575 | 74 | } |
576 | 74 | } |
577 | 75 | class_subproperties_iter = Compaction::kClassSubproperties.find(class_name); |
578 | 75 | if (class_subproperties_iter == Compaction::kClassSubproperties.end()) { |
579 | 2 | return STATUS(InvalidArgument, |
580 | 2 | Substitute("Unable to find compaction strategy class '$0'", class_name)); |
581 | 2 | } |
582 | 73 | continue; |
583 | 75 | } |
584 | 209 | auto iter = Compaction::kSubpropertyDataTypes.find(subproperty_name); |
585 | 209 | if (iter == Compaction::kSubpropertyDataTypes.end()) { |
586 | 2 | invalid_subproperties.push_back(subproperty_name); |
587 | 207 | } else { |
588 | | // iter->second is of type std::pair<CompactionSubproperty, DataType>. |
589 | 207 | subproperties.push_back(tnode); |
590 | 207 | } |
591 | 209 | } |
592 | 77 | LOG(INFO) << "AnalyzeCompaction 2"; |
593 | | |
594 | 77 | if (class_subproperties_iter == Compaction::kClassSubproperties.end()) { |
595 | 4 | LOG(INFO) << "AnalyzeCompaction 3"; |
596 | 4 | return STATUS(InvalidArgument, "Missing sub-option 'class' for the 'compaction' option"); |
597 | 4 | } |
598 | | |
599 | | // Verify that the types for subproperties values are valid. Also verify that elements in |
600 | | // subproperties are valid for the given class. |
601 | 73 | auto const& valid_subproperties_for_class = class_subproperties_iter->second; |
602 | 73 | long double bucket_high = 1.5, bucket_low = 0.5; |
603 | 73 | int64_t max_threshold = 32, min_threshold = 4; |
604 | 205 | for (const auto& subproperty : subproperties) { |
605 | 205 | string subproperty_name; |
606 | 205 | ToLowerCase(subproperty->lhs()->c_str(), &subproperty_name); |
607 | 205 | auto subproperty_enum = Compaction::kSubpropertyDataTypes.at(subproperty_name); |
608 | 205 | if (valid_subproperties_for_class.find(subproperty_enum) == |
609 | 205 | valid_subproperties_for_class.end()) { |
610 | 8 | invalid_subproperties.push_back(subproperty_name); |
611 | 8 | continue; |
612 | 8 | } |
613 | | // Even if we already know that we have a subproperty that is not valid for a given class, |
614 | | // Cassandra will first complain about invalid values for a valid subproperty. |
615 | 197 | int64_t int_val; |
616 | 197 | long double double_val; |
617 | 197 | bool bool_val; |
618 | 197 | string str_val; |
619 | 197 | switch(subproperty_enum) { |
620 | 16 | case Compaction::Subproperty::kBaseTimeSeconds: FALLTHROUGH_INTENDED; |
621 | 16 | case Compaction::Subproperty::kCompactionWindowSize: FALLTHROUGH_INTENDED; |
622 | 16 | case Compaction::Subproperty::kSstableSizeInMb: FALLTHROUGH_INTENDED; |
623 | 36 | case Compaction::Subproperty::kTombstoneCompactionInterval: |
624 | 36 | RETURN_NOT_OK(GetIntValueFromExpr(subproperty->rhs(), subproperty_name, &int_val)); |
625 | 36 | if (int_val <= 0) { |
626 | 2 | return STATUS(InvalidArgument, Substitute("$0 must be greater than 0, but was $1", |
627 | 2 | subproperty_name, std::to_string(int_val))); |
628 | 2 | } |
629 | 34 | break; |
630 | 34 | case Compaction::Subproperty::kBucketHigh: |
631 | 5 | RETURN_NOT_OK(GetDoubleValueFromExpr(subproperty->rhs(), subproperty_name, &bucket_high)); |
632 | 3 | break; |
633 | 3 | case Compaction::Subproperty::kBucketLow: |
634 | 2 | RETURN_NOT_OK(GetDoubleValueFromExpr(subproperty->rhs(), subproperty_name, &bucket_low)); |
635 | 2 | break; |
636 | 2 | case Compaction::Subproperty::kClass: |
637 | 0 | LOG(FATAL) << "Invalid subproperty"; |
638 | 0 | case Compaction::Subproperty::kCompactionWindowUnit: |
639 | 0 | RETURN_NOT_OK(GetStringValueFromExpr(subproperty->rhs(), true, subproperty_name, &str_val)); |
640 | 0 | if (Compaction::kWindowUnits.find(str_val) == |
641 | 0 | Compaction::kWindowUnits.end()) { |
642 | 0 | return STATUS(InvalidArgument, |
643 | 0 | Substitute("$0 is not valid for '$1'", str_val, subproperty_name)); |
644 | 0 | } |
645 | 0 | break; |
646 | 20 | case Compaction::Subproperty::kEnabled: FALLTHROUGH_INTENDED; |
647 | 24 | case Compaction::Subproperty::kLogAll: FALLTHROUGH_INTENDED; |
648 | 27 | case Compaction::Subproperty::kOnlyPurgeRepairedTombstones: FALLTHROUGH_INTENDED; |
649 | 47 | case Compaction::Subproperty::kUncheckedTombstoneCompaction: |
650 | 47 | RETURN_NOT_OK(GetBoolValueFromExpr(subproperty->rhs(), subproperty_name, &bool_val)); |
651 | 39 | break; |
652 | 39 | case Compaction::Subproperty::kMaxSstableAgeDays: |
653 | 18 | RETURN_NOT_OK(GetDoubleValueFromExpr(subproperty->rhs(), subproperty_name, &double_val)); |
654 | 18 | if (double_val < 0) { |
655 | 0 | return STATUS(InvalidArgument, Substitute("$0 must be non-negative, but was $1", |
656 | 0 | subproperty_name, std::to_string(double_val))); |
657 | 0 | } |
658 | 18 | break;; |
659 | 24 | case Compaction::Subproperty::kMaxThreshold: |
660 | 24 | RETURN_NOT_OK(GetIntValueFromExpr(subproperty->rhs(), subproperty_name, &max_threshold)); |
661 | 22 | break; |
662 | 22 | case Compaction::Subproperty::kMaxWindowSizeSeconds: 2 FALLTHROUGH_INTENDED2 ; |
663 | 3 | case Compaction::Subproperty::kMinSstableSize: |
664 | 3 | RETURN_NOT_OK(GetIntValueFromExpr(subproperty->rhs(), subproperty_name, &int_val)); |
665 | 3 | if (int_val < 0) { |
666 | 0 | return STATUS(InvalidArgument, Substitute("$0 must be non-negative, but was $1", |
667 | 0 | subproperty_name, std::to_string(int_val))); |
668 | 0 | } |
669 | 3 | break; |
670 | 24 | case Compaction::Subproperty::kMinThreshold: |
671 | 24 | RETURN_NOT_OK(GetIntValueFromExpr(subproperty->rhs(), subproperty_name, &min_threshold)); |
672 | 24 | if (min_threshold <= 0) { |
673 | 2 | return STATUS(InvalidArgument, |
674 | 2 | "Disabling compaction by setting compaction thresholds to 0 has been " |
675 | 2 | "removed, set the compaction option 'enabled' to false instead"); |
676 | 22 | } else if (min_threshold < 2) { |
677 | 0 | return STATUS(InvalidArgument, |
678 | 0 | Substitute("Min compaction threshold cannot be less than 2 (got $0)", |
679 | 0 | min_threshold)); |
680 | 0 | } |
681 | 22 | break; |
682 | 22 | case Compaction::Subproperty::kTimestampResolution: |
683 | 18 | RETURN_NOT_OK(GetStringValueFromExpr(subproperty->rhs(), true, subproperty_name, &str_val)); |
684 | 18 | if (Compaction::kTimestampResolutionUnits.find(str_val) == |
685 | 18 | Compaction::kTimestampResolutionUnits.end()) { |
686 | 0 | return STATUS(InvalidArgument, |
687 | 0 | Substitute("$0 is not valid for '$1'", str_val, subproperty_name)); |
688 | 0 | } |
689 | 18 | break; |
690 | 20 | case Compaction::Subproperty::kTombstoneThreshold: |
691 | 20 | RETURN_NOT_OK(GetDoubleValueFromExpr(subproperty->rhs(), subproperty_name, &double_val)); |
692 | 20 | if (double_val <= 0) { |
693 | 2 | return STATUS(InvalidArgument, Substitute("$0 must be greater than 0, but was $1", |
694 | 2 | subproperty_name, std::to_string(double_val))); |
695 | 2 | } |
696 | 18 | break; |
697 | 197 | } |
698 | 197 | } |
699 | | |
700 | 55 | if (bucket_high <= bucket_low) { |
701 | 2 | return STATUS(InvalidArgument, Substitute("'bucket_high' value ($0) is less than or equal to " |
702 | 2 | "'bucket_low' value ($1)", std::to_string(bucket_high), std::to_string(bucket_low))); |
703 | 2 | } |
704 | | |
705 | 53 | if (max_threshold <= min_threshold) { |
706 | 4 | return STATUS(InvalidArgument, Substitute("'max_threshold' value ($0) is less than or equal to " |
707 | 4 | "'min_threshold' value ($1)", std::to_string(max_threshold), |
708 | 4 | std::to_string(min_threshold))); |
709 | 4 | } |
710 | | |
711 | 49 | if (!invalid_subproperties.empty()) { |
712 | 8 | return STATUS_FORMAT(InvalidArgument, "Properties specified $0 are not understood by $1", |
713 | 8 | invalid_subproperties, class_name); |
714 | 8 | } |
715 | 41 | return Status::OK(); |
716 | 49 | } |
717 | | |
718 | 51 | Status PTTablePropertyMap::AnalyzeCaching() { |
719 | 51 | string str_val; |
720 | 51 | int64_t int_val; |
721 | 80 | for (const auto& subproperty : map_elements_->node_list()) { |
722 | 80 | string subproperty_name; |
723 | 80 | ToLowerCase(subproperty->lhs()->c_str(), &subproperty_name); |
724 | 80 | if (subproperty_name == common::kCachingKeys) { |
725 | | // First try to read the value as a string. |
726 | 43 | RETURN_NOT_OK(GetStringValueFromExpr(subproperty->rhs(), true, subproperty_name, &str_val)); |
727 | 43 | if (common::IsValidCachingKeysString(str_val)) { |
728 | 33 | continue; |
729 | 33 | } |
730 | 10 | return STATUS(InvalidArgument, Substitute("Invalid value for caching sub-option '$0': only " |
731 | 43 | "'$1' and '$2' are allowed", common::kCachingKeys, common::kCachingAll, |
732 | 43 | common::kCachingNone)); |
733 | 43 | } else if (37 subproperty_name == common::kCachingRowsPerPartition37 ) { |
734 | 35 | if (GetStringValueFromExpr(subproperty->rhs(), true, subproperty_name, &str_val).ok()) { |
735 | 32 | if (common::IsValidCachingRowsPerPartitionString(str_val)) { |
736 | 23 | continue; |
737 | 23 | } |
738 | 32 | } |
739 | 12 | if (GetIntValueFromExpr(subproperty->rhs(), subproperty_name, &int_val).ok()) { |
740 | 6 | if (common::IsValidCachingRowsPerPartitionInt(int_val)) { |
741 | 6 | continue; |
742 | 6 | } |
743 | 6 | } |
744 | 6 | return STATUS(InvalidArgument, Substitute("Invalid value for caching sub-option '$0': only " |
745 | 12 | "'$1', '$2' and integer values are allowed", common::kCachingRowsPerPartition, |
746 | 12 | common::kCachingAll, common::kCachingNone)); |
747 | 12 | } |
748 | 2 | return STATUS(InvalidArgument, Substitute("Invalid caching sub-options $0: only '$1' and " |
749 | 80 | "'$2' are allowed", subproperty_name, common::kCachingKeys, |
750 | 80 | common::kCachingRowsPerPartition)); |
751 | 80 | } |
752 | 33 | return Status::OK(); |
753 | 51 | } |
754 | | |
755 | 20 | Status PTTablePropertyMap::AnalyzeCompression() { |
756 | 20 | string class_name, sstable_compression; |
757 | 20 | bool has_sstable_compression = false; |
758 | | // 'class' and 'sstable_compression' are treated differently. |
759 | 55 | for (const auto& subproperty : map_elements_->node_list()) { |
760 | 55 | string subproperty_name; |
761 | 55 | ToLowerCase(subproperty->lhs()->c_str(), &subproperty_name); |
762 | 55 | if (subproperty_name == "class") { |
763 | 19 | RETURN_NOT_OK(GetStringValueFromExpr(subproperty->rhs(), true, subproperty_name, |
764 | 19 | &class_name)); |
765 | 19 | if (class_name.empty()) { |
766 | 0 | return STATUS(InvalidArgument, |
767 | 0 | "The 'class' option must not be empty. To disable compression use 'enabled' : false"); |
768 | 0 | } |
769 | 36 | } else if (subproperty_name == "sstable_compression") { |
770 | 3 | RETURN_NOT_OK(GetStringValueFromExpr(subproperty->rhs(), true, subproperty_name, |
771 | 3 | &sstable_compression)); |
772 | 3 | has_sstable_compression = true; |
773 | 3 | } |
774 | 55 | } |
775 | 20 | if (!class_name.empty() && has_sstable_compression19 ) { |
776 | 2 | return STATUS(InvalidArgument, "The 'sstable_compression' option must not be used if the " |
777 | 2 | "compression algorithm is already specified by the 'class' option"); |
778 | 2 | } |
779 | 18 | if (class_name.empty() && !has_sstable_compression1 ) { |
780 | 0 | return STATUS(InvalidArgument, "Missing sub-option 'class' for the 'compression' option"); |
781 | 0 | } |
782 | | |
783 | 51 | for (const auto& subproperty : map_elements_->node_list())18 { |
784 | 51 | string subproperty_name; |
785 | 51 | ToLowerCase(subproperty->lhs()->c_str(), &subproperty_name); |
786 | 51 | auto iter = Compression::kSubpropertyDataTypes.find(subproperty_name); |
787 | 51 | if (iter == Compression::kSubpropertyDataTypes.end()) { |
788 | 0 | return STATUS(InvalidArgument, Substitute("Unknown compression option $0", subproperty_name)); |
789 | 0 | } |
790 | | |
791 | 51 | int64_t int_val; |
792 | 51 | long double double_val; |
793 | 51 | bool bool_val; |
794 | 51 | switch(iter->second) { |
795 | 17 | case Compression::Subproperty::kChunkLengthKb: |
796 | 17 | RETURN_NOT_OK(GetIntValueFromExpr(subproperty->rhs(), subproperty_name, &int_val)); |
797 | 17 | if (int_val > std::numeric_limits<int32_t>::max() / 1024) { |
798 | 0 | return STATUS(InvalidArgument, Substitute("Value of $0 is too large ($1)", |
799 | 0 | subproperty_name, int_val)); |
800 | 0 | } |
801 | 17 | break; |
802 | 17 | case Compression::Subproperty::kClass: |
803 | 17 | break; |
804 | 0 | case Compression::Subproperty::kCrcCheckChance: |
805 | 0 | RETURN_NOT_OK(GetDoubleValueFromExpr(subproperty->rhs(), subproperty_name, &double_val)); |
806 | 0 | if (double_val < 0.0 || double_val > 1.0) { |
807 | 0 | return STATUS(InvalidArgument, Substitute("$0 should be between 0.0 and 1.0", |
808 | 0 | subproperty_name)); |
809 | 0 | } |
810 | 0 | break; |
811 | 16 | case Compression::Subproperty::kEnabled: |
812 | 16 | RETURN_NOT_OK(GetBoolValueFromExpr(subproperty->rhs(), subproperty_name, &bool_val)); |
813 | 16 | break; |
814 | 16 | case Compression::Subproperty::kSstableCompression: |
815 | 1 | break; |
816 | 51 | } |
817 | 51 | } |
818 | 18 | return Status::OK(); |
819 | 18 | } |
820 | | |
821 | 383 | Status PTTablePropertyMap::AnalyzeTransactions(SemContext *sem_context) { |
822 | 532 | for (const auto& subproperty : map_elements_->node_list()) { |
823 | 532 | string subproperty_name; |
824 | 532 | ToLowerCase(subproperty->lhs()->c_str(), &subproperty_name); |
825 | 532 | auto iter = Transactions::kSubpropertyDataTypes.find(subproperty_name); |
826 | 532 | if (iter == Transactions::kSubpropertyDataTypes.end()) { |
827 | 2 | return STATUS(InvalidArgument, Substitute("Unknown transactions option $0", |
828 | 2 | subproperty_name)); |
829 | 2 | } |
830 | | |
831 | 530 | bool bool_val; |
832 | 530 | string str_val; |
833 | 530 | switch(iter->second) { |
834 | 369 | case Transactions::Subproperty::kEnabled: |
835 | 369 | RETURN_NOT_OK(GetBoolValueFromExpr(subproperty->rhs(), subproperty_name, &bool_val)); |
836 | 367 | break; |
837 | 367 | case Transactions::Subproperty::kConsistencyLevel: |
838 | 161 | if (sem_context->current_create_table_stmt()->opcode() != TreeNodeOpcode::kPTCreateIndex) { |
839 | 0 | return STATUS(InvalidArgument, |
840 | 0 | Substitute("Unknown property '$0'", subproperty_name).c_str()); |
841 | 0 | } |
842 | 161 | RETURN_NOT_OK(GetStringValueFromExpr(subproperty->rhs(), true, subproperty_name, &str_val)); |
843 | 161 | if (str_val != Transactions::kConsistencyLevelUserEnforced) { |
844 | 0 | return STATUS(InvalidArgument, |
845 | 0 | Substitute("Invalid value for property '$0'", subproperty_name).c_str()); |
846 | 0 | } |
847 | 161 | break; |
848 | 530 | } |
849 | 530 | } |
850 | 379 | return Status::OK(); |
851 | 383 | } |
852 | | |
853 | | const std::map<std::string, Compression::Subproperty> Compression::kSubpropertyDataTypes = { |
854 | | {"chunk_length_kb", Compression::Subproperty::kChunkLengthKb}, |
855 | | {"chunk_length_in_kb", Compression::Subproperty::kChunkLengthKb}, |
856 | | {"class", Compression::Subproperty::kClass}, |
857 | | {"crc_check_chance", Compression::Subproperty::kCrcCheckChance}, |
858 | | {"enabled", Compression::Subproperty::kEnabled}, |
859 | | {"sstable_compression", Compression::Subproperty::kSstableCompression} |
860 | | }; |
861 | | |
862 | | const std::map<std::string, Compaction::Subproperty> Compaction::kSubpropertyDataTypes = { |
863 | | {"base_time_seconds", Compaction::Subproperty::kBaseTimeSeconds}, |
864 | | {"bucket_high", Compaction::Subproperty::kBucketHigh}, |
865 | | {"bucket_low", Compaction::Subproperty::kBucketLow}, |
866 | | {"class", Compaction::Subproperty::kClass}, |
867 | | {"compaction_window_size", Compaction::Subproperty::kCompactionWindowSize}, |
868 | | {"compaction_window_unit", Compaction::Subproperty::kCompactionWindowUnit}, |
869 | | {"enabled", Compaction::Subproperty::kEnabled}, |
870 | | {"log_all", Compaction::Subproperty::kLogAll}, |
871 | | {"max_sstable_age_days", Compaction::Subproperty::kMaxSstableAgeDays}, |
872 | | {"max_threshold", Compaction::Subproperty::kMaxThreshold}, |
873 | | {"max_window_size_seconds", Compaction::Subproperty::kMaxWindowSizeSeconds}, |
874 | | {"min_sstable_size", Compaction::Subproperty::kMinSstableSize}, |
875 | | {"min_threshold", Compaction::Subproperty::kMinThreshold}, |
876 | | {"only_purge_repaired_tombstones", Compaction::Subproperty::kOnlyPurgeRepairedTombstones}, |
877 | | {"sstable_size_in_mb", Compaction::Subproperty::kSstableSizeInMb}, |
878 | | {"timestamp_resolution", Compaction::Subproperty::kTimestampResolution}, |
879 | | {"tombstone_compaction_interval", Compaction::Subproperty::kTombstoneCompactionInterval}, |
880 | | {"tombstone_threshold", Compaction::Subproperty::kTombstoneThreshold}, |
881 | | {"unchecked_tombstone_compaction", Compaction::Subproperty::kUncheckedTombstoneCompaction} |
882 | | }; |
883 | | |
884 | | const std::map<std::string, Transactions::Subproperty> |
885 | | Transactions::kSubpropertyDataTypes = { |
886 | | {"enabled", Transactions::Subproperty::kEnabled}, |
887 | | {"consistency_level", Transactions::Subproperty::kConsistencyLevel} |
888 | | }; |
889 | | |
890 | | const std::map<std::string, std::set<Compaction::Subproperty>> Compaction::kClassSubproperties = { |
891 | | {"org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy", |
892 | | { |
893 | | Compaction::Subproperty::kBucketHigh, |
894 | | Compaction::Subproperty::kBucketLow, |
895 | | Compaction::Subproperty::kEnabled, |
896 | | Compaction::Subproperty::kLogAll, |
897 | | Compaction::Subproperty::kMaxThreshold, |
898 | | Compaction::Subproperty::kMinThreshold, |
899 | | Compaction::Subproperty::kMinSstableSize, |
900 | | Compaction::Subproperty::kOnlyPurgeRepairedTombstones, |
901 | | Compaction::Subproperty::kTombstoneCompactionInterval, |
902 | | Compaction::Subproperty::kTombstoneThreshold, |
903 | | Compaction::Subproperty::kUncheckedTombstoneCompaction |
904 | | } |
905 | | }, |
906 | | {"org.apache.cassandra.db.compaction.DateTieredCompactionStrategy", |
907 | | { |
908 | | Compaction::Subproperty::kBaseTimeSeconds, |
909 | | Compaction::Subproperty::kEnabled, |
910 | | Compaction::Subproperty::kLogAll, |
911 | | Compaction::Subproperty::kMaxSstableAgeDays, |
912 | | Compaction::Subproperty::kMaxWindowSizeSeconds, |
913 | | Compaction::Subproperty::kMaxThreshold, |
914 | | Compaction::Subproperty::kMinThreshold, |
915 | | Compaction::Subproperty::kTimestampResolution, |
916 | | Compaction::Subproperty::kTombstoneCompactionInterval, |
917 | | Compaction::Subproperty::kTombstoneThreshold, |
918 | | Compaction::Subproperty::kUncheckedTombstoneCompaction |
919 | | } |
920 | | }, |
921 | | {"org.apache.cassandra.db.compaction.LeveledCompactionStrategy", |
922 | | { |
923 | | Compaction::Subproperty::kEnabled, |
924 | | Compaction::Subproperty::kLogAll, |
925 | | Compaction::Subproperty::kSstableSizeInMb, |
926 | | Compaction::Subproperty::kTombstoneCompactionInterval, |
927 | | Compaction::Subproperty::kTombstoneThreshold, |
928 | | Compaction::Subproperty::kUncheckedTombstoneCompaction |
929 | | } |
930 | | }, |
931 | | {"org.apache.cassandra.db.compaction.TimeWindowCompactionStrategy", |
932 | | { |
933 | | Compaction::Subproperty::kCompactionWindowUnit, |
934 | | Compaction::Subproperty::kCompactionWindowSize, |
935 | | Compaction::Subproperty::kLogAll |
936 | | } |
937 | | } |
938 | | }; |
939 | | |
940 | | std::set<std::string> Compaction::kWindowUnits = {"minutes", "hours", "days"}; |
941 | | std::set<std::string> Compaction::kTimestampResolutionUnits = { |
942 | | "days", "hours", "microseconds", "milliseconds", "minutes", "nanoseconds", "seconds" |
943 | | }; |
944 | | |
945 | | |
946 | | } // namespace ql |
947 | | } // namespace yb |