MySQLProtocol.h 18.0 KB
Newer Older
Y
draft  
Yuriy 已提交
1 2
#pragma once

Y
Yuriy 已提交
3
#include <Core/Types.h>
4
#include <IO/copyData.h>
Y
Yuriy 已提交
5
#include <IO/ReadBuffer.h>
Y
Yuriy 已提交
6
#include <IO/ReadBufferFromPocoSocket.h>
Y
Yuriy 已提交
7
#include <IO/WriteBuffer.h>
Y
Yuriy 已提交
8
#include <IO/WriteBufferFromPocoSocket.h>
9
#include <IO/WriteBufferFromString.h>
Y
Yuriy 已提交
10
#include <Poco/Logger.h>
Y
Yuriy 已提交
11
#include <Poco/Net/StreamSocket.h>
Y
Yuriy 已提交
12 13
#include <Poco/RandomStream.h>
#include <common/logger_useful.h>
Y
draft  
Yuriy 已提交
14 15 16 17 18
#include <random>
#include <sstream>

/// Implementation of MySQL wire protocol

19 20 21 22 23 24 25 26 27 28
namespace DB
{

namespace ErrorCodes
{
    extern const int UNKNOWN_PACKET_FROM_CLIENT;
}

namespace MySQLProtocol
{
Y
draft  
Yuriy 已提交
29 30 31 32 33

const size_t MAX_PACKET_LENGTH = (1 << 24) - 1; // 16 mb
const size_t SCRAMBLE_LENGTH = 20;
const size_t AUTH_PLUGIN_DATA_PART_1_LENGTH = 8;
const size_t MYSQL_ERRMSG_SIZE = 512;
Y
Yuriy 已提交
34 35
const size_t PACKET_HEADER_SIZE = 4;
const size_t SSL_REQUEST_PAYLOAD_SIZE = 32;
Y
draft  
Yuriy 已提交
36

37 38
namespace Authentication
{
Y
Yuriy 已提交
39
    const String SHA256 = "sha256_password"; /// Caching SHA2 plugin is not used because it would be possible to authenticate knowing hash from users.xml.
Y
draft  
Yuriy 已提交
40 41
}

42 43 44 45
enum CharacterSet
{
    utf8_general_ci = 33,
    binary = 63
Y
draft  
Yuriy 已提交
46 47
};

48 49
enum StatusFlags
{
Y
draft  
Yuriy 已提交
50 51 52
    SERVER_SESSION_STATE_CHANGED = 0x4000
};

53 54
enum Capability
{
Y
draft  
Yuriy 已提交
55 56
    CLIENT_CONNECT_WITH_DB = 0x00000008,
    CLIENT_PROTOCOL_41 = 0x00000200,
Y
Yuriy 已提交
57
    CLIENT_SSL = 0x00000800,
Y
draft  
Yuriy 已提交
58 59 60 61 62 63 64 65
    CLIENT_TRANSACTIONS = 0x00002000, // TODO
    CLIENT_SESSION_TRACK = 0x00800000, // TODO
    CLIENT_SECURE_CONNECTION = 0x00008000,
    CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA = 0x00200000,
    CLIENT_PLUGIN_AUTH = 0x00080000,
    CLIENT_DEPRECATE_EOF = 0x01000000,
};

66 67
enum Command
{
Y
draft  
Yuriy 已提交
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
    COM_SLEEP = 0x0,
    COM_QUIT = 0x1,
    COM_INIT_DB = 0x2,
    COM_QUERY = 0x3,
    COM_FIELD_LIST = 0x4,
    COM_CREATE_DB = 0x5,
    COM_DROP_DB = 0x6,
    COM_REFRESH = 0x7,
    COM_SHUTDOWN = 0x8,
    COM_STATISTICS = 0x9,
    COM_PROCESS_INFO = 0xa,
    COM_CONNECT = 0xb,
    COM_PROCESS_KILL = 0xc,
    COM_DEBUG = 0xd,
    COM_PING = 0xe,
    COM_TIME = 0xf,
    COM_DELAYED_INSERT = 0x10,
    COM_CHANGE_USER = 0x11,
    COM_RESET_CONNECTION = 0x1f,
    COM_DAEMON = 0x1d
};

90 91
enum ColumnType
{
Y
draft  
Yuriy 已提交
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
    MYSQL_TYPE_DECIMAL = 0x00,
    MYSQL_TYPE_TINY = 0x01,
    MYSQL_TYPE_SHORT = 0x02,
    MYSQL_TYPE_LONG = 0x03,
    MYSQL_TYPE_FLOAT = 0x04,
    MYSQL_TYPE_DOUBLE = 0x05,
    MYSQL_TYPE_NULL = 0x06,
    MYSQL_TYPE_TIMESTAMP = 0x07,
    MYSQL_TYPE_LONGLONG = 0x08,
    MYSQL_TYPE_INT24 = 0x09,
    MYSQL_TYPE_DATE = 0x0a,
    MYSQL_TYPE_TIME = 0x0b,
    MYSQL_TYPE_DATETIME = 0x0c,
    MYSQL_TYPE_YEAR = 0x0d,
    MYSQL_TYPE_VARCHAR = 0x0f,
    MYSQL_TYPE_BIT = 0x10,
    MYSQL_TYPE_NEWDECIMAL = 0xf6,
    MYSQL_TYPE_ENUM = 0xf7,
    MYSQL_TYPE_SET = 0xf8,
    MYSQL_TYPE_TINY_BLOB = 0xf9,
    MYSQL_TYPE_MEDIUM_BLOB = 0xfa,
    MYSQL_TYPE_LONG_BLOB = 0xfb,
    MYSQL_TYPE_BLOB = 0xfc,
    MYSQL_TYPE_VAR_STRING = 0xfd,
    MYSQL_TYPE_STRING = 0xfe,
    MYSQL_TYPE_GEOMETRY = 0xff
};


121 122 123 124 125 126 127 128 129 130 131
class ProtocolError : public DB::Exception
{
public:
    using Exception::Exception;
};


class WritePacket
{
public:
    virtual String getPayload() const = 0;
Y
draft  
Yuriy 已提交
132

133 134
    virtual ~WritePacket() = default;
};
Y
draft  
Yuriy 已提交
135 136


137 138
class ReadPacket
{
Y
draft  
Yuriy 已提交
139
public:
Y
Yuriy 已提交
140 141
    ReadPacket() = default;
    ReadPacket(const ReadPacket &) = default;
142 143 144 145 146 147 148 149 150 151 152 153
    virtual void readPayload(String payload) = 0;

