blob: 86773d7db7f58803dcb11fcb849eb636f8ec7956 [file] [log] [blame]
/**
* Copyright 2016, Quickstep Research Group, Computer Sciences Department,
* University of Wisconsin—Madison.
*
* Licensed 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 "cli/CommandExecutor.hpp"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <memory>
#include <string>
#include <vector>
#include "catalog/CatalogAttribute.hpp"
#include "catalog/CatalogDatabase.hpp"
#include "catalog/CatalogRelation.hpp"
#include "catalog/CatalogRelationSchema.hpp"
#include "cli/DropRelation.hpp"
#include "cli/PrintToScreen.hpp"
#include "parser/ParseStatement.hpp"
#include "parser/ParseString.hpp"
#include "parser/SqlParserWrapper.hpp"
#include "query_execution/Foreman.hpp"
#include "query_optimizer/QueryHandle.hpp"
#include "query_optimizer/QueryPlan.hpp"
#include "query_optimizer/QueryProcessor.hpp"
#include "storage/StorageBlock.hpp"
#include "storage/StorageBlockInfo.hpp"
#include "storage/StorageManager.hpp"
#include "storage/TupleIdSequence.hpp"
#include "storage/TupleStorageSubBlock.hpp"
#include "types/Type.hpp"
#include "types/TypeID.hpp"
#include "types/TypedValue.hpp"
#include "utility/PtrVector.hpp"
#include "utility/SqlError.hpp"
#include "glog/logging.h"
using std::fprintf;
using std::fputc;
using std::fputs;
using std::size_t;
using std::string;
using std::vector;
namespace quickstep {
namespace cli {
namespace {
namespace C = ::quickstep::cli;
void executeDescribeDatabase(
const PtrVector<ParseString> *arguments,
const CatalogDatabase &catalog_database,
StorageManager *storage_manager,
FILE *out) {
// Column width initialized to 6 to take into account the header name
// and the column value table
int max_column_width = C::kInitMaxColumnWidth;
vector<std::size_t> num_tuples;
vector<std::size_t> num_blocks;
const CatalogRelation *relation = nullptr;
if (arguments->size() == 0) {
for (const CatalogRelation &rel : catalog_database) {
max_column_width =
std::max(static_cast<int>(rel.getName().length()), max_column_width);
num_blocks.push_back(rel.size_blocks());
num_tuples.push_back(
PrintToScreen::GetNumTuplesInRelation(rel, storage_manager));
}
} else {
const ParseString &table_name = arguments->front();
const std::string &table_name_val = table_name.value();
relation = catalog_database.getRelationByName(table_name_val);
if (relation == nullptr) {
THROW_SQL_ERROR_AT(&(arguments->front())) << " Unrecognized relation " << table_name_val;
}
max_column_width = std::max(static_cast<int>(relation->getName().length()),
max_column_width);
num_blocks.push_back(relation->size_blocks());
num_tuples.push_back(PrintToScreen::GetNumTuplesInRelation(
*relation,
storage_manager));
}
// Only if we have relations work on the printing logic.
if (catalog_database.size() > 0) {
const std::size_t max_num_blocks = *std::max_element(num_blocks.begin(), num_blocks.end());
const std::size_t max_num_rows = *std::max_element(num_tuples.begin(), num_tuples.end());
const int max_num_rows_digits = std::max(PrintToScreen::GetNumberOfDigits(max_num_rows),
C::kInitMaxColumnWidth);
const int max_num_blocks_digits = std::max(PrintToScreen::GetNumberOfDigits(max_num_blocks),
C::kInitMaxColumnWidth+2);
vector<int> column_widths;
column_widths.push_back(max_column_width +1);
column_widths.push_back(C::kInitMaxColumnWidth + 1);
column_widths.push_back(max_num_blocks_digits + 1);
column_widths.push_back(max_num_rows_digits + 1);
fputs(" List of relations\n\n", out);
fprintf(out, "%-*s |", max_column_width+1, " Name");
fprintf(out, "%-*s |", C::kInitMaxColumnWidth, " Type");
fprintf(out, "%-*s |", max_num_blocks_digits, " Blocks");
fprintf(out, "%-*s\n", max_num_rows_digits, " Rows");
PrintToScreen::printHBar(column_widths, out);
// If there are no argument print the entire list of tables
// else print the particular table only.
vector<std::size_t>::const_iterator num_tuples_it = num_tuples.begin();
vector<std::size_t>::const_iterator num_blocks_it = num_blocks.begin();
if (arguments->size() == 0) {
for (const CatalogRelation &rel : catalog_database) {
fprintf(out, " %-*s |", max_column_width, rel.getName().c_str());
fprintf(out, " %-*s |", C::kInitMaxColumnWidth - 1, "table");
fprintf(out, " %-*lu |", max_num_blocks_digits - 1, *num_blocks_it);
fprintf(out, " %-*lu\n", max_num_rows_digits - 1, *num_tuples_it);
++num_tuples_it;
++num_blocks_it;
}
} else {
fprintf(out, " %-*s |", max_column_width, relation->getName().c_str());
fprintf(out, " %-*s |", C::kInitMaxColumnWidth -1, "table");
fprintf(out, " %-*lu |", max_num_blocks_digits - 1, *num_blocks_it);
fprintf(out, " %-*lu\n", max_num_rows_digits - 1, *num_tuples_it);
++num_tuples_it;
++num_blocks_it;
}
fputc('\n', out);
}
}
void executeDescribeTable(
const PtrVector<ParseString> *arguments,
const CatalogDatabase &catalog_database, FILE *out) {
const ParseString &table_name = arguments->front();
const std::string &table_name_val = table_name.value();
const CatalogRelation *relation =
catalog_database.getRelationByName(table_name_val);
if (relation == nullptr) {
THROW_SQL_ERROR_AT(&(arguments->front())) << " Unrecognized relation " << table_name_val;
}
vector<int> column_widths;
int max_attr_column_width = C::kInitMaxColumnWidth;
int max_type_column_width = C::kInitMaxColumnWidth;
for (const CatalogAttribute &attr : *relation) {
// Printed column needs to be wide enough to print:
// 1. The attribute name (in the printed "header").
// 2. Any value of the attribute's Type.
max_attr_column_width =
std::max(max_attr_column_width,
static_cast<int>(attr.getDisplayName().length()));
max_type_column_width =
std::max(max_type_column_width,
static_cast<int>(attr.getType().getName().length()));
}
// Add room for one extra character to allow spacing between the column ending and the vertical bar
column_widths.push_back(max_attr_column_width+1);
column_widths.push_back(max_type_column_width+1);
fprintf(out, "%*s \"%s\"\n", C::kInitMaxColumnWidth, "Table", table_name_val.c_str());
fprintf(out, "%-*s |", max_attr_column_width+1, " Column");
fprintf(out, "%-*s\n", max_type_column_width+1, " Type");
PrintToScreen::printHBar(column_widths, out);
for (const CatalogAttribute &attr : *relation) {
fprintf(out, " %-*s |", max_attr_column_width,
attr.getDisplayName().c_str());
fprintf(out, " %-*s\n", max_type_column_width,
attr.getType().getName().c_str());
}
// TODO(rogers): Add handlers for partitioning information.
if (relation->hasIndexScheme()) {
fprintf(out, "%*s\n", C::kInitMaxColumnWidth+2, " Indexes");
const quickstep::IndexScheme &index_scheme = relation->getIndexScheme();
for (auto index_it = index_scheme.begin(); index_it != index_scheme.end();
++index_it) {
fprintf(out, " \"%-*s\" %s", static_cast<int>(index_it->first.length()),
index_it->first.c_str(),
index_it->second.IndexSubBlockType_Name(
index_it->second.sub_block_type()).c_str());
fputc(' ', out);
fputc('(', out);
fprintf(out, "%s", relation->getAttributeById(index_it->second.indexed_attribute_ids(0))
->getDisplayName().c_str());
for (std::size_t i = 1; i < static_cast<std::size_t>(index_it->second.indexed_attribute_ids_size()); ++i) {
const char *attribute_display_name = relation->getAttributeById(
index_it->second.indexed_attribute_ids(i))
->getDisplayName().c_str();
fprintf(out, ", %s", attribute_display_name);
}
fputc(')', out);
fputc('\n', out);
}
}
}
/**
* @brief A helper function that executes a SQL query to obtain a scalar result.
*/
inline TypedValue executeQueryForSingleResult(const std::string &query_string,
StorageManager *storage_manager,
QueryProcessor *query_processor,
SqlParserWrapper *parser_wrapper,
Foreman *foreman) {
parser_wrapper->feedNextBuffer(new std::string(query_string));
ParseResult result = parser_wrapper->getNextStatement();
DCHECK(result.condition == ParseResult::kSuccess);
// Generate the query plan.
std::unique_ptr<QueryHandle> query_handle(
query_processor->generateQueryHandle(*result.parsed_statement));
DCHECK(query_handle->getQueryPlanMutable() != nullptr);
// Use foreman to execute the query plan.
foreman->setQueryPlan(query_handle->getQueryPlanMutable()->getQueryPlanDAGMutable());
foreman->reconstructQueryContextFromProto(query_handle->getQueryContextProto());
foreman->start();
foreman->join();
// Retrieve the scalar result from the result relation.
const CatalogRelation *query_result_relation = query_handle->getQueryResultRelation();
DCHECK(query_result_relation != nullptr);
TypedValue value;
{
std::vector<block_id> blocks = query_result_relation->getBlocksSnapshot();
DCHECK_EQ(1u, blocks.size());
BlockReference block = storage_manager->getBlock(blocks[0], *query_result_relation);
const TupleStorageSubBlock &tuple_store = block->getTupleStorageSubBlock();
DCHECK_EQ(1, tuple_store.numTuples());
DCHECK_EQ(1u, tuple_store.getRelation().size());
if (tuple_store.isPacked()) {
value = tuple_store.getAttributeValueTyped(0, 0);
} else {
std::unique_ptr<TupleIdSequence> existence_map(tuple_store.getExistenceMap());
value = tuple_store.getAttributeValueTyped(*existence_map->begin(), 0);
}
value.ensureNotReference();
}
// Drop the result relation.
DropRelation::Drop(*query_result_relation,
query_processor->getDefaultDatabase(),
query_processor->getStorageManager());
return value;
}
void executeAnalyze(QueryProcessor *query_processor,
Foreman *foreman,
FILE *out) {
const CatalogDatabase &database = *query_processor->getDefaultDatabase();
StorageManager *storage_manager = query_processor->getStorageManager();
std::unique_ptr<SqlParserWrapper> parser_wrapper(new SqlParserWrapper());
std::vector<std::reference_wrapper<const CatalogRelation>> relations(
database.begin(), database.end());
// Analyze each relation in the database.
for (const CatalogRelation &relation : relations) {
fprintf(out, "Analyzing %s ... ", relation.getName().c_str());
fflush(out);
CatalogRelation *mutable_relation =
query_processor->getDefaultDatabase()->getRelationByIdMutable(relation.getID());
// Get the number of distinct values for each column.
for (const CatalogAttribute &attribute : relation) {
std::string query_string = "SELECT COUNT(DISTINCT ";
query_string.append(attribute.getName());
query_string.append(") FROM ");
query_string.append(relation.getName());
query_string.append(";");
TypedValue num_distinct_values =
executeQueryForSingleResult(query_string,
storage_manager,
query_processor,
parser_wrapper.get(),
foreman);
DCHECK(num_distinct_values.getTypeID() == TypeID::kLong);
mutable_relation->getStatisticsMutable()->setNumDistinctValues(
attribute.getID(),
num_distinct_values.getLiteral<std::int64_t>());
}
// Get the number of tuples for the relation.
std::string query_string = "SELECT COUNT(*) FROM ";
query_string.append(relation.getName());
query_string.append(";");
TypedValue num_tuples =
executeQueryForSingleResult(query_string,
storage_manager,
query_processor,
parser_wrapper.get(),
foreman);
DCHECK(num_tuples.getTypeID() == TypeID::kLong);
mutable_relation->getStatisticsMutable()->setNumTuples(
num_tuples.getLiteral<std::int64_t>());
fprintf(out, "done\n");
fflush(out);
}
query_processor->markCatalogAltered();
query_processor->saveCatalog();
}
} // namespace
void executeCommand(const ParseStatement &statement,
const CatalogDatabase &catalog_database,
StorageManager *storage_manager,
QueryProcessor *query_processor,
Foreman *foreman,
FILE *out) {
const ParseCommand &command = static_cast<const ParseCommand &>(statement);
const PtrVector<ParseString> *arguments = command.arguments();
const std::string &command_str = command.command()->value();
if (command_str == C::kDescribeDatabaseCommand) {
executeDescribeDatabase(arguments, catalog_database, storage_manager, out);
} else if (command_str == C::kDescribeTableCommand) {
if (arguments->size() == 0) {
executeDescribeDatabase(arguments, catalog_database, storage_manager, out);
} else {
executeDescribeTable(arguments, catalog_database, out);
}
} else if (command_str == C::kAnalyzeCommand) {
executeAnalyze(query_processor, foreman, out);
} else {
THROW_SQL_ERROR_AT(command.command()) << "Invalid Command";
}
}
} // namespace cli
} // namespace quickstep