If you look around the internet (e.g. What is maximum query size for mysql?), you will find claims, that the maximum query size for MariaDB/MySQL equals the value of the max_allowed_packet
variable. But when you actually try that, you will find that this is not exactly true.
Here is a small Lua script that generates a test query of a desired size:
prefix = "SELECT IF(0,'"
postfix = "',NULL);"
max_allowed_packet = 16777216 -- The value for my setup
function gen_query(size)
local n = size - string.len(prefix) - string.len(postfix)
local query = prefix .. string.rep('X', n) .. postfix
io.open("/tmp/testquery.sql", "w"):write(query):close()
end
gen_query(max_allowed_packet)
/tmp/testquery.sql
now contains a query like SELECT IF(0,'<a lot of Xs here>',NULL);
.
If we now try to execute this, we will get an error:
$ mysql -A -B < /tmp/testquery.sql
ERROR 1153 (08S01) at line 1: Got a packet bigger than 'max_allowed_packet' bytes
If we do the same thing, but with gen_query(max_allowed_packet - 1)
(generating a query one byte smaller), the server will happily execute the query:
$ mysql -uroot -pmysql --protocol=tcp -A -B < /tmp/testquery.sql
IF(0,'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
NULL
If we dig around the documentation of MySQL (couldn't find the equivalent for MariaDB), this begins to make sense, as the packet includes an extra byte (0x03) that announces the packet as a COM_QUERY
packet: https://dev.mysql.com/doc/internals/en/com-query.html
So far this all makes sense to me, but if we go ahead and modify our lua program to not include a semicolon at the end (by setting postfix = "',NULL)"
), this is suddenly the result again:
$ mysql -uroot -pmysql --protocol=tcp -A -B < /tmp/testquery.sql
ERROR 1153 (08S01) at line 1: Got a packet bigger than 'max_allowed_packet' bytes
The test query is still max_allowed_packet - 1 bytes long:
$ wc -c /tmp/testquery.sql
16777215 /tmp/testquery.sql
A max_allowed_packet - 2 bytes long query without the semicolon works again as expected.
What is going on? Does the mysql
command line client / the mysql library automatically insert a ;
at the end? As far as I can tell, it doesn't, here is an excerpt of an strace output:
$ strace -v -e write=3 mysql -A -B < /tmp/testquery.sql
<<< irrelevant syscalls omitted >>>
sendto(3, "\377\377\377\0\3SELECT IF(0,'XXXXXXXXXXXXXX"..., 16384, 0, NULL, 0) = 16384
| 00000 ff ff ff 00 03 53 45 4c 45 43 54 20 49 46 28 30 .....SELECT IF(0 |
| 00010 2c 27 58 58 58 58 58 58 58 58 58 58 58 58 58 58 ,'XXXXXXXXXXXXXX |
<<< lots of hexdump data omitted >>>
| ffbff0 58 58 58 58 58 58 58 58 58 58 58 58 27 2c 20 4e XXXXXXXXXXXX', N |
| ffc000 55 4c 4c ULL |
sendto(3, "\1\0\0\1)", 5, 0, NULL, 0) = 5
| 00000 01 00 00 01 29 ....) |
<<< more irrelevant syscalls omitted >>>
Can I send a query that is max_allowed_packet - 2 bytes long and always expect it to work (given there are no errors in the sql query, of course :) )? And is this behavior documented somewhere?