    virtual ~ReadPacket() = default;
};


/* Writes and reads packets, keeping sequence-id.
 * Throws ProtocolError, if packet with incorrect sequence-id was received.
 */
class PacketSender
{
public:
154 155 156
    size_t & sequence_id;
    ReadBuffer * in;
    WriteBuffer * out;
Y
Yuriy 已提交
157
    size_t max_packet_size = MAX_PACKET_LENGTH;
158

159 160 161 162 163 164 165
    /// For reading and writing.
    PacketSender(ReadBuffer & in, WriteBuffer & out, size_t & sequence_id, const String logger_name)
        : sequence_id(sequence_id)
        , in(&in)
        , out(&out)
        , log(&Poco::Logger::get(logger_name))
    {
166
        log->setLevel("information");
167
    }
Y
Yuriy 已提交
168

169 170
    /// For writing.
    PacketSender(WriteBuffer & out, size_t & sequence_id, const String logger_name)
Y
Yuriy 已提交
171
        : sequence_id(sequence_id)
172 173 174
        , in(nullptr)
        , out(&out)
        , log(&Poco::Logger::get(logger_name))
175
    {
176
        log->setLevel("information");
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
    }

    String receivePacketPayload()
    {
        WriteBufferFromOwnString buf;

        size_t payload_length = 0;
        size_t packet_sequence_id = 0;

        // packets which are larger than or equal to 16MB are splitted
        do
        {
            LOG_TRACE(log, "Reading from buffer");

            in->readStrict(reinterpret_cast<char *>(&payload_length), 3);

Y
Yuriy 已提交
193
            if (payload_length > max_packet_size)
194 195
            {
                std::ostringstream tmp;
Y
Yuriy 已提交
196
                tmp << "Received packet with payload larger than max_packet_size: " << payload_length;
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
                throw ProtocolError(tmp.str(), ErrorCodes::UNKNOWN_PACKET_FROM_CLIENT);
            }

            in->readStrict(reinterpret_cast<char *>(&packet_sequence_id), 1);

            if (packet_sequence_id != sequence_id)
            {
                std::ostringstream tmp;
                tmp << "Received packet with wrong sequence-id: " << packet_sequence_id << ". Expected: " << sequence_id << '.';
                throw ProtocolError(tmp.str(), ErrorCodes::UNKNOWN_PACKET_FROM_CLIENT);
            }
            sequence_id++;

            LOG_TRACE(log, "Received packet. Sequence-id: " << packet_sequence_id << ", payload length: " << payload_length);

            copyData(*in, static_cast<WriteBuffer &>(buf), payload_length);
Y
Yuriy 已提交
213
        } while (payload_length == max_packet_size);
214 215 216 217

        return std::move(buf.str());
    }

Y
Yuriy 已提交
218
    void receivePacket(ReadPacket & packet)
219
    {
Y
Yuriy 已提交
220
        packet.readPayload(receivePacketPayload());
221 222 223 224 225 226 227 228 229 230
    }

