# See the file "COPYING" in the main distribution directory for copyright. # # All information is from the MySQL internals documentation at: # # # Basic Types type uint24le = record { byte3: uint8; byte2: uint8; byte1: uint8; }; type LengthEncodedInteger = record { length : uint8; integer: LengthEncodedIntegerLookahead(length); }; type LengthEncodedIntegerArg(length: uint8) = record { integer: LengthEncodedIntegerLookahead(length); }; type LengthEncodedIntegerLookahead(length: uint8) = record { val: case length of { 0xfb -> i0 : empty; 0xfc -> i2 : uint16; 0xfd -> i3 : uint24le; 0xfe -> i4 : uint64; 0xff -> err_packet: empty; default -> one : empty; }; }; type LengthEncodedString = record { len: LengthEncodedInteger; val: bytestring &length=to_int()(len); }; type LengthEncodedStringArg(first_byte: uint8) = record { len: LengthEncodedIntegerArg(first_byte); val: bytestring &length=to_int()(len); }; %header{ class to_int { public: int operator()(uint24le* num) const { // Convert 24bit little endian int parsed as 3 uint8 into host endianness. return (num->byte1() << 16) | (num->byte2() << 8) | num->byte3(); } int operator()(LengthEncodedInteger* lei) const { if ( lei->length() < 0xfb ) return lei->length(); else if ( lei->length() == 0xfc ) return lei->integer()->i2(); else if ( lei->length() == 0xfd ) return to_int()(lei->integer()->i3()); else if ( lei->length() == 0xfe ) return lei->integer()->i4(); else return 0; } int operator()(LengthEncodedIntegerArg* lei) const { if ( lei->length() < 0xfb ) return lei->length(); else if ( lei->length() == 0xfc ) return lei->integer()->i2(); else if ( lei->length() == 0xfd ) return to_int()(lei->integer()->i3()); else if ( lei->length() == 0xfe ) return lei->integer()->i4(); else return 0; } int operator()(LengthEncodedIntegerLookahead* lei) const { if ( lei->length() < 0xfb ) return lei->length(); else if ( lei->length() == 0xfc ) return lei->i2(); else if ( lei->length() == 0xfd ) return to_int()(lei->i3()); else if ( lei->length() == 0xfe ) return lei->i4(); else return 0; } }; %} extern type to_int; # Enums enum command_consts { COM_SLEEP = 0x00, COM_QUIT = 0x01, COM_INIT_DB = 0x02, COM_QUERY = 0x03, COM_FIELD_LIST = 0x04, COM_CREATE_DB = 0x05, COM_DROP_DB = 0x06, COM_REFRESH = 0x07, COM_SHUTDOWN = 0x08, COM_STATISTICS = 0x09, COM_PROCESS_INFO = 0x0a, COM_CONNECT = 0x0b, COM_PROCESS_KILL = 0x0c, COM_DEBUG = 0x0d, COM_PING = 0x0e, COM_TIME = 0x0f, COM_DELAYED_INSERT = 0x10, COM_CHANGE_USER = 0x11, COM_BINLOG_DUMP = 0x12, COM_TABLE_DUMP = 0x13, COM_CONNECT_OUT = 0x14, COM_REGISTER_SLAVE = 0x15, COM_STMT_PREPARE = 0x16, COM_STMT_EXECUTE = 0x17, COM_STMT_SEND_LONG_DATA = 0x18, COM_STMT_CLOSE = 0x19, COM_STMT_RESET = 0x1a, COM_SET_OPTION = 0x1b, COM_STMT_FETCH = 0x1c, COM_DAEMON = 0x1d, COM_BINLOG_DUMP_GTID = 0x1e, COM_RESET_CONNECTION = 0x1f, }; enum state { CONNECTION_PHASE = 0, COMMAND_PHASE = 1, }; enum ConnectionExpected { EXPECT_HANDSHAKE, EXPECT_AUTH_DATA, }; enum Expected { NO_EXPECTATION, EXPECT_STATUS, EXPECT_COLUMN_DEFINITION, EXPECT_COLUMN_DEFINITION_OR_EOF, EXPECT_COLUMN_COUNT, EXPECT_EOF_THEN_RESULTSET, EXPECT_RESULTSET, EXPECT_REST_OF_PACKET, }; enum EOFType { EOF_INTERMEDIATE, # column definition to result row transition EOF_END, }; enum Client_Capabilities { CLIENT_CONNECT_WITH_DB = 0x00000008, CLIENT_SSL = 0x00000800, CLIENT_PLUGIN_AUTH = 0x00080000, CLIENT_CONNECT_ATTRS = 0x00100000, # Expects an OK (instead of EOF) after the resultset rows of a Text Resultset. CLIENT_DEPRECATE_EOF = 0x01000000, CLIENT_ZSTD_COMPRESSION_ALGORITHM = 0x04000000, CLIENT_QUERY_ATTRIBUTES = 0x08000000, }; # Binary Protocol Resultset encoding. # # https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_binary_resultset.html # # Values taken from here: https://dev.mysql.com/doc/dev/mysql-server/latest/namespaceclassic__protocol_1_1field__type.html enum field_types { TYPE_DECIMAL = 0x00, TYPE_TINY = 0x01, TYPE_SHORT = 0x02, TYPE_LONG = 0x03, TYPE_FLOAT = 0x04, TYPE_DOUBLE = 0x05, TYPE_NULL = 0x06, TYPE_TIMESTAMP = 0x07, TYPE_LONGLONG = 0x08, TYPE_INT24 = 0x09, TYPE_DATE = 0x0a, TYPE_TIME = 0x0b, TYPE_DATETIME = 0x0c, TYPE_YEAR = 0x0d, TYPE_VARCHAR = 0x0f, TYPE_BIT = 0x10, TYPE_TIMESTAMP2 = 0x11, TYPE_JSON = 0xf5, TYPE_NEWDECIMAL = 0xf6, TYPE_ENUM = 0xf7, TYPE_SET = 0xf8, TYPE_TINYBLOB = 0xf9, TYPE_MEDIUMBLOB = 0xfa, TYPE_LONGBLOB = 0xfb, TYPE_BLOB = 0xfc, TYPE_VARSTRING = 0xfd, TYPE_STRING = 0xfe, TYPE_GEOMETRY = 0xff, }; type Date = record { year : int16; month: int8; day : int8; }; type Time = record { hour : int8; minute: int8; second: int8; }; type BinaryDate = record { len: uint8 &enforce(len == 0 || len == 4 || len == 7 || len == 11); have_date: case ( len > 0 ) of { true -> date : Date; false -> none_1: empty; }; have_time: case ( len > 4 ) of { true -> time : Time; false -> none_2: empty; }; have_micros: case ( len > 7 ) of { true -> micros: int32; false -> none_3: empty; }; }; type DurationTime = record { is_negative: int8 &enforce(is_negative == 0 || is_negative == 1); days : int32; time : Time; }; type BinaryTime = record { len: uint8 &enforce(len == 0 || len == 8 || len == 12); have_time: case ( len > 0 ) of { true -> time : DurationTime; false -> none_1: empty; }; have_micros: case ( len > 8 ) of { true -> micros: int32; false -> none_2: empty; }; }; type BinaryValue(type: uint16) = record { value: case ( type ) of { TYPE_DECIMAL -> decimal_val: LengthEncodedInteger; TYPE_TINY -> tiny_val: int8; TYPE_SHORT -> short_val: int16; TYPE_LONG -> long_val: int32; TYPE_FLOAT -> float_val: bytestring &length=4; TYPE_DOUBLE -> double_val: bytestring &length=8; TYPE_NULL -> null_val: empty; # in null_bitmap TYPE_TIMESTAMP -> timestamp_val: BinaryDate; TYPE_LONGLONG -> longlong_val: int64; TYPE_INT24 -> int24_val: int32; TYPE_DATE -> date_val: BinaryDate; TYPE_TIME -> time_val: BinaryTime; TYPE_DATETIME -> datetime_val: BinaryDate; TYPE_YEAR -> year_val: int16; TYPE_VARCHAR -> varchar_val: LengthEncodedString; TYPE_BIT -> bit_val: LengthEncodedString; TYPE_TIMESTAMP2 -> timestamp2_val: BinaryDate; TYPE_JSON -> json_val: LengthEncodedString; TYPE_NEWDECIMAL -> newdecimal_val: LengthEncodedString; TYPE_ENUM -> enum_val: LengthEncodedString; TYPE_SET -> set_val: LengthEncodedString; TYPE_TINYBLOB -> tinyblob_val: LengthEncodedString; TYPE_MEDIUMBLOB -> mediumblob_val: LengthEncodedString; TYPE_LONGBLOB -> longblob_val: LengthEncodedString; TYPE_BLOB -> blob_val: LengthEncodedString; TYPE_VARSTRING -> varstring_val: LengthEncodedString; TYPE_STRING -> string_val: LengthEncodedString; TYPE_GEOMETRY -> geometry_val: LengthEncodedString; }; }; type NUL_String = RE/[^\0]*\0/; type EmptyOrNUL_String = RE/([^\0]*\0)?/; # MySQL PDU type MySQL_PDU(is_orig: bool) = record { hdr : Header; msg : case is_orig of { false -> server_msg: Server_Message(hdr.seq_id, hdr.len, state); true -> client_msg: Client_Message(hdr.len, state); } &requires(state); } &let { state: int = $context.connection.get_state(); } &length=hdr.len &byteorder=littleendian; type Header = record { le_len: uint24le; seq_id: uint8; } &let { len : uint32 = to_int()(le_len) + 4; } &length=4; type Server_Message(seq_id: uint8, pkt_len: uint32, state: int) = case state of { CONNECTION_PHASE -> connection_phase: Server_Connection_Phase(is_initial); COMMAND_PHASE -> command_response: Command_Response(pkt_len); } &requires(is_initial) &let { is_initial : bool = (seq_id == 0) && ($context.connection.get_previous_seq_id() != 255); update_seq_id : bool = $context.connection.set_previous_seq_id(seq_id); }; type Server_Connection_Phase(is_initial: bool) = case is_initial of { true -> initial_handshake: Initial_Handshake_Packet; false -> subsequent_handshake: Server_Connection_Phase_Packets; }; # Handshake Request type Initial_Handshake_Packet = record { version : uint8; pkt : case version of { 10 -> handshake10: Handshake_v10; 9 -> handshake9 : Handshake_v9; default -> error : ERR_Packet; }; } &let { set_version: bool = $context.connection.set_version(version); }; type Handshake_v10 = record { server_version : NUL_String; connection_id : uint32; auth_plugin_data_part_1: bytestring &length=8; filler_1 : uint8; capability_flag_1 : uint16; character_set : uint8; status_flags : uint16; capability_flags_2 : uint16; auth_plugin_data_len : uint8 &enforce( auth_plugin_data_len==0 || auth_plugin_data_len >= 21); reserved : padding[10]; auth_plugin_data_part_2: bytestring &length=auth_plugin_data_part_2_len; have_plugin : case ( ( capability_flags_2 << 16 ) & CLIENT_PLUGIN_AUTH ) of { CLIENT_PLUGIN_AUTH -> auth_plugin: NUL_String; 0x0 -> none : empty; }; } &let { # The length of auth_plugin_data_part_2 is at least 13 bytes, # or auth_plugin_data_len - 8 if that is larger, check for # auth_plugin_data_len > 21 (8 + 13) to prevent underflow for # when subtracting 8. # # https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase_packets_protocol_handshake_v10.html auth_plugin_data_part_2_len = auth_plugin_data_len > 21 ? auth_plugin_data_len - 8 : 13; server_query_attrs: bool = $context.connection.set_server_query_attrs(( capability_flags_2 << 16 ) & CLIENT_QUERY_ATTRIBUTES); }; type Handshake_v9 = record { server_version: NUL_String; connection_id : uint32; scramble : NUL_String; }; # While in the CONNECTION_PHASE, handle the following packets. Note that # this is subtly different from Command_Response_Status which interprets # 0xfe as EOF packet and also has does not support AuthMoreData. type Server_Connection_Phase_Packets = record { pkt_type: uint8; packet: case pkt_type of { 0x00 -> data_ok: OK_Packet; 0x01 -> auth_more_data: AuthMoreData(false); 0xfe -> auth_switch_request: AuthSwitchRequest; 0xff -> data_err: ERR_Packet; }; }; # Handshake Response type Handshake_Response_Packet = case $context.connection.get_version() of { 10 -> v10_response: Handshake_Response_Packet_v10; 9 -> v9_response : Handshake_Response_Packet_v9; } &let { version: uint8 = $context.connection.get_version(); }; type Handshake_Credentials_v10 = record { username : NUL_String; password : LengthEncodedString; }; type Connection_Attribute = record { name : LengthEncodedString; value : LengthEncodedString; }; type Handshake_Connection_Attributes = record { length : uint8; attrs : Connection_Attribute[] &until($input.length() == 0); } &length = length+1; type Handshake_Plain_v10(cap_flags: uint32) = record { credentials: Handshake_Credentials_v10; have_db : case ( cap_flags & CLIENT_CONNECT_WITH_DB ) of { CLIENT_CONNECT_WITH_DB -> database: NUL_String; 0x0 -> none_1 : empty; }; have_plugin : case ( cap_flags & CLIENT_PLUGIN_AUTH ) of { CLIENT_PLUGIN_AUTH -> auth_plugin: EmptyOrNUL_String; 0x0 -> none_2 : empty; }; have_attrs : case ( cap_flags & CLIENT_CONNECT_ATTRS ) of { CLIENT_CONNECT_ATTRS -> conn_attrs: Handshake_Connection_Attributes; 0x0 -> none_3 : empty; }; have_zstd : case ( cap_flags & CLIENT_ZSTD_COMPRESSION_ALGORITHM ) of { CLIENT_ZSTD_COMPRESSION_ALGORITHM -> zstd_compression_level: uint8; 0x0 -> none_4 : empty; }; } &let { # Switch client state into expecting more auth data. If the server responds # with an OK_Packet before, will switch into COMMAND_PHASE. update_conn_expectation: bool = $context.connection.set_next_conn_expected(EXPECT_AUTH_DATA) &if( cap_flags & CLIENT_PLUGIN_AUTH ); }; type Handshake_Response_Packet_v10 = record { cap_flags : uint32; max_pkt_size: uint32; char_set : uint8; pad : padding[23]; use_ssl : case ( cap_flags & CLIENT_SSL ) of { CLIENT_SSL -> none : empty; default -> plain: Handshake_Plain_v10(cap_flags); }; } &let { deprecate_eof: bool = $context.connection.set_deprecate_eof(cap_flags & CLIENT_DEPRECATE_EOF); client_query_attrs: bool = $context.connection.set_client_query_attrs(cap_flags & CLIENT_QUERY_ATTRIBUTES); proc_cap_flags: bool = $context.connection.set_client_capabilities(cap_flags); }; type Handshake_Response_Packet_v9 = record { cap_flags : uint16; max_pkt_size : uint24le; username : NUL_String; auth_response: NUL_String; have_db : case ( cap_flags & CLIENT_CONNECT_WITH_DB ) of { CLIENT_CONNECT_WITH_DB -> database: NUL_String; 0x0 -> none : empty; }; password : bytestring &restofdata; }; # Connection Phase type Client_Message(pkt_len: uint32, state: int) = case state of { CONNECTION_PHASE -> connection_phase: Connection_Phase_Packets; COMMAND_PHASE -> command_phase : Command_Request_Packet(pkt_len); }; type Connection_Phase_Packets = case $context.connection.get_conn_expectation() of { EXPECT_HANDSHAKE -> handshake_resp: Handshake_Response_Packet; EXPECT_AUTH_DATA -> auth_data: AuthMoreData(true); }; # Query attribute handling for COM_QUERY # type AttributeTypeAndName = record { type: uint8; unsigned_flag: uint8; name: LengthEncodedString; }; type AttributeValue(is_null: bool, type: uint8) = record { null: case is_null of { false -> val: BinaryValue(type); true -> null_val: empty; }; } &let { # Move parsing the next query attribute. done = $context.connection.next_query_attr(); }; type Attributes(count: int) = record { null_bitmap : bytestring &length=(count + 7) / 8; send_types_to_server: uint8 &enforce(send_types_to_server == 1); names : AttributeTypeAndName[count]; values : AttributeValue( # Check if null_bitmap contains this attribute index. This # will pass true if the attribute value is NULL and parsing # skipped in AttributeValue above. (null_bitmap[$context.connection.query_attr_idx() / 8] >> ($context.connection.query_attr_idx() % 8)) & 0x01, names[$context.connection.query_attr_idx()].type )[] &until($context.connection.query_attr_idx() >= count); }; type Query_Attributes = record { count : LengthEncodedInteger; set_count: LengthEncodedInteger; have_attr: case ( attr_count > 0 ) of { true -> attrs: Attributes(attr_count); false -> none: empty; } &requires(new_query_attrs); } &let { attr_count: int = to_int()(count); new_query_attrs = $context.connection.new_query_attrs(); }; # Command Request type Command_Request_Packet(pkt_len: uint32) = record { command: uint8; attrs : case ( command == COM_QUERY && $context.connection.get_client_query_attrs() && $context.connection.get_server_query_attrs() ) of { true -> query_attrs: Query_Attributes; false -> none: empty; }; have_change_user: case is_change_user of { true -> change_user: Change_User_Packet(pkt_len); false -> none_change_user: empty; }; arg : bytestring &restofdata; } &let { is_change_user = command == COM_CHANGE_USER; update_expectation: bool = $context.connection.set_next_expected_from_command(command); }; # Command from the client to switch the user mid-session. # # https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_com_change_user.html type Change_User_Packet(pkt_len: uint32) = record { username : NUL_String; auth_plugin_data_len: uint8; auth_plugin_data: bytestring &length=auth_plugin_data_len; database: NUL_String; charset: uint16; auth_plugin_name_case: case have_auth_plugin_name of { true -> auth_plugin_name: NUL_String; false -> no_more_data1: empty; }; conn_attrs_case: case have_conn_attrs of { true -> conn_attrs: Handshake_Connection_Attributes; false -> no_conn_attrs: empty; }; } &let { have_more_data = offsetof(auth_plugin_name_case) < pkt_len; have_auth_plugin_name = have_more_data && ($context.connection.get_client_capabilities() & CLIENT_PLUGIN_AUTH) == CLIENT_PLUGIN_AUTH; have_conn_attrs = have_more_data && ($context.connection.get_client_capabilities() & CLIENT_CONNECT_ATTRS) == CLIENT_CONNECT_ATTRS; } &exportsourcedata; # Command Response type Command_Response(pkt_len: uint32) = case $context.connection.get_expectation() of { EXPECT_COLUMN_COUNT -> col_count_meta: ColumnCountMeta; EXPECT_COLUMN_DEFINITION -> col_def : ColumnDefinition; EXPECT_COLUMN_DEFINITION_OR_EOF -> def_or_eof : ColumnDefinitionOrEOF(pkt_len); EXPECT_RESULTSET -> resultset : Resultset(pkt_len); EXPECT_REST_OF_PACKET -> rest : bytestring &restofdata; EXPECT_STATUS -> status : Command_Response_Status; EXPECT_EOF_THEN_RESULTSET -> eof : EOFIfLegacyThenResultset(pkt_len); default -> unknown : empty; }; type Command_Response_Status = record { pkt_type: uint8; response: case pkt_type of { 0x00 -> data_ok: OK_Packet; 0xfe -> data_eof: EOF_Packet(EOF_END); 0xff -> data_err: ERR_Packet; default -> unknown: empty; }; }; type ColumnCountMeta = record { byte : uint8; pkt_type: case byte of { 0x00 -> ok : OK_Packet; 0xff -> err : ERR_Packet; # 0xfb -> Not implemented default -> col_count: ColumnCount(byte); }; }; type ColumnCount(byte: uint8) = record { le_column_count : LengthEncodedIntegerLookahead(byte); } &let { col_num : uint32 = to_int()(le_column_count); update_col_num : bool = $context.connection.set_col_count(col_num); update_remain : bool = $context.connection.set_remaining_cols(col_num); update_expectation: bool = $context.connection.set_next_expected(EXPECT_COLUMN_DEFINITION); }; type ColumnDefinition = record { dummy: uint8; def : ColumnDefinition41(dummy); } &let { update_remain : bool = $context.connection.dec_remaining_cols(); update_expectation: bool = $context.connection.set_next_expected($context.connection.get_remaining_cols() > 0 ? EXPECT_COLUMN_DEFINITION : EXPECT_EOF_THEN_RESULTSET); }; # Only used to indicate the end of a result, no intermediate eofs here. # MySQL spec says "You must check whether the packet length is less than 9 # to make sure that it is a EOF_Packet packet" so the value of 13 here # comes from that 9, plus a 4-byte header. type EOFOrOK(pkt_len: uint32) = case ( $context.connection.get_deprecate_eof() || pkt_len > 13 ) of { false -> eof: EOF_Packet(EOF_END); true -> ok: OK_Packet; }; type ColumnDefinitionOrEOF(pkt_len: uint32) = record { marker : uint8; def_or_eof: case is_eof_or_ok of { true -> eof: EOFOrOK(pkt_len); false -> def: ColumnDefinition41(marker); } &requires(is_eof_or_ok); } &let { is_eof_or_ok: bool = (marker == 0xfe); }; type EOFIfLegacyThenResultset(pkt_len: uint32) = case $context.connection.get_deprecate_eof() of { false -> eof: EOF_Packet_With_Marker(EOF_INTERMEDIATE); true -> resultset: Resultset(pkt_len); } &let { update_result_seen: bool = $context.connection.set_results_seen(0); update_expectation: bool = $context.connection.set_next_expected(EXPECT_RESULTSET) &if( ! $context.connection.get_deprecate_eof() ); }; type Resultset(pkt_len: uint32) = record { marker : uint8; row_or_eof: case is_eof_or_ok of { true -> eof: EOFOrOK(pkt_len); false -> row: ResultsetRow(marker); } &requires(is_eof_or_ok); } &let { is_eof_or_ok : bool = (marker == 0xfe); update_result_seen: bool = $context.connection.inc_results_seen(); update_expectation: bool = $context.connection.set_next_expected(is_eof_or_ok ? NO_EXPECTATION : EXPECT_RESULTSET); }; type ResultsetRow(first_byte: uint8) = record { first_field: LengthEncodedStringArg(first_byte); fields : LengthEncodedString[$context.connection.get_col_count() - 1]; }; type ColumnDefinition41(first_byte: uint8) = record { catalog : LengthEncodedStringArg(first_byte); schema : LengthEncodedString; table : LengthEncodedString; org_table: LengthEncodedString; name : LengthEncodedString; org_name : LengthEncodedString; next_len : LengthEncodedInteger; char_set : uint16; col_len : uint32; type : uint8; flags : uint16; decimals : uint8; filler : padding[2]; }; # Opaque auth data exchanged during the connection phase between client and server. type AuthMoreData(is_orig: bool) = record { data : bytestring &restofdata; }; type AuthSwitchRequest = record { name : NUL_String; data : bytestring &restofdata; } &let { update_conn_expectation: bool = $context.connection.set_next_conn_expected(EXPECT_AUTH_DATA); # After an AuthSwitchRequest, server replies with OK_Packet, ERR_Packet or AuthMoreData. update_expectation: bool = $context.connection.set_next_expected(EXPECT_STATUS); }; type ColumnDefinition320 = record { table : LengthEncodedString; name : LengthEncodedString; length_of_col_len: LengthEncodedInteger; col_len : uint24le; type_len : LengthEncodedInteger; type : uint8; }; type OK_Packet = record { le_rows: LengthEncodedInteger; todo : bytestring &restofdata; } &let { rows : uint32 = to_int()(le_rows); update_state: bool = $context.connection.update_state(COMMAND_PHASE); }; type ERR_Packet = record { code : uint16; state: bytestring &length=6; msg : bytestring &restofdata; } &let { update_state: bool = $context.connection.update_state(COMMAND_PHASE); }; type EOF_Packet(typ: EOFType) = record { warnings: uint16; status : uint16; }; type EOF_Packet_With_Marker(typ: EOFType) = record { marker : uint8; payload: EOF_Packet(typ); } &let { update_state: bool = $context.connection.update_state(COMMAND_PHASE); }; # State tracking refine connection MySQL_Conn += { %member{ uint8 version_; uint8 previous_seq_id_; int state_; Expected expected_; ConnectionExpected conn_expected_; uint32 col_count_; uint32 remaining_cols_; uint32 results_seen_; bool deprecate_eof_; bool server_query_attrs_; bool client_query_attrs_; uint32 client_capabilities_; std::string auth_plugin_; int query_attr_idx_; %} %init{ version_ = 0; previous_seq_id_ = 0; state_ = CONNECTION_PHASE; expected_ = EXPECT_STATUS; conn_expected_ = EXPECT_HANDSHAKE; col_count_ = 0; remaining_cols_ = 0; results_seen_ = 0; deprecate_eof_ = false; server_query_attrs_ = false; client_query_attrs_ = false; query_attr_idx_ = 0; %} function get_version(): uint8 %{ return version_; %} function set_version(v: uint8): bool %{ version_ = v; return true; %} function get_previous_seq_id(): uint8 %{ return previous_seq_id_; %} function set_previous_seq_id(s: uint8): bool %{ previous_seq_id_ = s; return true; %} function get_state(): int %{ return state_; %} function update_state(s: state): bool %{ state_ = s; if ( s == COMMAND_PHASE ) conn_expected_ = EXPECT_HANDSHAKE; // Reset connection phase expectation return true; %} function get_deprecate_eof(): bool %{ return deprecate_eof_; %} function set_deprecate_eof(d: bool): bool %{ deprecate_eof_ = d; return true; %} function get_server_query_attrs(): bool %{ return server_query_attrs_; %} function set_server_query_attrs(q: bool): bool %{ server_query_attrs_ = q; return true; %} function get_client_query_attrs(): bool %{ return client_query_attrs_; %} function set_client_query_attrs(q: bool): bool %{ client_query_attrs_ = q; return true; %} function set_client_capabilities(c: uint32): bool %{ client_capabilities_ = c; return true; %} function get_client_capabilities(): uint32 %{ return client_capabilities_; %} function get_expectation(): Expected %{ return expected_; %} function set_next_expected(e: Expected): bool %{ expected_ = e; return true; %} function get_conn_expectation(): ConnectionExpected %{ return conn_expected_; %} function set_next_conn_expected(c: ConnectionExpected): bool %{ conn_expected_ = c; return true; %} function set_next_expected_from_command(cmd: uint8): bool %{ switch ( cmd ) { case COM_SLEEP: expected_ = EXPECT_STATUS; break; case COM_QUIT: expected_ = EXPECT_STATUS; break; case COM_INIT_DB: expected_ = EXPECT_STATUS; break; case COM_QUERY: expected_ = EXPECT_COLUMN_COUNT; break; case COM_FIELD_LIST: expected_ = EXPECT_COLUMN_DEFINITION_OR_EOF; break; case COM_CREATE_DB: expected_ = EXPECT_STATUS; break; case COM_DROP_DB: expected_ = EXPECT_STATUS; break; case COM_REFRESH: expected_ = EXPECT_STATUS; break; case COM_SHUTDOWN: expected_ = EXPECT_STATUS; break; case COM_STATISTICS: expected_ = EXPECT_REST_OF_PACKET; break; case COM_PROCESS_INFO: expected_ = EXPECT_COLUMN_COUNT; break; case COM_CONNECT: expected_ = EXPECT_STATUS; break; case COM_PROCESS_KILL: expected_ = EXPECT_STATUS; break; case COM_DEBUG: expected_ = EXPECT_STATUS; break; case COM_PING: expected_ = EXPECT_STATUS; break; case COM_TIME: expected_ = EXPECT_STATUS; break; case COM_DELAYED_INSERT: expected_ = EXPECT_STATUS; break; case COM_CHANGE_USER: update_state(CONNECTION_PHASE); break; case COM_BINLOG_DUMP: expected_ = NO_EXPECTATION; break; case COM_TABLE_DUMP: expected_ = NO_EXPECTATION; break; case COM_CONNECT_OUT: expected_ = NO_EXPECTATION; break; case COM_REGISTER_SLAVE: expected_ = NO_EXPECTATION; break; case COM_STMT_PREPARE: expected_ = NO_EXPECTATION; break; case COM_STMT_EXECUTE: expected_ = NO_EXPECTATION; break; case COM_STMT_SEND_LONG_DATA: expected_ = NO_EXPECTATION; break; case COM_STMT_CLOSE: expected_ = NO_EXPECTATION; break; case COM_STMT_RESET: expected_ = NO_EXPECTATION; break; case COM_SET_OPTION: expected_ = NO_EXPECTATION; break; case COM_STMT_FETCH: expected_ = NO_EXPECTATION; break; case COM_DAEMON: expected_ = EXPECT_STATUS; break; case COM_BINLOG_DUMP_GTID: expected_ = NO_EXPECTATION; break; case COM_RESET_CONNECTION: expected_ = EXPECT_STATUS; break; default: expected_ = NO_EXPECTATION; break; } return true; %} function get_col_count(): uint32 %{ return col_count_; %} function set_col_count(i: uint32): bool %{ col_count_ = i; return true; %} function get_remaining_cols(): uint32 %{ return remaining_cols_; %} function set_remaining_cols(i: uint32): bool %{ remaining_cols_ = i; return true; %} function dec_remaining_cols(): bool %{ --remaining_cols_; return true; %} function get_results_seen(): uint32 %{ return results_seen_; %} function set_results_seen(i: uint32): bool %{ results_seen_ = i; return true; %} function inc_results_seen(): bool %{ ++results_seen_; return true; %} function query_attr_idx(): int %{ return query_attr_idx_; %} function new_query_attrs(): bool %{ query_attr_idx_ = 0; return true; %} function next_query_attr(): bool %{ query_attr_idx_++; return true; %} };