2021-04-14 17:51:55 +00:00
|
|
|
#include <Storages/StorageExecutable.h>
|
2021-08-25 19:30:22 +00:00
|
|
|
|
|
|
|
#include <filesystem>
|
|
|
|
|
|
|
|
#include <Common/ShellCommand.h>
|
|
|
|
#include <Core/Block.h>
|
2021-04-15 09:40:41 +00:00
|
|
|
#include <IO/ReadHelpers.h>
|
2021-08-25 19:30:22 +00:00
|
|
|
#include <Processors/Pipe.h>
|
2021-04-14 17:51:55 +00:00
|
|
|
#include <Interpreters/Context.h>
|
|
|
|
#include <Storages/StorageFactory.h>
|
2021-08-25 19:30:22 +00:00
|
|
|
#include <DataStreams/IBlockInputStream.h>
|
2021-08-24 19:38:42 +00:00
|
|
|
#include <DataStreams/ShellCommandSource.h>
|
2021-04-14 17:51:55 +00:00
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
2021-08-24 19:38:42 +00:00
|
|
|
|
2021-04-14 17:51:55 +00:00
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
2021-08-25 19:30:22 +00:00
|
|
|
extern const int UNSUPPORTED_METHOD;
|
2021-04-14 17:51:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
StorageExecutable::StorageExecutable(
|
2021-04-15 09:40:41 +00:00
|
|
|
const StorageID & table_id_,
|
2021-08-25 19:30:22 +00:00
|
|
|
const String & script_name_,
|
2021-04-15 21:15:54 +00:00
|
|
|
const String & format_,
|
2021-08-25 19:30:22 +00:00
|
|
|
const std::vector<BlockInputStreamPtr> & inputs_,
|
2021-04-14 17:51:55 +00:00
|
|
|
const ColumnsDescription & columns,
|
2021-08-24 19:38:42 +00:00
|
|
|
const ConstraintsDescription & constraints)
|
2021-04-15 09:40:41 +00:00
|
|
|
: IStorage(table_id_)
|
2021-08-25 19:30:22 +00:00
|
|
|
, script_name(script_name_)
|
2021-04-15 21:15:54 +00:00
|
|
|
, format(format_)
|
2021-08-25 19:30:22 +00:00
|
|
|
, inputs(inputs_)
|
2021-08-24 19:38:42 +00:00
|
|
|
, log(&Poco::Logger::get("StorageExecutable"))
|
2021-04-14 17:51:55 +00:00
|
|
|
{
|
|
|
|
StorageInMemoryMetadata storage_metadata;
|
|
|
|
storage_metadata.setColumns(columns);
|
|
|
|
storage_metadata.setConstraints(constraints);
|
|
|
|
setInMemoryMetadata(storage_metadata);
|
|
|
|
}
|
|
|
|
|
|
|
|
Pipe StorageExecutable::read(
|
|
|
|
const Names & /*column_names*/,
|
|
|
|
const StorageMetadataPtr & metadata_snapshot,
|
|
|
|
SelectQueryInfo & /*query_info*/,
|
2021-08-25 19:30:22 +00:00
|
|
|
ContextPtr context,
|
2021-04-14 17:51:55 +00:00
|
|
|
QueryProcessingStage::Enum /*processed_stage*/,
|
|
|
|
size_t max_block_size,
|
2021-08-25 19:30:22 +00:00
|
|
|
unsigned /*threads*/)
|
2021-04-14 17:51:55 +00:00
|
|
|
{
|
2021-08-25 19:30:22 +00:00
|
|
|
auto user_scripts_path = context->getUserScriptsPath();
|
|
|
|
auto script_path = user_scripts_path + '/' + script_name;
|
|
|
|
if (!std::filesystem::exists(std::filesystem::path(script_path)))
|
|
|
|
throw Exception(ErrorCodes::UNSUPPORTED_METHOD,
|
|
|
|
"Executable file {} does not exists inside {}",
|
|
|
|
script_name,
|
|
|
|
user_scripts_path);
|
|
|
|
|
2021-08-24 19:38:42 +00:00
|
|
|
auto sample_block = metadata_snapshot->getSampleBlock();
|
2021-08-25 19:30:22 +00:00
|
|
|
|
|
|
|
ShellCommand::Config config(script_path);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < inputs.size() - 1; ++i)
|
|
|
|
config.write_descriptors.emplace_back(i + 3);
|
|
|
|
|
|
|
|
auto process = ShellCommand::execute(config);
|
|
|
|
|
|
|
|
Pipe result;
|
|
|
|
if (inputs.empty())
|
|
|
|
{
|
|
|
|
Pipe pipe(FormatFactory::instance().getInput(format, process->out, std::move(sample_block), context, max_block_size));
|
|
|
|
pipe.addTransform(std::make_shared<ShellCommandOwningTransform>(pipe.getHeader(), log, std::move(process)));
|
|
|
|
|
|
|
|
result = std::move(pipe);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Pipe pipe(std::make_unique<ShellCommandSourceWithBackgroundThread>(context, format, std::move(sample_block), std::move(process), log,
|
|
|
|
[context, config, this](ShellCommand & command) mutable
|
|
|
|
{
|
|
|
|
std::vector<std::pair<BlockInputStreamPtr, BlockOutputStreamPtr>> input_output_streams;
|
|
|
|
|
|
|
|
size_t inputs_size = inputs.size();
|
|
|
|
input_output_streams.reserve(inputs_size);
|
|
|
|
|
|
|
|
auto & out = command.in;
|
|
|
|
auto & stdin_input_stream = inputs[0];
|
|
|
|
auto stdin_output_stream = context->getOutputStream(format, out, stdin_input_stream->getHeader().cloneEmpty());
|
|
|
|
input_output_streams.emplace_back(stdin_input_stream, stdin_output_stream);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < config.write_descriptors.size(); ++i)
|
|
|
|
{
|
|
|
|
auto write_descriptor = config.write_descriptors[i];
|
|
|
|
auto it = command.write_descriptors.find(write_descriptor);
|
|
|
|
if (it == command.write_descriptors.end())
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Process does not contain descriptor to write {}", write_descriptor);
|
|
|
|
|
|
|
|
auto input_stream = inputs[i];
|
|
|
|
auto output_stream = context->getOutputStream(format, it->second, input_stream->getHeader().cloneEmpty());
|
|
|
|
input_output_streams.emplace_back(input_stream, output_stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto & [input_stream, output_stream] : input_output_streams)
|
|
|
|
{
|
|
|
|
input_stream->readPrefix();
|
|
|
|
output_stream->writePrefix();
|
|
|
|
|
|
|
|
while (auto block = input_stream->read())
|
|
|
|
output_stream->write(block);
|
|
|
|
|
|
|
|
input_stream->readSuffix();
|
|
|
|
output_stream->writeSuffix();
|
|
|
|
|
|
|
|
output_stream->flush();
|
|
|
|
out.close();
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
|
|
|
|
result = std::move(pipe);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
2021-04-14 17:51:55 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|