MySQL Protocol

This document descibes the MySQL network protocol version 10, as of October 2002 (MySQL 3.23.52). It has been produced by reading libmysql.c, net.c and sql_show.cc in the MySQL source.

The protocol requires first that a client and server negotiate a connection, including agreeing about protocol features and authentication. This is followed by a series of requests from the client and reponses from the server, until either the client issues a QUIT request or the connection is broken.

MySQL uses either a Unix domain socket (usually /tmp/mysql.sock or /var/run/mysqld/mysql.sock), an MS Windows named pipe (usually \\hostname\pipe\mysql) or a TCP port (usually 3306). It treats all communications methods identically.

Each client message is sent as a single MySQL 'packet'; a server response may consist of many packets. These packets are not related to the underlying network layer packets, and indeed many response packets may be contained in a single IP datagram.

Each packet begins with a 4-byte header. The first three header bytes give the body length as a Little-Endian integer (all MySQL integers are Little-Endian unless described otherwise). The fourth gives a packet number for this transaction, starting from 0, and including both client and server messages. The body of the message then follows.

All example packets are taken from a real system.

Connection

Once the transport-level connection is established, the connection negotiation starts with the server identifying itself.

Server greeting

The host sends an initial greeting similar to:

2c 00 00 00 Body Length=44, packet=0
0aProtocol=10
33 2e 32 33 2e 35 32 2d 6c 6f 67 00 Version="3.23.52-log"
04 00 00 00 Thread ID=4
79 46 2f 57 48 43 57 6a 00Salt="yF/WHCWj"
2c 20 Caps=LONG_FLAG | CONNECT_WITH_DB | COMPRESS | TRANSACTIONS
08Charset=Latin-1
02 00Status=AUTOCOMMIT
00 00 00 00 00 00 00 00 00 00 00 00 00 13 bytes unused

The first byte of the message body is the protocol version, currently 10; the current client is capable of connecting to protocol 9 or 10 servers. The protocol version is followed by a version string for the server (here 3.23.52-log), null-terminated - the -log suffix means logging is enabled. Following this is a 4-byte integer describing the thread ID of the server thread handling the request. Finally an 8-byte 'scramble string' salt for encryption, again null-terminated.

Newer servers follow this by a 2-byte integer describing the server's capabilities - this server can do LONG_FLAG and CONNECT_WITH_DB, as well as the more recent COMPRESS and TRANSACTIONS. Even newer servers follow this with a single byte giving the server character set and two bytes describing the server status.

Client authentication

The client now sends a request to the server, similar to:

12 00 00 01 Body Length=18, packet=1
85 24 Caps=LONG_PASSWORD | LONG_FLAG | TRANSACTIONS | INTERACTIVE | LOCAL_FILES
00 00 00Max packet=16777216
72 6f 6f 74 00User="root"
5b 43 4f 54 52 46 47 59Password="[COTRFGY"

This is the client's turn to send its capabilities in two bytes. These will always include LONG_PASSWORD, LONG_FLAG and TRANSACTIONS for modern clients, and in this case include INTERACTIVE and LOCAL_FILES as well, as this is the standard MySQL command-line client.

At this point the client will switch to SSL on the communications channel if it has set the SSL capability bit - which it will not do if the server has not also set that bit.

This is followed by three bytes giving the maximum allowed packet length. A value of 0 here means 2^24=16MB, and will be used by modern clients. This is followed by the username, null-terminated, and the eight characters of scrambled password (the example given here is not a real password) if a password is provided.

If the client is trying to connect to a particular database and the server is capable of CONNECT_WITH_DB, it will add the database name as a null-terminated string.

The client will now expect the server to send an OK simple response or an error. After an OK response, the flow of client commands can begin; after an error response the client closes the connection.

If the client and server are both capable of compression, and the client wants to use it (it usually has to be explicitly enabled), the client will now switch to using it. It follows the standard packet header with a three-byte compressed length. The compression mechanism used is standard zlib.

Simple responses

Most commands produce a simple 'OK' response. This will usually be of the form:

05 00 00 01 Body Length=5, packet=1
00 00 00 02 00 OK

In the case of an OK response to the initial authentication, of course, the packet number will be 2.

For an error, the response will be more like the following:

27 00 00 01 Body Length=39, packet=1
ffError
7a 04Error code=NO_SUCH_TABLE (1146)
54 61 62 6c 65 20 27 72 65 64 66 65 72 6e 69 2e 73 61 6d 70 6c 65 27 20 64 6f 65 73 6e 27 74 20 65 78 69 73 74 Error meesage="Table 'redferni.sample' doesn't exist"

The first byte of the error response body is set to 255.

If the body length is at least 4, then the packet contains a 2-byte error code, followed by the error message, of up to 200 characters. The error code is absent in protocol 9 servers.

There are 218 error codes, ranging from HASHCHK (1000) to ROW_IS_REFERENCED (1217).

Commands

A simple command is sent in the form:

0f 00 00 00 Body Length=15, packet=0
03Command=QUERY
73 68 6f 77 20 64 61 74 61 62 61 73 65 73Arg="show databases"

The first byte is the command code, followed by an optional argument. Any command not explicitly listed, including any SQL command, is sent as QUERY, as in the example.

Disconnection

When the client wishes to disconnect, it sends a QUIT simple command and closes its end of the connection, as follows
01 00 00 00 Body Length=1, packet=0
01Command=QUIT

The server will close its end of the connection without further response.

Field-length encoding

When data is sent in tabular form, each field is preceded by its field length, encoded in the following manner:

ByteMeaning
0-250Same as byte
251NULL field
252Value in following 2 bytes
253Value in following 3 bytes
254Value in following 8 bytes

This mechanism is also used for encoding other integers efficiently.

Tabular response

When returning data as a table, as a response to a QUERY, FIELD_LIST or PROCESS_INFO command, MySQL first describes the fields in each row, then sends the rows.

16 00 00 02 Body Length=22, packet=2
06 73 65 6d 70 6c 65Table="sample"
04 6e 61 6d 65Field="name"
03 14 00 00Length=20
01 fdType=VAR_STRING
03 00 00 00Flags=0000, Decimals=00

There is one logical packet per field description. Each field description contains five parts, each preceded by their length in field_length encoding: the table name, the field name, the maximum length (a 3 byte integer), the field type and 2 bytes of flags followed by one byte of decimals.

In a client or server without the LONG_FLAG capability, there is only one byte of field flags.

If default values are provided (this will usually just be for the FIELD_LIST command), there is a sixth part to the field description: default value.

01 00 00 04 Body Length=1, packet=4
feEnd of field list

After all the field description packets, a packet with just a single byte of value 254 indicates the start of the rows.

09 00 00 05 Body Length=9, packet=5
06 41 73 68 6c 65 79Row 1 Col 1="Ashley"
01 31Row 1 Col 2=1

Each row is in a separate packet, with the field values preceded by a field length. All non-binary fields values are sent as text - in particular, all numeric fields appear as ASCII, and timestamps as YYYY-MM-DD HH:MM:SS. Trailing spaces are removed from char and varchar fields.

01 00 00 07 Body Length=1, packet=7
feEnd of row list

After the last row, another packet holding just the byte 254 indicates the end of the data.

Complex Responses

There are four commands that can return complex responses.

STATISTICS

The STATISTICS command response is simply a string describing the current server statistics.

FIELD_LIST

The FIELD_LIST command response is the same as the first part of a tabular response (the field descriptions) - with the exception that a default value may be attached to each field description.

PROCESS_INFO

The PROCESS_INFO command response is first a packet containing a field count, in field_length encoding, followed by a tabular response.

QUERY

The QUERY command handles all SQL commands, as well as almost all client commands.

There are three forms of response. The simplest is where no tabular data needs to be returned:

05 00 00 01 Body Length=5, packet=1
000=OK - no tabular data
00Affected rows=0
00Insert id=0
02 00Status=AUTOCOMMIT