    template<class T>
    void sendPacket(const T & packet, bool flush = false)
    {
        static_assert(std::is_base_of<WritePacket, T>());
        String payload = packet.getPayload();
        size_t pos = 0;
        do
        {
Y
Yuriy 已提交
231
            size_t payload_length = std::min(payload.length() - pos, max_packet_size);
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259

            LOG_TRACE(log, "Writing packet of size " << payload_length << " with sequence-id " << static_cast<int>(sequence_id));
            LOG_TRACE(log, packetToText(payload));

            out->write(reinterpret_cast<const char *>(&payload_length), 3);
            out->write(reinterpret_cast<const char *>(&sequence_id), 1);
            out->write(payload.data() + pos, payload_length);

            pos += payload_length;
            sequence_id++;
        } while (pos < payload.length());

        LOG_TRACE(log, "Packet was sent.");

        if (flush)
        {
            out->next();
        }
    }

    /// Sets sequence-id to 0. Must be called before each command phase.
    void resetSequenceId();

private:
    /// Converts packet to text. Is used for debug output.
    static String packetToText(String payload);

    Poco::Logger * log;
Y
draft  
Yuriy 已提交
260 261 262
};


263
uint64_t readLengthEncodedNumber(std::istringstream & ss);
Y
draft  
Yuriy 已提交
264

265
String writeLengthEncodedNumber(uint64_t x);
Y
draft  
Yuriy 已提交
266

267 268 269 270 271 272 273
void writeLengthEncodedString(String & payload, const String & s);

void writeNulTerminatedString(String & payload, const String & s);


class Handshake : public WritePacket
{
Y
draft  
Yuriy 已提交
274
    int protocol_version = 0xa;
275
    String server_version;
Y
draft  
Yuriy 已提交
276 277 278 279
    uint32_t connection_id;
    uint32_t capability_flags;
    uint8_t character_set;
    uint32_t status_flags;
280
    String auth_plugin_data;
Y
draft  
Yuriy 已提交
281
public:
Y
Yuriy 已提交
282
    explicit Handshake(uint32_t capability_flags, uint32_t connection_id, String server_version, String auth_plugin_data)
283 284 285
        : protocol_version(0xa)
        , server_version(std::move(server_version))
        , connection_id(connection_id)
Y
Yuriy 已提交
286
        , capability_flags(capability_flags)
287 288 289
        , character_set(CharacterSet::utf8_general_ci)
        , status_flags(0)
        , auth_plugin_data(auth_plugin_data)
290
    {
Y
draft  
Yuriy 已提交
291 292
    }

293 294 295
    String getPayload() const override
    {
        String result;
Y
draft  
Yuriy 已提交
296
        result.append(1, protocol_version);
297
        writeNulTerminatedString(result, server_version);
Y
draft  
Yuriy 已提交
298
        result.append(reinterpret_cast<const char *>(&connection_id), 4);
299
        writeNulTerminatedString(result, auth_plugin_data.substr(0, AUTH_PLUGIN_DATA_PART_1_LENGTH));
Y
draft  
Yuriy 已提交
300 301 302 303
        result.append(reinterpret_cast<const char *>(&capability_flags), 2);
        result.append(reinterpret_cast<const char *>(&character_set), 1);
        result.append(reinterpret_cast<const char *>(&status_flags), 2);
        result.append((reinterpret_cast<const char *>(&capability_flags)) + 2, 2);
304
        result.append(1, auth_plugin_data.size());
Y
draft  
Yuriy 已提交
305
        result.append(10, 0x0);
306
        result.append(auth_plugin_data.substr(AUTH_PLUGIN_DATA_PART_1_LENGTH, auth_plugin_data.size() - AUTH_PLUGIN_DATA_PART_1_LENGTH));
Y
Yuriy 已提交
307
        result.append(Authentication::SHA256);
Y
draft  
Yuriy 已提交
308 309 310 311 312
        result.append(1, 0x0);
        return result;
    }
};

Y
Yuriy 已提交
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
class SSLRequest : public ReadPacket
{
public:
    uint32_t capability_flags;
    uint32_t max_packet_size;
    uint8_t character_set;

