forked from ClickHouse/ClickHouse
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathShellCommandSource.h
More file actions
184 lines (146 loc) · 5.37 KB
/
Copy pathShellCommandSource.h
File metadata and controls
184 lines (146 loc) · 5.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#pragma once
#include <memory>
#include <base/logger_useful.h>
#include <base/BorrowedObjectPool.h>
#include <Common/ShellCommand.h>
#include <Common/ThreadPool.h>
#include <IO/ReadHelpers.h>
#include <Processors/ISimpleTransform.h>
#include <Processors/Sources/SourceWithProgress.h>
#include <Processors/Formats/IInputFormat.h>
#include <QueryPipeline/QueryPipelineBuilder.h>
#include <Processors/Executors/PullingPipelineExecutor.h>
namespace DB
{
/** A stream, that get child process and sends data using tasks in background threads.
* For each send data task background thread is created. Send data task must send data to process input pipes.
* ShellCommandPoolSource receives data from process stdout.
*
* If process_pool is passed in constructor then after source is destroyed process is returned to pool.
*/
using ProcessPool = BorrowedObjectPool<std::unique_ptr<ShellCommand>>;
struct ShellCommandSourceConfiguration
{
/// Read fixed number of rows from command output
bool read_fixed_number_of_rows = false;
/// Valid only if read_fixed_number_of_rows = true
bool read_number_of_rows_from_process_output = false;
/// Valid only if read_fixed_number_of_rows = true
size_t number_of_rows_to_read = 0;
/// Max block size
size_t max_block_size = DBMS_DEFAULT_BUFFER_SIZE;
};
class ShellCommandSource final : public SourceWithProgress
{
public:
using SendDataTask = std::function<void(void)>;
ShellCommandSource(
ContextPtr context,
const std::string & format,
const Block & sample_block,
std::unique_ptr<ShellCommand> && command_,
std::vector<SendDataTask> && send_data_tasks = {},
const ShellCommandSourceConfiguration & configuration_ = {},
std::shared_ptr<ProcessPool> process_pool_ = nullptr)
: SourceWithProgress(sample_block)
, command(std::move(command_))
, configuration(configuration_)
, process_pool(process_pool_)
{
for (auto && send_data_task : send_data_tasks)
{
send_data_threads.emplace_back([task = std::move(send_data_task), this]()
{
try
{
task();
}
catch (...)
{
std::lock_guard<std::mutex> lock(send_data_lock);
exception_during_send_data = std::current_exception();
}
});
}
size_t max_block_size = configuration.max_block_size;
if (configuration.read_fixed_number_of_rows)
{
/** Currently parallel parsing input format cannot read exactly max_block_size rows from input,
* so it will be blocked on ReadBufferFromFileDescriptor because this file descriptor represent pipe that does not have eof.
*/
auto context_for_reading = Context::createCopy(context);
context_for_reading->setSetting("input_format_parallel_parsing", false);
context = context_for_reading;
if (configuration.read_number_of_rows_from_process_output)
{
readText(configuration.number_of_rows_to_read, command->out);
char dummy;
readChar(dummy, command->out);
}
max_block_size = configuration.number_of_rows_to_read;
}
pipeline = QueryPipeline(Pipe(context->getInputFormat(format, command->out, sample_block, max_block_size)));
executor = std::make_unique<PullingPipelineExecutor>(pipeline);
}
~ShellCommandSource() override
{
for (auto & thread : send_data_threads)
if (thread.joinable())
thread.join();
if (command && process_pool)
process_pool->returnObject(std::move(command));
}
protected:
Chunk generate() override
{
rethrowExceptionDuringSendDataIfNeeded();
if (configuration.read_fixed_number_of_rows && configuration.number_of_rows_to_read == current_read_rows)
return {};
Chunk chunk;
try
{
if (!executor->pull(chunk))
return {};
current_read_rows += chunk.getNumRows();
}
catch (...)
{
command = nullptr;
throw;
}
return chunk;
}
Status prepare() override
{
auto status = SourceWithProgress::prepare();
if (status == Status::Finished)
{
for (auto & thread : send_data_threads)
if (thread.joinable())
thread.join();
rethrowExceptionDuringSendDataIfNeeded();
}
return status;
}
String getName() const override { return "ShellCommandSource"; }
private:
void rethrowExceptionDuringSendDataIfNeeded()
{
std::lock_guard<std::mutex> lock(send_data_lock);
if (exception_during_send_data)
{
command = nullptr;
std::rethrow_exception(exception_during_send_data);
}
}
std::unique_ptr<ShellCommand> command;
ShellCommandSourceConfiguration configuration;
size_t current_read_rows = 0;
std::shared_ptr<ProcessPool> process_pool;
QueryPipeline pipeline;
std::unique_ptr<PullingPipelineExecutor> executor;
std::vector<ThreadFromGlobalPool> send_data_threads;
std::mutex send_data_lock;
std::exception_ptr exception_during_send_data;
};
}