blob: e73642889a07023c40183983c2ff9decebbba4de [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 "format/result_set_format.h"
#include <algorithm>
#include <cstdio>
#include <ctime>
#include <iomanip>
#include <limits>
#include <random>
#include <sstream>
#include <utility>
#include <vector>
#include "utils/errno_define.h"
namespace tsfile_cli {
std::string cell_to_string(storage::ResultSet* rs, uint32_t i,
common::TSDataType type) {
std::ostringstream ss;
switch (type) {
case common::BOOLEAN:
return rs->get_value<bool>(i) ? "true" : "false";
case common::INT32:
ss << rs->get_value<int32_t>(i);
return ss.str();
case common::INT64:
case common::TIMESTAMP:
ss << rs->get_value<int64_t>(i);
return ss.str();
case common::FLOAT:
ss << std::setprecision(std::numeric_limits<float>::max_digits10)
<< rs->get_value<float>(i);
return ss.str();
case common::DOUBLE:
ss << std::setprecision(std::numeric_limits<double>::max_digits10)
<< rs->get_value<double>(i);
return ss.str();
case common::DATE: {
std::tm d = rs->get_value<std::tm>(i);
char buf[16];
std::snprintf(buf, sizeof(buf), "%04d-%02d-%02d", d.tm_year + 1900,
d.tm_mon + 1, d.tm_mday);
return buf;
}
case common::TEXT:
case common::STRING:
case common::BLOB: {
common::String* s = rs->get_value<common::String*>(i);
return s == nullptr ? std::string() : s->to_std_string();
}
default:
return "<UNKNOWN>";
}
}
int emit_result_set(storage::ResultSet* rs, OutputFormat fmt, bool no_header,
std::ostream& out, long long offset, long long limit) {
auto meta = rs->get_metadata();
const uint32_t ncol = meta->get_column_count();
std::vector<std::string> header;
std::vector<common::TSDataType> types;
header.reserve(ncol);
types.reserve(ncol);
for (uint32_t i = 1; i <= ncol; ++i) {
header.push_back(meta->get_column_name(i));
types.push_back(meta->get_column_type(i));
}
RowWriter writer(out, fmt, header, types, no_header);
bool has_next = false;
int code = common::E_OK;
long long skipped = 0;
long long emitted = 0;
while ((code = rs->next(has_next)) == common::E_OK && has_next) {
if (skipped < offset) {
++skipped;
continue;
}
if (limit >= 0 && emitted >= limit) {
break;
}
std::vector<std::string> cells(ncol);
std::vector<bool> nulls(ncol, false);
for (uint32_t i = 1; i <= ncol; ++i) {
if (rs->is_null(i)) {
nulls[i - 1] = true;
} else {
cells[i - 1] = cell_to_string(rs, i, types[i - 1]);
}
}
writer.write(cells, nulls);
++emitted;
}
writer.finish();
return code;
}
namespace {
struct BufferedRow {
std::vector<std::string> cells;
std::vector<bool> nulls;
};
BufferedRow read_current_row(storage::ResultSet* rs,
const std::vector<common::TSDataType>& types) {
BufferedRow row;
const uint32_t ncol = static_cast<uint32_t>(types.size());
row.cells.assign(ncol, "");
row.nulls.assign(ncol, false);
for (uint32_t i = 1; i <= ncol; ++i) {
if (rs->is_null(i)) {
row.nulls[i - 1] = true;
} else {
row.cells[i - 1] = cell_to_string(rs, i, types[i - 1]);
}
}
return row;
}
} // namespace
int emit_result_set_sampled(storage::ResultSet* rs, OutputFormat fmt,
bool no_header, std::ostream& out, long long limit,
unsigned long long seed) {
auto meta = rs->get_metadata();
const uint32_t ncol = meta->get_column_count();
std::vector<std::string> header;
std::vector<common::TSDataType> types;
header.reserve(ncol);
types.reserve(ncol);
for (uint32_t i = 1; i <= ncol; ++i) {
header.push_back(meta->get_column_name(i));
types.push_back(meta->get_column_type(i));
}
std::vector<BufferedRow> reservoir;
// Cap the pre-allocation: limit is user input and a huge -n would make
// reserve() throw std::length_error before any row is read. The vector
// still grows up to `limit` as rows actually arrive.
reservoir.reserve(static_cast<size_t>(std::min<long long>(limit, 4096)));
std::mt19937_64 rng(seed);
bool has_next = false;
int code = common::E_OK;
long long seen = 0;
while ((code = rs->next(has_next)) == common::E_OK && has_next) {
BufferedRow row = read_current_row(rs, types);
if (static_cast<long long>(reservoir.size()) < limit) {
reservoir.push_back(std::move(row));
} else {
std::uniform_int_distribution<long long> dist(0, seen);
long long idx = dist(rng);
if (idx < limit) {
reservoir[static_cast<size_t>(idx)] = std::move(row);
}
}
++seen;
}
RowWriter writer(out, fmt, header, types, no_header);
for (const BufferedRow& row : reservoir) {
writer.write(row.cells, row.nulls);
}
writer.finish();
return code;
}
} // namespace tsfile_cli