    void readPayload(String s) override
    {
        std::istringstream ss(s);
        ss.readsome(reinterpret_cast<char *>(&capability_flags), 4);
        ss.readsome(reinterpret_cast<char *>(&max_packet_size), 4);
        ss.readsome(reinterpret_cast<char *>(&character_set), 1);
    }
};

329 330
class HandshakeResponse : public ReadPacket
{
Y
draft  
Yuriy 已提交
331 332 333 334
public:
    uint32_t capability_flags;
    uint32_t max_packet_size;
    uint8_t character_set;
335 336 337 338
    String username;
    String auth_response;
    String database;
    String auth_plugin_name;
Y
draft  
Yuriy 已提交
339

Y
Yuriy 已提交
340 341 342 343
    HandshakeResponse() = default;

    HandshakeResponse(const HandshakeResponse &) = default;

344 345
    void readPayload(String s) override
    {
Y
draft  
Yuriy 已提交
346 347 348 349 350 351 352 353 354
        std::istringstream ss(s);

        ss.readsome(reinterpret_cast<char *>(&capability_flags), 4);
        ss.readsome(reinterpret_cast<char *>(&max_packet_size), 4);
        ss.readsome(reinterpret_cast<char *>(&character_set), 1);
        ss.ignore(23);

        std::getline(ss, username, static_cast<char>(0x0));

355 356 357
        if (capability_flags & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA)
        {
            auto len = readLengthEncodedNumber(ss);
Y
draft  
Yuriy 已提交
358 359
            auth_response.resize(len);
            ss.read(auth_response.data(), static_cast<std::streamsize>(len));
360 361 362
        }
        else if (capability_flags & CLIENT_SECURE_CONNECTION)
        {
Y
draft  
Yuriy 已提交
363 364 365 366
            uint8_t len;
            ss.read(reinterpret_cast<char *>(&len), 1);
            auth_response.resize(len);
            ss.read(auth_response.data(), len);
367 368 369
        }
        else
        {
Y
draft  
Yuriy 已提交
370 371 372
            std::getline(ss, auth_response, static_cast<char>(0x0));
        }

373 374
        if (capability_flags & CLIENT_CONNECT_WITH_DB)
        {
Y
draft  
Yuriy 已提交
375 376 377
            std::getline(ss, database, static_cast<char>(0x0));
        }

378 379
        if (capability_flags & CLIENT_PLUGIN_AUTH)
        {
Y
draft  
Yuriy 已提交
380 381 382 383 384
            std::getline(ss, auth_plugin_name, static_cast<char>(0x0));
        }
    }
};

385 386 387 388 389 390 391 392 393 394 395 396 397 398
class AuthSwitchRequest : public WritePacket
{
    String plugin_name;
    String auth_plugin_data;
public:
    AuthSwitchRequest(String plugin_name, String auth_plugin_data)
        : plugin_name(std::move(plugin_name)), auth_plugin_data(std::move(auth_plugin_data))
    {
    }

    String getPayload() const override
    {
        String result;
        result.append(1, 0xfe);
Y
Yuriy 已提交
399
        writeNulTerminatedString(result, plugin_name);
400 401 402 403 404
        result.append(auth_plugin_data);
        return result;
    }
};

405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
class AuthSwitchResponse : public ReadPacket
{
public:
    String value;

