blob: ad623e85ca65e495ceaff87b69072702a61d6bd9 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* License); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <algorithm>
#include <map>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "cli/exit_codes.h"
#include "commands/commands.h"
#include "common/schema.h"
#include "reader/tsfile_reader.h"
#include "utils/storage_utils.h"
namespace tsfile_cli {
namespace {
const char* column_category_name(common::ColumnCategory category) {
switch (category) {
case common::ColumnCategory::TIME:
return "TIME";
case common::ColumnCategory::TAG:
return "TAG";
case common::ColumnCategory::ATTRIBUTE:
return "ATTRIBUTE";
case common::ColumnCategory::FIELD:
default:
return "FIELD";
}
}
struct SchemaRow {
std::vector<std::string> cells;
std::vector<bool> nulls;
};
bool table_measurement_selected(const ParsedArgs& args,
const std::string& name) {
if (args.measurements.empty()) {
return true;
}
const std::string folded_name = storage::to_lower(name);
for (const std::string& requested : args.measurements) {
if (storage::to_lower(requested) == folded_name) {
return true;
}
}
return false;
}
int collect_table_schema_rows(const ParsedArgs& args,
storage::TsFileReader& reader,
std::vector<SchemaRow>& rows, std::ostream& err) {
const std::string target_table_name = storage::to_lower(args.table);
auto schemas = reader.get_all_table_schemas();
bool matched_table = target_table_name.empty();
std::set<std::string> matched_measurements;
for (auto& schema : schemas) {
if (!schema) {
continue;
}
if (!target_table_name.empty() &&
schema->get_table_name() != target_table_name) {
continue;
}
matched_table = true;
auto categories = schema->get_column_categories();
auto measurements = schema->get_measurement_schemas();
for (size_t i = 0; i < measurements.size(); ++i) {
const auto& ms = measurements[i];
if (!ms) {
continue;
}
const std::string& name = ms->measurement_name_;
if (!table_measurement_selected(args, name)) {
continue;
}
matched_measurements.insert(storage::to_lower(name));
common::ColumnCategory category =
i < categories.size() ? categories[i]
: common::ColumnCategory::FIELD;
const bool physical_null =
category == common::ColumnCategory::TIME ||
category == common::ColumnCategory::ATTRIBUTE;
rows.push_back(
{{"table", schema->get_table_name(), name,
column_category_name(category),
tsdatatype_name(ms->data_type_),
physical_null ? "" : tsencoding_name(ms->encoding_),
physical_null ? "" : compression_name(ms->compression_type_)},
{false, false, false, false, false, physical_null,
physical_null}});
}
}
if (!matched_table) {
err << "Error: table '" << args.table << "' does not exist\n";
return kExitUsage;
}
for (const std::string& requested : args.measurements) {
if (matched_measurements.find(storage::to_lower(requested)) ==
matched_measurements.end()) {
err << "Error: column '" << requested << "' does not exist";
if (!args.table.empty()) {
err << " in table '" << target_table_name << "'";
}
err << "\n";
return kExitUsage;
}
}
return kExitOk;
}
} // namespace
int cmd_schema(const ParsedArgs& args, storage::TsFileReader& reader,
OutputFormat fmt, std::ostream& out, std::ostream& err) {
std::vector<SchemaRow> rows;
if (is_table_model(args, reader)) {
int ret = collect_table_schema_rows(args, reader, rows, err);
if (ret != kExitOk) {
return ret;
}
} else {
storage::DeviceTimeseriesMetadataMap meta =
reader.get_timeseries_metadata();
bool matched_device = args.device.empty();
std::set<std::string> matched_measurements;
for (auto& kv : meta) {
std::string target = kv.first ? kv.first->get_device_name() : "";
if (!args.device.empty() && target != args.device) {
continue;
}
matched_device = true;
std::map<std::string, std::pair<std::string, std::string>> enc_comp;
if (kv.first) {
std::vector<storage::MeasurementSchema> ms;
if (reader.get_timeseries_schema(kv.first, ms) == 0) {
for (auto& m : ms) {
enc_comp[m.measurement_name_] = std::make_pair(
tsencoding_name(m.encoding_),
compression_name(m.compression_type_));
}
}
}
for (auto& ts : kv.second) {
if (!ts) {
continue;
}
std::string m = ts->get_measurement_name().to_std_string();
if (!args.measurements.empty() &&
std::find(args.measurements.begin(),
args.measurements.end(),
m) == args.measurements.end()) {
continue;
}
matched_measurements.insert(m);
std::string enc;
std::string comp;
auto it = enc_comp.find(m);
if (it != enc_comp.end()) {
enc = it->second.first;
comp = it->second.second;
}
rows.push_back(
{{"tree", target, m, "FIELD",
tsdatatype_name(ts->get_data_type()), enc, comp},
{false, false, false, false, false, enc.empty(),
comp.empty()}});
}
}
if (!matched_device) {
err << "Error: device '" << args.device << "' does not exist\n";
return kExitUsage;
}
for (const std::string& requested : args.measurements) {
if (matched_measurements.find(requested) ==
matched_measurements.end()) {
err << "Error: measurement '" << requested
<< "' does not exist";
if (!args.device.empty()) {
err << " in device '" << args.device << "'";
}
err << "\n";
return kExitUsage;
}
}
}
RowWriter w(out, fmt,
{"model", "object", "column", "category", "data_type",
"encoding", "compression"},
{common::STRING, common::STRING, common::STRING, common::STRING,
common::STRING, common::STRING, common::STRING},
args.no_header);
for (const SchemaRow& row : rows) {
if (!w.write(row.cells, row.nulls)) {
err << "Error: failed to write output\n";
return kExitRuntime;
}
}
if (!w.finish()) {
err << "Error: failed to write output\n";
return kExitRuntime;
}
return kExitOk;
}
} // namespace tsfile_cli