YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/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