    void readPayload(String s) override
    {
        value = std::move(s);
    }
};

class AuthMoreData : public WritePacket
{
    String data;
public:
    AuthMoreData(String data): data(std::move(data)) {}

    String getPayload() const override
    {
        String result;
        result.append(1, 0x01);
        result.append(data);
        return result;
    }
};

431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
/// Packet with a single null-terminated string. Is used for clear text authentication.
class NullTerminatedString : public ReadPacket
{
public:
    String value;

    void readPayload(String s) override
    {
        if (s.length() == 0 || s.back() != 0)
        {
            throw ProtocolError("String is not null terminated.", ErrorCodes::UNKNOWN_PACKET_FROM_CLIENT);
        }
        value = s;
        value.pop_back();
    }
};

class OK_Packet : public WritePacket
{
Y
draft  
Yuriy 已提交
450 451 452 453 454
    uint8_t header;
    uint32_t capabilities;
    uint64_t affected_rows;
    int16_t warnings = 0;
    uint32_t status_flags;
455
    String session_state_changes;
456
    String info;
Y
draft  
Yuriy 已提交
457
public:
458 459 460 461 462 463 464 465 466 467 468 469 470 471
    OK_Packet(uint8_t header,
        uint32_t capabilities,
        uint64_t affected_rows,
        uint32_t status_flags,
        int16_t warnings,
        String session_state_changes = "",
        String info = "")
        : header(header)
        , capabilities(capabilities)
        , affected_rows(affected_rows)
        , warnings(warnings)
        , status_flags(status_flags)
        , session_state_changes(std::move(session_state_changes))
        , info(info)
Y
draft  
Yuriy 已提交
472 473 474
    {
    }

475 476 477
    String getPayload() const override
    {
        String result;
Y
draft  
Yuriy 已提交
478
        result.append(1, header);
479
        result.append(writeLengthEncodedNumber(affected_rows));
480
        result.append(writeLengthEncodedNumber(0)); /// last insert-id
Y
draft  
Yuriy 已提交
481

482 483
        if (capabilities & CLIENT_PROTOCOL_41)
        {
Y
draft  
Yuriy 已提交
484 485
            result.append(reinterpret_cast<const char *>(&status_flags), 2);
            result.append(reinterpret_cast<const char *>(&warnings), 2);
486 487 488
        }
        else if (capabilities & CLIENT_TRANSACTIONS)
        {
Y
draft  
Yuriy 已提交
489 490 491
            result.append(reinterpret_cast<const char *>(&status_flags), 2);
        }

492 493 494
        if (capabilities & CLIENT_SESSION_TRACK)
        {
            result.append(writeLengthEncodedNumber(info.length()));
Y
draft  
Yuriy 已提交
495
            result.append(info);
496 497 498
            if (status_flags & SERVER_SESSION_STATE_CHANGED)
            {
                result.append(writeLengthEncodedNumber(session_state_changes.length()));
Y
draft  
Yuriy 已提交
499 500
                result.append(session_state_changes);
            }
501 502 503
        }
        else
        {
Y
draft  
Yuriy 已提交
504 505 506 507 508 509
            result.append(info);
        }
        return result;
    }
};

510 511
class EOF_Packet : public WritePacket
{
Y
draft  
Yuriy 已提交
512 513 514
    int warnings;
    int status_flags;
public:
515 516
    EOF_Packet(int warnings, int status_flags) : warnings(warnings), status_flags(status_flags)
    {}
Y
draft  
Yuriy 已提交
517

518 519 520
    String getPayload() const override
    {
        String result;
Y
draft  
Yuriy 已提交
521 522 523 524 525 526 527
        result.append(1, 0xfe); // EOF header
        result.append(reinterpret_cast<const char *>(&warnings), 2);
        result.append(reinterpret_cast<const char *>(&status_flags), 2);
        return result;
    }
};

528 529
class ERR_Packet : public WritePacket
{
Y
draft  
Yuriy 已提交
530
    int error_code;
531 532
    String sql_state;
    String error_message;
Y
draft  
Yuriy 已提交
533
public:
534 535
    ERR_Packet(int error_code, String sql_state, String error_message)
        : error_code(error_code), sql_state(std::move(sql_state)), error_message(std::move(error_message))
Y
draft  
Yuriy 已提交
536 537 538
    {
    }

539
    String getPayload() const override
Y
draft  
Yuriy 已提交
540
    {
541
        String result;
Y
draft  
Yuriy 已提交
542 543 544 545 546 547 548 549 550
        result.append(1, 0xff);
        result.append(reinterpret_cast<const char *>(&error_code), 2);
        result.append("#", 1);
        result.append(sql_state.data(), sql_state.length());
        result.append(error_message.data(), std::min(error_message.length(), MYSQL_ERRMSG_SIZE));
        return result;
    }
};

551 552 553 554 555 556 557
class ColumnDefinition : public WritePacket
{
    String schema;
    String table;
    String org_table;
    String name;
    String org_name;
Y
draft  
Yuriy 已提交
558 559 560 561 562 563 564
    size_t next_length = 0x0c;
    uint16_t character_set;
    uint32_t column_length;
    ColumnType column_type;
    uint16_t flags;
    uint8_t decimals = 0x00;
public:
565 566 567 568 569 570
    ColumnDefinition(
        String schema,
        String table,
        String org_table,
        String name,
        String org_name,
Y
draft  
Yuriy 已提交
571 572 573 574 575 576
        uint16_t character_set,
        uint32_t column_length,
        ColumnType column_type,
        uint16_t flags,
        uint8_t decimals)

577 578 579
        : schema(std::move(schema)), table(std::move(table)), org_table(std::move(org_table)), name(std::move(name)),
          org_name(std::move(org_name)), character_set(character_set), column_length(column_length), column_type(column_type), flags(flags),
          decimals(decimals)
Y
draft  
Yuriy 已提交
580 581 582
    {
    }

583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
    /// Should be used when column metadata (original name, table, original table, database) is unknown.
    ColumnDefinition(
        String name,
        uint16_t character_set,
        uint32_t column_length,
        ColumnType column_type,
        uint16_t flags,
        uint8_t decimals)
        : ColumnDefinition("", "", "", std::move(name), "", character_set, column_length, column_type, flags, decimals)
    {
    }