Here, the first byte is 0 and the following two entries are field_length encoded. If the server is capable of transactions, two bytes of server status will follow.

Finally, some additional information may be provided as a string, preceded by its field length.

If a file is to be sent to the server, the following response is generated:

01 00 00 01 Body Length=1, packet=1
fb251=Upload file

The file is sent by the client as a series of ordinary packets, with the final packet empty.

Otherwise, the result contains a field count, followed by some extra information (possibly a row count), both field_length encoded:

01 00 00 01 Body Length=1, packet=1
02Field count=2

This is then followed by the tabular data.

Semantics

Many MySQL clients and drivers expect certain behaviour from the server, and perform queries behind the scenes. The important responses are shown below.

Getting current database and user

The mysql command-line client issues the request
select DATABASE(),USER()
when the user asks for the current status. It will expect the following response:

DATABASE()USER()
VARCHAR(34) NOT NULLVARCHAR(77) NOT NULL

The DATABASE() field may be empty. The USER() field is of the form user@host.

Status enquiry

The current status response is similar to:

Uptime: 129  Threads: 1  Questions: 6  Slow queries: 0  
Opens: 6  Flush tables: 1  Open tables: 0 Queries per second avg: 0.047

The mysql client assumes the number following the first space is a time and formats it appropriately.

Show status

Variable_nameValue
CHAR(30) NOT NULLCHAR(256) NOT NULL

Show databases

Database
CHAR(64) NOT NULL

Show tables

Tables_in_db
CHAR(64) NOT NULL

Show fields

FieldTypeNullKeyDefaultExtra
CHAR(64) NOT NULLCHAR(40) NOT NULLCHAR(1) NOT NULLCHAR(3) NOT NULLCHAR(64)CHAR(20) NOT NULL

The Null field takes values '' or 'YES' - this is presumably a bug. The Key field is 'PRI', 'UNI', 'MUL'or ''.

The same output is produce by show columns, desc or describe.

Show processlist

IdUserHostdbCommandTimeStateInfo
IntCHAR(16) NOT NULLCHAR(64) NOT NULLCHAR(64)CHAR(16) NOT NULLCHAR(7) NOT NULLCHAR(30)CHAR(100)

Show processlist full removes the size limit on the Info column.

Password algorithm

The protocol 9 and protocol 10 password algorithms are different. In both cases, a seed is provided by the server and a password is provided by the user, and together they are used to generate a scrambled message of the same length as the seed.

Hashing mechanism

A string is hashed to a pair of unsigned longs using the following algorithm:

n1 = 1345345333
n2 = 305419889
add = 7
foreach c in password (ignoring space or tab)
n1 = n1 EOR (((n1 & 63) + add) * c + n1 * 256)
n2 = (n2 * 256) EOR n1
add = add + c

Only the bottom 31 bits of the unsigned longs are used.

Protocol 10

First the password, then the salt are hashed to give two pairs of longs: p1, p2, s1 and s2. These are then used to generate the two seeds of a random number generator, which takes values between 0 and 230-2.

seed1 = p1 EOR s1
seed2 = p2 EOR s2

The random number generator algorithm is:

seed1 = (seed1 * 3 + seed2) mod 230 - 1
seed2 = (seed1 + seed2 + 33) mod 230 - 1
return seed1 / (230 - 1)

The random number generator is called once for each byte of the salt, and the number given is multiplied by 31 and added to 64 to give an ASCII character between '@' and '^'.

msg[i] = chr(64 + rng()*31)

The string just given then has each character exclusive-ored with the random number generator multiplied by 31.

msg[i] = msg[i] eor(rng()*31)

Protocol 9

Here only p1 and s1 are used, 230 is replaced by 229 and seed2 is initially set to seed1 / 2. The final exclusive-or pass to modify the message is also skipped.

Magic numbers

MySQL uses the following codes:

Capability nameValueMeaning
LONG_PASSWORD1New more secure passwords
FOUND_ROWS 2Found instead of affected rows
LONG_FLAG4Get all column flags
CONNECT_WITH_DB8One can specify db on connect
NO_SCHEMA16Don't allow database.table.column
COMPRESS 32Can use compression protocol
ODBC 64ODBC client
LOCAL_FILES128Can use LOAD DATA LOCAL
IGNORE_SPACE 256Ignore spaces before '('
CHANGE_USER512Support the mysql_change_user()
INTERACTIVE1024This is an interactive client
SSL2048Switch to SSL after handshake
IGNORE_SIGPIPE 4096IGNORE sigpipes
TRANSACTIONS8192Client knows about transactions
Capabilities

CommandCodeArgumentsAllowed from client
SLEEP0No
QUIT1""
INIT_DB2db
QUERY3query
FIELD_LIST4table
CREATE_DB5db
DROP_DB6db
REFRESH7option bits
SHUTDOWN8
STATISTICS9
PROCESS_INFO10
CONNECT11No
PROCESS_KILL124-byte pid
DEBUG13
PING14
TIME15No
DELAYED_INSERT16No
CHANGE_USER17user
BINLOG_DUMP18
TABLE_DUMP19
CONNECT_OUT20No
Commands

TypeCodeTypeCode
DECIMAL0ENUM247
TINY1SET248
SHORT2TINY_BLOB249
LONG3MEDIUM_BLOB250
FLOAT4LONG_BLOB251
DOUBLE5BLOB252
NULL6VAR_STRING253
TIMESTAMP7STRING254
LONGLONG8
INT249
DATE10
TIME11
DATETIME12
YEAR13
NEWDATE14
Field types

StatusCode
IN_TRANS1
AUTOCOMMIT2
Status codes

CharsetCodeCharsetCode
big51hebrew16
czech2win125117
dec83tis62018
dos4euc_kr19
german15estonia20
hp86hungarian21
koi8_ru7koi8_ukr22
latin18win1251ukr23
latin29gb231224
swe710greek25
usa711win125026
ujis12croat27
sjis13gbk28
cp125114cp125729
danish15latin530
Character sets

Field FlagValueMeaning
NOT_NULL1Field can't be NULL
PRI_KEY2Field is part of a primary key
UNIQUE_KEY4Field is part of a unique key
MULTIPLE_KEY8Field is part of a key
BLOB16Field is a blob
UNSIGNED32Field is unsigned
ZEROFILL64Field is zerofill
BINARY128Field is binary
ENUM256Field is an enum
AUTO_INCREMENT512Field is an autoincrement field
TIMESTAMP1024Field is a timestamp
SET2048Field is a set
NUM32768Field is num (for clients)
Field flags

Note that the NUM flag is set automatically by the client, based on the field type.

