mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-26 01:22:04 +00:00
dbms: development [#CONV-2944].
This commit is contained in:
parent
c89ff86132
commit
bfb5eceea7
99
dbms/include/DB/Client/Connection.h
Normal file
99
dbms/include/DB/Client/Connection.h
Normal file
@ -0,0 +1,99 @@
|
||||
#include <Poco/Net/StreamSocket.h>
|
||||
|
||||
#include <DB/Core/Block.h>
|
||||
#include <DB/Core/Progress.h>
|
||||
#include <DB/Core/Protocol.h>
|
||||
#include <DB/Core/QueryProcessingStage.h>
|
||||
|
||||
#include <DB/IO/ReadBufferFromPocoSocket.h>
|
||||
#include <DB/IO/WriteBufferFromPocoSocket.h>
|
||||
|
||||
#include <DB/DataTypes/DataTypeFactory.h>
|
||||
|
||||
#include <DB/DataStreams/IBlockInputStream.h>
|
||||
#include <DB/DataStreams/IBlockOutputStream.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
using Poco::SharedPtr;
|
||||
|
||||
|
||||
/** Соединение с сервером БД для использования в клиенте.
|
||||
* Как использовать - см. Core/Protocol.h
|
||||
* (Реализацию на стороне сервера - см. Server/TCPHandler.h)
|
||||
*/
|
||||
class Connection
|
||||
{
|
||||
public:
|
||||
Connection(const String & host_, UInt16 port_,
|
||||
DataTypeFactory & data_type_factory_,
|
||||
Protocol::Compression::Enum compression_ = Protocol::Compression::Enable)
|
||||
: host(host_), port(port_), socket(), in(socket), out(socket),
|
||||
query_id(0), compression(compression_), data_type_factory(data_type_factory_)
|
||||
{
|
||||
connect();
|
||||
}
|
||||
|
||||
virtual ~Connection() {};
|
||||
|
||||
|
||||
/// Пакет, который может быть получен от сервера.
|
||||
struct Packet
|
||||
{
|
||||
UInt64 type;
|
||||
|
||||
Block block;
|
||||
SharedPtr<Exception> exception;
|
||||
Progress progress;
|
||||
|
||||
Packet() : type(Protocol::Server::Hello) {}
|
||||
};
|
||||
|
||||
|
||||
void sendQuery(const String & query, UInt64 query_id_ = 0, UInt64 stage = QueryProcessingStage::Complete);
|
||||
void sendCancel();
|
||||
void sendData(Block & block);
|
||||
|
||||
/// Проверить, если ли данные, которые можно прочитать.
|
||||
bool poll(size_t timeout_microseconds = 0);
|
||||
|
||||
/// Получить пакет от сервера.
|
||||
Packet receivePacket();
|
||||
|
||||
private:
|
||||
String host;
|
||||
UInt16 port;
|
||||
|
||||
Poco::Net::StreamSocket socket;
|
||||
ReadBufferFromPocoSocket in;
|
||||
WriteBufferFromPocoSocket out;
|
||||
|
||||
UInt64 query_id;
|
||||
UInt64 compression; /// Сжимать ли данные при взаимодействии с сервером.
|
||||
|
||||
DataTypeFactory & data_type_factory;
|
||||
|
||||
/// Откуда читать результат выполнения запроса.
|
||||
SharedPtr<ReadBuffer> chunked_in;
|
||||
SharedPtr<ReadBuffer> maybe_compressed_in;
|
||||
BlockInputStreamPtr block_in;
|
||||
|
||||
/// Куда писать данные INSERT-а.
|
||||
SharedPtr<WriteBuffer> chunked_out;
|
||||
SharedPtr<WriteBuffer> maybe_compressed_out;
|
||||
BlockOutputStreamPtr block_out;
|
||||
|
||||
void connect();
|
||||
void sendHello();
|
||||
void receiveHello();
|
||||
void forceConnected();
|
||||
bool ping();
|
||||
|
||||
Block receiveData();
|
||||
SharedPtr<Exception> receiveException();
|
||||
Progress receiveProgress();
|
||||
};
|
||||
|
||||
}
|
36
dbms/include/DB/Core/Progress.h
Normal file
36
dbms/include/DB/Core/Progress.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <DB/IO/ReadBuffer.h>
|
||||
#include <DB/IO/WriteBuffer.h>
|
||||
#include <DB/IO/ReadHelpers.h>
|
||||
#include <DB/IO/WriteHelpers.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
|
||||
/// Прогресс выполнения запроса
|
||||
struct Progress
|
||||
{
|
||||
size_t rows; /// Строк обработано.
|
||||
size_t bytes; /// Байт обработано.
|
||||
|
||||
Progress() : rows(0), bytes(0) {}
|
||||
Progress(size_t rows_, size_t bytes_) : rows(rows_), bytes(bytes_) {}
|
||||
|
||||
void read(ReadBuffer & in)
|
||||
{
|
||||
readBinary(rows, in);
|
||||
readBinary(bytes, in);
|
||||
}
|
||||
|
||||
void write(WriteBuffer & out)
|
||||
{
|
||||
writeBinary(rows, out);
|
||||
writeBinary(bytes, out);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
@ -4,6 +4,32 @@ namespace DB
|
||||
{
|
||||
|
||||
/** Протокол взаимодействия с сервером.
|
||||
*
|
||||
* Клиент соединяется с сервером и передаёт ему пакет Hello.
|
||||
* Если версия не устраивает, то сервер может разорвать соединение.
|
||||
* Сервер отвечает пакетом Hello.
|
||||
* Если версия не устраивает, то клиент может разорвать соединение.
|
||||
*
|
||||
* Далее в цикле.
|
||||
*
|
||||
* Клиент отправляет на сервер пакет Query.
|
||||
*
|
||||
* Если этот запрос требует передачи данных от клиента, то клиент отправляет один или несколько пакетов Data
|
||||
* с помощью ChunkedWriteBuffer.
|
||||
* Конец данных определается по отправленному пустому блоку.
|
||||
*
|
||||
* Далее, сервер передаёт набор пакетов одного из следующих видов:
|
||||
* - Data - данные результата выполнения запроса (один блок);
|
||||
* - Progress - прогресс выполнения запроса;
|
||||
* - Exception - ошибка;
|
||||
* - EndOfStream - конец передачи данных;
|
||||
*
|
||||
* Клиент должен читать пакеты до EndOfStream или Exception.
|
||||
* Также, клиент может передать на сервер пакет Cancel - отмена выполнения запроса.
|
||||
* В этом случае, сервер может прервать выполнение запроса и вернуть неполные данные;
|
||||
* но клиент всё равно должен читать все пакеты до EndOfStream.
|
||||
*
|
||||
* Между запросами, клиент может отправить Ping, и сервер должен ответить Pong.
|
||||
*/
|
||||
|
||||
namespace Protocol
|
||||
@ -13,12 +39,12 @@ namespace Protocol
|
||||
{
|
||||
enum Enum
|
||||
{
|
||||
Hello = 0, /// Имя, версия, ревизия.
|
||||
Data = 1, /// Идентификатор запроса, признак последнего чанка, размер чанка, часть данных со сжатием или без.
|
||||
Exception = 2, /// Исключение во время обработки запроса.
|
||||
Progress = 3, /// Прогресс выполнения запроса: строк считано, байт считано.
|
||||
Ok = 4, /// Запрос без возвращаемого результата успешно выполнен.
|
||||
Pong = 5, /// Ответ на Ping.
|
||||
Hello = 0, /// Имя, версия, ревизия.
|
||||
Data = 1, /// Идентификатор запроса, признак последнего чанка, размер чанка, часть данных со сжатием или без.
|
||||
Exception = 2, /// Исключение во время обработки запроса.
|
||||
Progress = 3, /// Прогресс выполнения запроса: строк считано, байт считано.
|
||||
Pong = 4, /// Ответ на Ping.
|
||||
EndOfStream = 5, /// Все пакеты были переданы.
|
||||
};
|
||||
}
|
||||
|
||||
@ -27,12 +53,14 @@ namespace Protocol
|
||||
{
|
||||
enum Enum
|
||||
{
|
||||
Query = 0, /** Идентификатор запроса, информация, до какой стадии исполнять запрос,
|
||||
Hello = 0, /// Имя, версия, ревизия.
|
||||
Query = 1, /** Идентификатор запроса, информация, до какой стадии исполнять запрос,
|
||||
* использовать ли сжатие, формат входных данных, формат выходных данных, текст запроса (без данных для INSERT-а).
|
||||
*/
|
||||
Data = 1, /// Идентификатор запроса, признак последнего чанка, размер чанка, часть данных со сжатием или без.
|
||||
Cancel = 2, /// Отменить выполнение запроса.
|
||||
Ping = 3, /// Проверка живости соединения с сервером.
|
||||
Data = 2, /// Идентификатор запроса, признак последнего чанка, размер чанка, часть данных со сжатием или без.
|
||||
Cancel = 3, /// Отменить выполнение запроса.
|
||||
Ping = 4, /// Проверка живости соединения с сервером.
|
||||
EndOfStream = 5, /// Все пакеты были переданы.
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -18,8 +18,6 @@
|
||||
#include <Poco/File.h>
|
||||
#include <Poco/SharedPtr.h>
|
||||
#include <Poco/Util/Application.h>
|
||||
#include <Poco/Net/StreamSocket.h>
|
||||
#include <Poco/Net/NetException.h>
|
||||
|
||||
#include <Yandex/Revision.h>
|
||||
|
||||
@ -27,15 +25,8 @@
|
||||
|
||||
#include <DB/Core/Exception.h>
|
||||
#include <DB/Core/Types.h>
|
||||
#include <DB/Core/Protocol.h>
|
||||
#include <DB/Core/QueryProcessingStage.h>
|
||||
|
||||
#include <DB/IO/ReadBufferFromPocoSocket.h>
|
||||
#include <DB/IO/WriteBufferFromPocoSocket.h>
|
||||
#include <DB/IO/CompressedReadBuffer.h>
|
||||
#include <DB/IO/CompressedWriteBuffer.h>
|
||||
#include <DB/IO/ChunkedReadBuffer.h>
|
||||
#include <DB/IO/ChunkedWriteBuffer.h>
|
||||
#include <DB/IO/ReadBufferFromFileDescriptor.h>
|
||||
#include <DB/IO/WriteBufferFromFileDescriptor.h>
|
||||
#include <DB/IO/ReadHelpers.h>
|
||||
@ -46,6 +37,8 @@
|
||||
|
||||
#include <DB/Interpreters/Context.h>
|
||||
|
||||
#include <DB/Client/Connection.h>
|
||||
|
||||
|
||||
/** Клиент командной строки СУБД ClickHouse.
|
||||
*/
|
||||
@ -109,7 +102,7 @@ private:
|
||||
class Client : public Poco::Util::Application
|
||||
{
|
||||
public:
|
||||
Client() : is_interactive(true), stdin_is_not_tty(false), socket(), in(socket), out(socket), query_id(0), compression(Protocol::Compression::Enable),
|
||||
Client() : is_interactive(true), stdin_is_not_tty(false), query_id(0),
|
||||
format_max_block_size(0), std_in(STDIN_FILENO), std_out(STDOUT_FILENO), received_rows(0),
|
||||
rows_read_on_server(0), bytes_read_on_server(0), written_progress_chars(0), written_first_block(false) {}
|
||||
|
||||
@ -120,32 +113,16 @@ private:
|
||||
bool is_interactive; /// Использовать readline интерфейс или batch режим.
|
||||
bool stdin_is_not_tty; /// stdin - не терминал.
|
||||
|
||||
Poco::Net::StreamSocket socket;
|
||||
ReadBufferFromPocoSocket in;
|
||||
WriteBufferFromPocoSocket out;
|
||||
SharedPtr<Connection> connection; /// Соединение с БД.
|
||||
String query; /// Текущий запрос.
|
||||
UInt64 query_id; /// Идентификатор запроса. Его можно использовать, чтобы отменить запрос.
|
||||
|
||||
UInt64 compression; /// Сжимать ли данные при взаимодействии с сервером.
|
||||
String in_format; /// Формат передачи данных (INSERT-а) на сервер.
|
||||
String out_format; /// Формат приёма данных (результата) от сервера.
|
||||
String format; /// Формат вывода результата в консоль.
|
||||
size_t format_max_block_size; /// Максимальный размер блока при выводе в консоль.
|
||||
String insert_format; /// Формат данных для INSERT-а при чтении их из stdin в batch режиме
|
||||
size_t insert_format_max_block_size; /// Максимальный размер блока при чтении данных INSERT-а.
|
||||
|
||||
Context context;
|
||||
Block empty_block;
|
||||
|
||||
/// Откуда читать результат выполнения запроса.
|
||||
SharedPtr<ReadBuffer> chunked_in;
|
||||
SharedPtr<ReadBuffer> maybe_compressed_in;
|
||||
BlockInputStreamPtr block_in;
|
||||
|
||||
/// Куда писать данные INSERT-а.
|
||||
SharedPtr<WriteBuffer> chunked_out;
|
||||
SharedPtr<WriteBuffer> maybe_compressed_out;
|
||||
BlockOutputStreamPtr block_out;
|
||||
|
||||
/// Чтение из stdin для batch режима
|
||||
ReadBufferFromFileDescriptor std_in;
|
||||
@ -256,17 +233,14 @@ private:
|
||||
<< "." << Revision::get()
|
||||
<< "." << std::endl;
|
||||
|
||||
compression = config().getBool("compression", true) ? Protocol::Compression::Enable : Protocol::Compression::Disable;
|
||||
in_format = config().getString("in_format", "Native");
|
||||
out_format = config().getString("out_format", "Native");
|
||||
format = config().getString("format", is_interactive ? "PrettyCompact" : "TabSeparated");
|
||||
format = config().getString("format", is_interactive ? "PrettyCompact" : "TabSeparated");
|
||||
format_max_block_size = config().getInt("format_max_block_size", DEFAULT_BLOCK_SIZE);
|
||||
|
||||
connect();
|
||||
|
||||
context.format_factory = new FormatFactory();
|
||||
context.data_type_factory = new DataTypeFactory();
|
||||
|
||||
connect();
|
||||
|
||||
if (is_interactive)
|
||||
{
|
||||
/// Отключаем tab completion.
|
||||
@ -298,34 +272,14 @@ private:
|
||||
{
|
||||
String host = config().getString("host", "localhost");
|
||||
UInt16 port = config().getInt("port", 9000);
|
||||
Protocol::Compression::Enum compression = config().getBool("compression", true)
|
||||
? Protocol::Compression::Enable
|
||||
: Protocol::Compression::Disable;
|
||||
|
||||
if (is_interactive)
|
||||
std::cout << "Connecting to " << host << ":" << port << "." << std::endl;
|
||||
|
||||
socket.connect(Poco::Net::SocketAddress(host, port));
|
||||
|
||||
/// Получить hello пакет.
|
||||
UInt64 packet_type = 0;
|
||||
String server_name;
|
||||
UInt64 server_version_major = 0;
|
||||
UInt64 server_version_minor = 0;
|
||||
UInt64 server_revision = 0;
|
||||
|
||||
readVarUInt(packet_type, in);
|
||||
if (packet_type != Protocol::Server::Hello)
|
||||
throw Exception("Unexpected packet from server", ErrorCodes::UNEXPECTED_PACKET_FROM_SERVER);
|
||||
|
||||
readStringBinary(server_name, in);
|
||||
readVarUInt(server_version_major, in);
|
||||
readVarUInt(server_version_minor, in);
|
||||
readVarUInt(server_revision, in);
|
||||
|
||||
if (is_interactive)
|
||||
std::cout << "Connected to " << server_name
|
||||
<< " server version " << server_version_major
|
||||
<< "." << server_version_minor
|
||||
<< "." << server_revision
|
||||
<< "." << std::endl << std::endl;
|
||||
connection = new Connection(host, port, *context.data_type_factory, compression);
|
||||
}
|
||||
|
||||
|
||||
@ -367,6 +321,9 @@ private:
|
||||
if (exit_strings.end() != exit_strings.find(line))
|
||||
return false;
|
||||
|
||||
block_std_in = NULL;
|
||||
block_std_out = NULL;
|
||||
|
||||
watch.restart();
|
||||
|
||||
query = line;
|
||||
@ -381,9 +338,7 @@ private:
|
||||
written_progress_chars = 0;
|
||||
written_first_block = false;
|
||||
|
||||
forceConnected();
|
||||
|
||||
sendQuery();
|
||||
connection->sendQuery(query, query_id, QueryProcessingStage::Complete);
|
||||
sendData();
|
||||
receiveResult();
|
||||
|
||||
@ -392,34 +347,10 @@ private:
|
||||
<< received_rows << " rows in set. Elapsed: " << watch.elapsedSeconds() << " sec."
|
||||
<< std::endl << std::endl;
|
||||
|
||||
block_in = NULL;
|
||||
maybe_compressed_in = NULL;
|
||||
chunked_in = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void forceConnected()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ping())
|
||||
{
|
||||
socket.close();
|
||||
connect();
|
||||
}
|
||||
}
|
||||
catch (const Poco::Net::NetException & e)
|
||||
{
|
||||
if (is_interactive)
|
||||
std::cout << e.displayText() << std::endl;
|
||||
|
||||
connect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool parseQuery()
|
||||
{
|
||||
ParserQuery parser;
|
||||
@ -454,48 +385,6 @@ private:
|
||||
}
|
||||
|
||||
|
||||
bool ping()
|
||||
{
|
||||
UInt64 pong = 0;
|
||||
writeVarUInt(Protocol::Client::Ping, out);
|
||||
out.next();
|
||||
|
||||
if (in.eof())
|
||||
return false;
|
||||
|
||||
readVarUInt(pong, in);
|
||||
|
||||
if (pong != Protocol::Server::Pong)
|
||||
throw Exception("Unknown packet from server (expected Pong)", ErrorCodes::UNKNOWN_PACKET_FROM_SERVER);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void sendQuery()
|
||||
{
|
||||
UInt64 stage = QueryProcessingStage::Complete;
|
||||
|
||||
writeVarUInt(Protocol::Client::Query, out);
|
||||
writeIntBinary(query_id, out);
|
||||
writeVarUInt(stage, out);
|
||||
writeVarUInt(compression, out);
|
||||
writeStringBinary(in_format, out);
|
||||
writeStringBinary(out_format, out);
|
||||
|
||||
writeStringBinary(query, out);
|
||||
|
||||
out.next();
|
||||
}
|
||||
|
||||
|
||||
void sendCancel()
|
||||
{
|
||||
writeVarUInt(Protocol::Client::Cancel, out);
|
||||
out.next();
|
||||
}
|
||||
|
||||
|
||||
void sendData()
|
||||
{
|
||||
/// Если нужно отправить данные INSERT-а.
|
||||
@ -521,48 +410,16 @@ private:
|
||||
|
||||
void sendDataFrom(ReadBuffer & buf)
|
||||
{
|
||||
/* if (!block_out)
|
||||
if (!block_std_in)
|
||||
{
|
||||
chunked_out = new ChunkedWriteBuffer(out, query_id);
|
||||
maybe_compressed_out = compression == Protocol::Compression::Enable
|
||||
? new CompressedWriteBuffer(*chunked_out)
|
||||
: chunked_out;
|
||||
|
||||
block_out = context.format_factory->getOutput(
|
||||
in_format,
|
||||
*maybe_compressed_out,
|
||||
empty_block,
|
||||
insert_format_max_block_size,
|
||||
*context.data_type_factory);
|
||||
// TODO
|
||||
}
|
||||
|
||||
/// Прочитать из сети один блок и вывести его в консоль
|
||||
Block block = block_in->read();
|
||||
if (block)
|
||||
{
|
||||
received_rows += block.rows();
|
||||
if (!block_std_out)
|
||||
{
|
||||
String current_format = format;
|
||||
|
||||
/// Формат может быть указан в SELECT запросе.
|
||||
if (ASTSelectQuery * select = dynamic_cast<ASTSelectQuery *>(&*parsed_query))
|
||||
if (select->format)
|
||||
if (ASTIdentifier * id = dynamic_cast<ASTIdentifier *>(&*select->format))
|
||||
current_format = id->name;
|
||||
|
||||
block_std_out = context.format_factory->getOutput(current_format, std_out, block);
|
||||
}
|
||||
|
||||
block_std_out->write(block);
|
||||
std_out.next();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;*/
|
||||
}
|
||||
|
||||
|
||||
/** Получает и обрабатывает пакеты из сервера.
|
||||
* Также следит, не требуется ли прервать выполнение запроса.
|
||||
*/
|
||||
void receiveResult()
|
||||
{
|
||||
InterruptListener interrupt_listener;
|
||||
@ -579,12 +436,12 @@ private:
|
||||
{
|
||||
if (interrupt_listener.check())
|
||||
{
|
||||
sendCancel();
|
||||
connection->sendCancel();
|
||||
cancelled = true;
|
||||
if (is_interactive)
|
||||
std::cout << "Cancelling query." << std::endl;
|
||||
}
|
||||
else if (!in.poll(1000000))
|
||||
else if (!connection->poll(1000000))
|
||||
continue; /// Если новых данных в ещё нет, то после таймаута продолжим проверять, не остановлено ли выполнение запроса.
|
||||
}
|
||||
|
||||
@ -594,57 +451,43 @@ private:
|
||||
|
||||
if (cancelled && is_interactive)
|
||||
std::cout << "Query was cancelled." << std::endl;
|
||||
|
||||
block_std_out = NULL;
|
||||
}
|
||||
|
||||
|
||||
/// Получить кусок результата или прогресс выполнения или эксепшен.
|
||||
/** Получить кусок результата или прогресс выполнения или эксепшен,
|
||||
* и обработать пакет соответствующим образом.
|
||||
* Возвращает true, если нужно продолжать чтение пакетов.
|
||||
*/
|
||||
bool receivePacket()
|
||||
{
|
||||
UInt64 packet_type = 0;
|
||||
readVarUInt(packet_type, in);
|
||||
Connection::Packet packet = connection->receivePacket();
|
||||
|
||||
switch (packet_type)
|
||||
switch (packet.type)
|
||||
{
|
||||
case Protocol::Server::Data:
|
||||
return receiveData();
|
||||
|
||||
case Protocol::Server::Exception:
|
||||
receiveException();
|
||||
return false;
|
||||
|
||||
case Protocol::Server::Ok:
|
||||
receiveOk();
|
||||
return false;
|
||||
onData(packet.block);
|
||||
return true;
|
||||
|
||||
case Protocol::Server::Progress:
|
||||
receiveProgress();
|
||||
onProgress(packet.progress);
|
||||
return true;
|
||||
|
||||
case Protocol::Server::Exception:
|
||||
onException(*packet.exception);
|
||||
return false;
|
||||
|
||||
case Protocol::Server::EndOfStream:
|
||||
onEndOfStream();
|
||||
return false;
|
||||
|
||||
default:
|
||||
throw Exception("Unknown packet from server", ErrorCodes::UNKNOWN_PACKET_FROM_SERVER);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool receiveData()
|
||||
void onData(Block & block)
|
||||
{
|
||||
if (!block_in)
|
||||
{
|
||||
chunked_in = new ChunkedReadBuffer(in, query_id);
|
||||
maybe_compressed_in = compression == Protocol::Compression::Enable
|
||||
? new CompressedReadBuffer(*chunked_in)
|
||||
: chunked_in;
|
||||
|
||||
block_in = context.format_factory->getInput(
|
||||
out_format,
|
||||
*maybe_compressed_in,
|
||||
empty_block,
|
||||
format_max_block_size,
|
||||
*context.data_type_factory);
|
||||
}
|
||||
|
||||
if (written_progress_chars)
|
||||
{
|
||||
if (written_first_block)
|
||||
@ -658,8 +501,6 @@ private:
|
||||
|
||||
written_first_block = true;
|
||||
|
||||
/// Прочитать из сети один блок и вывести его в консоль
|
||||
Block block = block_in->read();
|
||||
if (block)
|
||||
{
|
||||
received_rows += block.rows();
|
||||
@ -679,7 +520,6 @@ private:
|
||||
|
||||
block_std_out->write(block);
|
||||
std_out.next();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -687,37 +527,14 @@ private:
|
||||
block_std_out->writeSuffix();
|
||||
|
||||
std_out.next();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void receiveException()
|
||||
void onProgress(const Progress & progress)
|
||||
{
|
||||
Exception e;
|
||||
readException(e, in);
|
||||
|
||||
std::cerr << "Received exception from server:" << std::endl
|
||||
<< "Code: " << e.code() << ". " << e.displayText();
|
||||
}
|
||||
|
||||
|
||||
void receiveOk()
|
||||
{
|
||||
if (is_interactive)
|
||||
std::cout << "Ok." << std::endl;
|
||||
}
|
||||
|
||||
|
||||
void receiveProgress()
|
||||
{
|
||||
size_t rows = 0;
|
||||
size_t bytes = 0;
|
||||
readVarUInt(rows, in);
|
||||
readVarUInt(bytes, in);
|
||||
|
||||
rows_read_on_server += rows;
|
||||
bytes_read_on_server += bytes;
|
||||
rows_read_on_server += progress.rows;
|
||||
bytes_read_on_server += progress.bytes;
|
||||
|
||||
static size_t increment = 0;
|
||||
static const char * indicators[8] =
|
||||
@ -734,8 +551,6 @@ private:
|
||||
|
||||
if (is_interactive)
|
||||
{
|
||||
/*for (size_t i = 0; i < written_progress_chars; ++i)
|
||||
std::cerr << "\b \b";*/
|
||||
std::cerr << std::string(written_progress_chars, '\b');
|
||||
|
||||
std::stringstream message;
|
||||
@ -758,6 +573,20 @@ private:
|
||||
}
|
||||
|
||||
|
||||
void onException(const Exception & e)
|
||||
{
|
||||
std::cerr << "Received exception from server:" << std::endl
|
||||
<< "Code: " << e.code() << ". " << e.displayText();
|
||||
}
|
||||
|
||||
|
||||
void onEndOfStream()
|
||||
{
|
||||
if (is_interactive && !written_first_block)
|
||||
std::cout << "Ok." << std::endl;
|
||||
}
|
||||
|
||||
|
||||
void defineOptions(Poco::Util::OptionSet & options)
|
||||
{
|
||||
Poco::Util::Application::defineOptions(options);
|
||||
|
214
dbms/src/Client/Connection.cpp
Normal file
214
dbms/src/Client/Connection.cpp
Normal file
@ -0,0 +1,214 @@
|
||||
#include <Poco/Net/NetException.h>
|
||||
|
||||
#include <Yandex/Revision.h>
|
||||
|
||||
#include <DB/Core/Defines.h>
|
||||
|
||||
#include <DB/IO/CompressedReadBuffer.h>
|
||||
#include <DB/IO/CompressedWriteBuffer.h>
|
||||
#include <DB/IO/ChunkedReadBuffer.h>
|
||||
#include <DB/IO/ChunkedWriteBuffer.h>
|
||||
#include <DB/IO/ReadHelpers.h>
|
||||
#include <DB/IO/WriteHelpers.h>
|
||||
|
||||
#include <DB/DataStreams/NativeBlockInputStream.h>
|
||||
#include <DB/DataStreams/NativeBlockOutputStream.h>
|
||||
|
||||
#include <DB/Client/Connection.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
void Connection::connect()
|
||||
{
|
||||
socket.connect(Poco::Net::SocketAddress(host, port));
|
||||
|
||||
sendHello();
|
||||
receiveHello();
|
||||
}
|
||||
|
||||
|
||||
void Connection::sendHello()
|
||||
{
|
||||
writeVarUInt(Protocol::Client::Hello, out);
|
||||
writeStringBinary(String(DBMS_NAME) + " client", out);
|
||||
writeVarUInt(DBMS_VERSION_MAJOR, out);
|
||||
writeVarUInt(DBMS_VERSION_MINOR, out);
|
||||
writeVarUInt(Revision::get(), out);
|
||||
|
||||
out.next();
|
||||
}
|
||||
|
||||
|
||||
void Connection::receiveHello()
|
||||
{
|
||||
/// Получить hello пакет.
|
||||
UInt64 packet_type = 0;
|
||||
String server_name;
|
||||
UInt64 server_version_major = 0;
|
||||
UInt64 server_version_minor = 0;
|
||||
UInt64 server_revision = 0;
|
||||
|
||||
readVarUInt(packet_type, in);
|
||||
if (packet_type != Protocol::Server::Hello)
|
||||
throw Exception("Unexpected packet from server", ErrorCodes::UNEXPECTED_PACKET_FROM_SERVER);
|
||||
|
||||
readStringBinary(server_name, in);
|
||||
readVarUInt(server_version_major, in);
|
||||
readVarUInt(server_version_minor, in);
|
||||
readVarUInt(server_revision, in);
|
||||
}
|
||||
|
||||
|
||||
void Connection::forceConnected()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ping())
|
||||
{
|
||||
socket.close();
|
||||
connect();
|
||||
}
|
||||
}
|
||||
catch (const Poco::Net::NetException & e)
|
||||
{
|
||||
connect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Connection::ping()
|
||||
{
|
||||
UInt64 pong = 0;
|
||||
writeVarUInt(Protocol::Client::Ping, out);
|
||||
out.next();
|
||||
|
||||
if (in.eof())
|
||||
return false;
|
||||
|
||||
readVarUInt(pong, in);
|
||||
|
||||
if (pong != Protocol::Server::Pong)
|
||||
throw Exception("Unknown packet from server (expected Pong)", ErrorCodes::UNKNOWN_PACKET_FROM_SERVER);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Connection::sendQuery(const String & query, UInt64 query_id_, UInt64 stage)
|
||||
{
|
||||
forceConnected();
|
||||
|
||||
query_id = query_id_;
|
||||
|
||||
writeVarUInt(Protocol::Client::Query, out);
|
||||
writeIntBinary(query_id, out);
|
||||
writeVarUInt(stage, out);
|
||||
writeVarUInt(compression, out);
|
||||
writeStringBinary("Native", out);
|
||||
writeStringBinary("Native", out);
|
||||
|
||||
writeStringBinary(query, out);
|
||||
|
||||
out.next();
|
||||
|
||||
maybe_compressed_in = NULL;
|
||||
maybe_compressed_out = NULL;
|
||||
chunked_in = NULL;
|
||||
chunked_out = NULL;
|
||||
block_in = NULL;
|
||||
block_out = NULL;
|
||||
}
|
||||
|
||||
|
||||
void Connection::sendCancel()
|
||||
{
|
||||
writeVarUInt(Protocol::Client::Cancel, out);
|
||||
out.next();
|
||||
}
|
||||
|
||||
|
||||
void Connection::sendData(Block & block)
|
||||
{
|
||||
if (!block_out)
|
||||
{
|
||||
chunked_out = new ChunkedWriteBuffer(out, query_id);
|
||||
maybe_compressed_out = compression == Protocol::Compression::Enable
|
||||
? new CompressedWriteBuffer(*chunked_out)
|
||||
: chunked_out;
|
||||
|
||||
block_out = new NativeBlockOutputStream(*maybe_compressed_out);
|
||||
}
|
||||
|
||||
block_out->write(block);
|
||||
}
|
||||
|
||||
|
||||
bool Connection::poll(size_t timeout_microseconds)
|
||||
{
|
||||
return in.poll(timeout_microseconds);
|
||||
}
|
||||
|
||||
|
||||
Connection::Packet Connection::receivePacket()
|
||||
{
|
||||
Packet res;
|
||||
readVarUInt(res.type, in);
|
||||
|
||||
switch (res.type)
|
||||
{
|
||||
case Protocol::Server::Data:
|
||||
res.block = receiveData();
|
||||
return res;
|
||||
|
||||
case Protocol::Server::Exception:
|
||||
res.exception = receiveException();
|
||||
return res;
|
||||
|
||||
case Protocol::Server::Progress:
|
||||
res.progress = receiveProgress();
|
||||
return res;
|
||||
|
||||
case Protocol::Server::EndOfStream:
|
||||
return res;
|
||||
|
||||
default:
|
||||
throw Exception("Unknown packet from server", ErrorCodes::UNKNOWN_PACKET_FROM_SERVER);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Block Connection::receiveData()
|
||||
{
|
||||
if (!block_in)
|
||||
{
|
||||
chunked_in = new ChunkedReadBuffer(in, query_id);
|
||||
maybe_compressed_in = compression == Protocol::Compression::Enable
|
||||
? new CompressedReadBuffer(*chunked_in)
|
||||
: chunked_in;
|
||||
|
||||
block_in = new NativeBlockInputStream(*maybe_compressed_in, data_type_factory);
|
||||
}
|
||||
|
||||
/// Прочитать из сети один блок
|
||||
return block_in->read();
|
||||
}
|
||||
|
||||
|
||||
SharedPtr<Exception> Connection::receiveException()
|
||||
{
|
||||
Exception e;
|
||||
readException(e, in);
|
||||
return e.clone();
|
||||
}
|
||||
|
||||
|
||||
Progress Connection::receiveProgress()
|
||||
{
|
||||
Progress progress;
|
||||
progress.read(in);
|
||||
return progress;
|
||||
}
|
||||
|
||||
}
|
@ -28,7 +28,7 @@ void TCPHandler::runImpl()
|
||||
WriteBufferFromPocoSocket out(socket());
|
||||
WriteBufferFromPocoSocket out_for_chunks(socket());
|
||||
|
||||
/// Сразу после соединения, отправляем hello-пакет.
|
||||
receiveHello(in);
|
||||
sendHello(out);
|
||||
|
||||
while (!in.eof())
|
||||
@ -68,8 +68,8 @@ void TCPHandler::runImpl()
|
||||
while (sendData(out, out_for_chunks))
|
||||
;
|
||||
}
|
||||
else
|
||||
sendOk(out);
|
||||
|
||||
sendEndOfStream(out);
|
||||
}
|
||||
catch (DB::Exception & e)
|
||||
{
|
||||
@ -104,6 +104,32 @@ void TCPHandler::runImpl()
|
||||
}
|
||||
|
||||
|
||||
void TCPHandler::receiveHello(ReadBuffer & in)
|
||||
{
|
||||
/// Получить hello пакет.
|
||||
UInt64 packet_type = 0;
|
||||
String client_name;
|
||||
UInt64 client_version_major = 0;
|
||||
UInt64 client_version_minor = 0;
|
||||
UInt64 client_revision = 0;
|
||||
|
||||
readVarUInt(packet_type, in);
|
||||
if (packet_type != Protocol::Client::Hello)
|
||||
throw Exception("Unexpected packet from client", ErrorCodes::UNEXPECTED_PACKET_FROM_CLIENT);
|
||||
|
||||
readStringBinary(client_name, in);
|
||||
readVarUInt(client_version_major, in);
|
||||
readVarUInt(client_version_minor, in);
|
||||
readVarUInt(client_revision, in);
|
||||
|
||||
LOG_DEBUG(log, "Connected to " << client_name
|
||||
<< " client version " << client_version_major
|
||||
<< "." << client_version_minor
|
||||
<< "." << client_revision
|
||||
<< ".")
|
||||
}
|
||||
|
||||
|
||||
void TCPHandler::sendHello(WriteBuffer & out)
|
||||
{
|
||||
writeVarUInt(Protocol::Server::Hello, out);
|
||||
@ -287,11 +313,11 @@ void TCPHandler::sendException(WriteBuffer & out)
|
||||
out.next();
|
||||
}
|
||||
|
||||
void TCPHandler::sendOk(WriteBuffer & out)
|
||||
void TCPHandler::sendEndOfStream(WriteBuffer & out)
|
||||
{
|
||||
Poco::ScopedLock<Poco::FastMutex> lock(send_mutex);
|
||||
|
||||
writeVarUInt(Protocol::Server::Ok, out);
|
||||
writeVarUInt(Protocol::Server::EndOfStream, out);
|
||||
out.next();
|
||||
}
|
||||
|
||||
|
@ -112,8 +112,9 @@ private:
|
||||
bool sendData(WriteBuffer & out, WriteBuffer & out_for_chunks);
|
||||
void sendException(WriteBuffer & out);
|
||||
void sendProgress(WriteBuffer & out, size_t rows, size_t bytes);
|
||||
void sendOk(WriteBuffer & out);
|
||||
void sendEndOfStream(WriteBuffer & out);
|
||||
|
||||
void receiveHello(ReadBuffer & in);
|
||||
bool receivePacket(ReadBuffer & in, WriteBuffer & out);
|
||||
void receiveQuery(ReadBuffer & in);
|
||||
bool receiveData(ReadBuffer & in);
|
||||
|
Loading…
Reference in New Issue
Block a user