    String getPayload() const override
    {
        String result;
        writeLengthEncodedString(result, "def"); /// always "def"
        writeLengthEncodedString(result, ""); /// schema
        writeLengthEncodedString(result, ""); /// table
        writeLengthEncodedString(result, ""); /// org_table
        writeLengthEncodedString(result, name);
        writeLengthEncodedString(result, ""); /// org_name
        result.append(writeLengthEncodedNumber(next_length));
Y
draft  
Yuriy 已提交
605 606 607 608 609 610 611 612 613 614
        result.append(reinterpret_cast<const char *>(&character_set), 2);
        result.append(reinterpret_cast<const char *>(&column_length), 4);
        result.append(reinterpret_cast<const char *>(&column_type), 1);
        result.append(reinterpret_cast<const char *>(&flags), 2);
        result.append(reinterpret_cast<const char *>(&decimals), 2);
        result.append(2, 0x0);
        return result;
    }
};

615 616
class ComFieldList : public ReadPacket
{
Y
draft  
Yuriy 已提交
617
public:
618
    String table, field_wildcard;
Y
draft  
Yuriy 已提交
619

620
    void readPayload(String payload)
Y
draft  
Yuriy 已提交
621 622 623 624 625 626 627 628
    {
        std::istringstream ss(payload);
        ss.ignore(1); // command byte
        std::getline(ss, table, static_cast<char>(0x0));
        field_wildcard = payload.substr(table.length() + 2); // rest of the packet
    }
};

629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
class LengthEncodedNumber : public WritePacket
{
    uint64_t value;
public:
    LengthEncodedNumber(uint64_t value): value(value)
    {
    }

    String getPayload() const override
    {
        return writeLengthEncodedNumber(value);
    }
};

class ResultsetRow : public WritePacket
{
    std::vector<String> columns;
public:
    ResultsetRow()
    {
    }

    void appendColumn(String value)
    {
        columns.emplace_back(std::move(value));
    }

    String getPayload() const override
    {
        String result;
        for (const String & column : columns)
        {
            writeLengthEncodedString(result, column);
        }
        return result;
    }
};
Y
draft  
Yuriy 已提交
666 667 668

}
}