ErrorValueErrorValue
HASHCHK1000WRONG_PARAMETERS_TO_PROCEDURE1108
NISAMCHK1001UNKNOWN_TABLE1109
NO1002FIELD_SPECIFIED_TWICE1110
YES1003INVALID_GROUP_FUNC_USE1111
CANT_CREATE_FILE1004UNSUPPORTED_EXTENSION1112
CANT_CREATE_TABLE1005TABLE_MUST_HAVE_COLUMNS1113
CANT_CREATE_DB1006RECORD_FILE_FULL1114
DB_CREATE_EXISTS1007UNKNOWN_CHARACTER_SET1115
DB_DROP_EXISTS1008TOO_MANY_TABLES1116
DB_DROP_DELETE1009TOO_MANY_FIELDS1117
DB_DROP_RMDIR1010TOO_BIG_ROWSIZE1118
CANT_DELETE_FILE1011STACK_OVERRUN1119
CANT_FIND_SYSTEM_REC1012WRONG_OUTER_JOIN1120
CANT_GET_STAT1013NULL_COLUMN_IN_INDEX1121
CANT_GET_WD1014CANT_FIND_UDF1122
CANT_LOCK1015CANT_INITIALIZE_UDF1123
CANT_OPEN_FILE1016UDF_NO_PATHS1124
FILE_NOT_FOUND1017UDF_EXISTS1125
CANT_READ_DIR1018CANT_OPEN_LIBRARY1126
CANT_SET_WD1019CANT_FIND_DL_ENTRY1127
CHECKREAD1020FUNCTION_NOT_DEFINED1128
DISK_FULL1021HOST_IS_BLOCKED1129
DUP_KEY1022HOST_NOT_PRIVILEGED1130
ERROR_ON_CLOSE1023PASSWORD_ANONYMOUS_USER1131
ERROR_ON_READ1024PASSWORD_NOT_ALLOWED1132
ERROR_ON_RENAME1025PASSWORD_NO_MATCH1133
ERROR_ON_WRITE1026UPDATE_INFO1134
FILE_USED1027CANT_CREATE_THREAD1135
FILSORT_ABORT1028WRONG_VALUE_COUNT_ON_ROW1136
FORM_NOT_FOUND1029CANT_REOPEN_TABLE1137
GET_ERRNO1030INVALID_USE_OF_NULL1138
ILLEGAL_HA1031REGEXP_ERROR1139
KEY_NOT_FOUND1032MIX_OF_GROUP_FUNC_AND_FIELDS1140
NOT_FORM_FILE1033NONEXISTING_GRANT1141
NOT_KEYFILE1034TABLEACCESS_DENIED_ERROR1142
OLD_KEYFILE1035COLUMNACCESS_DENIED_ERROR1143
OPEN_AS_READONLY1036ILLEGAL_GRANT_FOR_TABLE1144
OUTOFMEMORY1037GRANT_WRONG_HOST_OR_USER1145
OUT_OF_SORTMEMORY1038NO_SUCH_TABLE1146
UNEXPECTED_EOF1039NONEXISTING_TABLE_GRANT1147
CON_COUNT_ERROR1040NOT_ALLOWED_COMMAND1148
OUT_OF_RESOURCES1041SYNTAX_ERROR1149
BAD_HOST_ERROR1042DELAYED_CANT_CHANGE_LOCK1150
HANDSHAKE_ERROR1043TOO_MANY_DELAYED_THREADS1151
DBACCESS_DENIED_ERROR1044ABORTING_CONNECTION1152
ACCESS_DENIED_ERROR1045NET_PACKET_TOO_LARGE1153
NO_DB_ERROR1046NET_READ_ERROR_FROM_PIPE1154
UNKNOWN_COM_ERROR1047NET_FCNTL_ERROR1155
BAD_NULL_ERROR1048NET_PACKETS_OUT_OF_ORDER1156
BAD_DB_ERROR1049NET_UNCOMPRESS_ERROR1157
TABLE_EXISTS_ERROR1050NET_READ_ERROR1158
BAD_TABLE_ERROR1051NET_READ_INTERRUPTED1159
NON_UNIQ_ERROR1052NET_ERROR_ON_WRITE1160
SERVER_SHUTDOWN1053NET_WRITE_INTERRUPTED1161
BAD_FIELD_ERROR1054TOO_LONG_STRING1162
WRONG_FIELD_WITH_GROUP1055TABLE_CANT_HANDLE_BLOB1163
WRONG_GROUP_FIELD1056TABLE_CANT_HANDLE_AUTO_INCREMENT1164
WRONG_SUM_SELECT1057DELAYED_INSERT_TABLE_LOCKED1165
WRONG_VALUE_COUNT1058WRONG_COLUMN_NAME1166
TOO_LONG_IDENT1059WRONG_KEY_COLUMN1167
DUP_FIELDNAME1060WRONG_MRG_TABLE1168
DUP_KEYNAME1061DUP_UNIQUE1169
DUP_ENTRY1062BLOB_KEY_WITHOUT_LENGTH1170
WRONG_FIELD_SPEC1063PRIMARY_CANT_HAVE_NULL1171
PARSE_ERROR1064TOO_MANY_ROWS1172
EMPTY_QUERY1065REQUIRES_PRIMARY_KEY1173
NONUNIQ_TABLE1066NO_RAID_COMPILED1174
INVALID_DEFAULT1067UPDATE_WITHOUT_KEY_IN_SAFE_MODE1175
MULTIPLE_PRI_KEY1068KEY_DOES_NOT_EXITS1176
TOO_MANY_KEYS1069CHECK_NO_SUCH_TABLE1177
TOO_MANY_KEY_PARTS1070CHECK_NOT_IMPLEMENTED1178
TOO_LONG_KEY1071CANT_DO_THIS_DURING_AN_TRANSACTION1179
KEY_COLUMN_DOES_NOT_EXITS1072ERROR_DURING_COMMIT1180
BLOB_USED_AS_KEY1073ERROR_DURING_ROLLBACK1181
TOO_BIG_FIELDLENGTH1074ERROR_DURING_FLUSH_LOGS1182
WRONG_AUTO_KEY1075ERROR_DURING_CHECKPOINT1183
READY1076NEW_ABORTING_CONNECTION1184
NORMAL_SHUTDOWN1077DUMP_NOT_IMPLEMENTED1185
GOT_SIGNAL1078FLUSH_MASTER_BINLOG_CLOSED1186
SHUTDOWN_COMPLETE1079INDEX_REBUILD1187
FORCING_CLOSE1080MASTER1188
IPSOCK_ERROR1081MASTER_NET_READ1189
NO_SUCH_INDEX1082MASTER_NET_WRITE1190
WRONG_FIELD_TERMINATORS1083FT_MATCHING_KEY_NOT_FOUND1191
BLOBS_AND_NO_TERMINATED1084LOCK_OR_ACTIVE_TRANSACTION1192
TEXTFILE_NOT_READABLE1085UNKNOWN_SYSTEM_VARIABLE1193
FILE_EXISTS_ERROR1086CRASHED_ON_USAGE1194
LOAD_INFO1087CRASHED_ON_REPAIR1195
ALTER_INFO1088WARNING_NOT_COMPLETE_ROLLBACK1196
WRONG_SUB_KEY1089TRANS_CACHE_FULL1197
CANT_REMOVE_ALL_FIELDS1090SLAVE_MUST_STOP1198
CANT_DROP_FIELD_OR_KEY1091SLAVE_NOT_RUNNING1199
INSERT_INFO1092BAD_SLAVE1200
INSERT_TABLE_USED1093MASTER_INFO1201
NO_SUCH_THREAD1094SLAVE_THREAD1202
KILL_DENIED_ERROR1095TOO_MANY_USER_CONNECTIONS1203
NO_TABLES_USED1096SET_CONSTANTS_ONLY1204
TOO_BIG_SET1097LOCK_WAIT_TIMEOUT1205
NO_UNIQUE_LOGFILE1098LOCK_TABLE_FULL1206
TABLE_NOT_LOCKED_FOR_WRITE1099READ_ONLY_TRANSACTION1207
TABLE_NOT_LOCKED1100DROP_DB_WITH_READ_LOCK1208
BLOB_CANT_HAVE_DEFAULT1101CREATE_DB_WITH_READ_LOCK1209
WRONG_DB_NAME1102WRONG_ARGUMENTS1210
WRONG_TABLE_NAME1103NO_PERMISSION_TO_CREATE_USER1211
TOO_BIG_SELECT1104UNION_TABLES_IN_DIFFERENT_DIR1212
UNKNOWN_ERROR1105LOCK_DEADLOCK1213
UNKNOWN_PROCEDURE1106TABLE_CANT_HANDLE_FULLTEXT1214
WRONG_PARAMCOUNT_TO_PROCEDURE1107CANNOT_ADD_FOREIGN1215
Errors

Release 1.1 - 5th October 2002
Ian Redfern (mysql(at)redferni.uklinux.net)

This document is in the public domain, and you may do anything with it you wish. The author takes no responsibility for the accuracy of its contents. Some of the terms in this document are trademarks of MySQL AB and other companies. No trade secrets or other privileged information has been used in its compilation, and the author has no relationship with MySQL.