--[[ $%BEGINLICENSE%$ Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA $%ENDLICENSE%$ --]] --- -- map SQL commands to the hidden MySQL Protocol commands -- -- some protocol commands are only available through the mysqladmin tool like -- * ping -- * shutdown -- * debug -- * statistics -- -- ... while others are avaible -- * process info (SHOW PROCESS LIST) -- * process kill (KILL ) -- -- ... and others are ignored -- * time -- -- that way we can test MySQL Servers more easily with "mysqltest" -- --- -- recognize special SQL commands and turn them into COM_* sequences -- function read_query(packet) if packet:byte() ~= proxy.COM_QUERY then return end if packet:sub(2) == "COMMIT SUICIDE" then proxy.queries:append(proxy.COM_SHUTDOWN, string.char(proxy.COM_SHUTDOWN), { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "PING" then proxy.queries:append(proxy.COM_PING, string.char(proxy.COM_PING), { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "STATISTICS" then proxy.queries:append(proxy.COM_STATISTICS, string.char(proxy.COM_STATISTICS), { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "PROCINFO" then proxy.queries:append(proxy.COM_PROCESS_INFO, string.char(proxy.COM_PROCESS_INFO), { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "TIME" then proxy.queries:append(proxy.COM_TIME, string.char(proxy.COM_TIME), { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "DEBUG" then proxy.queries:append(proxy.COM_DEBUG, string.char(proxy.COM_DEBUG), { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "PROCKILL" then proxy.queries:append(proxy.COM_PROCESS_KILL, string.char(proxy.COM_PROCESS_KILL), { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "SETOPT" then proxy.queries:append(proxy.COM_SET_OPTION, string.char(proxy.COM_SET_OPTION), { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "BINLOGDUMP" then proxy.queries:append(proxy.COM_BINLOG_DUMP, string.char(proxy.COM_BINLOG_DUMP), { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "BINLOGDUMP1" then proxy.queries:append(proxy.COM_BINLOG_DUMP, string.char(proxy.COM_BINLOG_DUMP) .. "\004\000\000\000" .. "\000\000" .. "\002\000\000\000" .. "\000" .. "" , { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "REGSLAVE" then proxy.queries:append(proxy.COM_REGISTER_SLAVE, string.char(proxy.COM_REGISTER_SLAVE), { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "REGSLAVE1" then proxy.queries:append(proxy.COM_REGISTER_SLAVE, string.char(proxy.COM_REGISTER_SLAVE) .. "\001\000\000\000" .. -- server-id "\000" .. -- report-host "\000" .. -- report-user "\000" .. -- report-password ? "\001\000" .. -- our port "\000\000\000\000" .. -- recovery rank "\001\000\000\000" .. -- master id ... what ever that is "" , { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "PREP" then proxy.queries:append(proxy.COM_STMT_PREPARE, string.char(proxy.COM_STMT_PREPARE), { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "PREP1" then proxy.queries:append(proxy.COM_STMT_PREPARE, string.char(proxy.COM_STMT_PREPARE) .. "SELECT ?", { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "EXEC" then proxy.queries:append(proxy.COM_STMT_EXECUTE, string.char(proxy.COM_STMT_EXECUTE), { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "EXEC1" then proxy.queries:append(proxy.COM_STMT_EXECUTE, string.char(proxy.COM_STMT_EXECUTE) .. "\001\000\000\000" .. -- stmt-id "\000" .. -- flags "\001\000\000\000" .. -- iteration count "\000" .. -- null-bits "\001" .. -- new-parameters "\000\254" .. "\004" .. "1234" .. "", { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "DEAL" then proxy.queries:append(proxy.COM_STMT_CLOSE, string.char(proxy.COM_STMT_CLOSE), { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "DEAL1" then proxy.queries:append(proxy.COM_STMT_CLOSE, string.char(proxy.COM_STMT_CLOSE) .. "\001\000\000\000", { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "RESET" then proxy.queries:append(proxy.COM_STMT_RESET, string.char(proxy.COM_STMT_RESET), { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "RESET1" then proxy.queries:append(proxy.COM_STMT_RESET, string.char(proxy.COM_STMT_RESET) .. "\001\000\000\000", { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "FETCH" then proxy.queries:append(proxy.COM_STMT_FETCH, string.char(proxy.COM_STMT_FETCH), { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "FETCH1" then proxy.queries:append(proxy.COM_STMT_FETCH, string.char(proxy.COM_STMT_FETCH) .. "\001\000\000\000" .. "\128\000\000\000", { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "FLIST" then proxy.queries:append(proxy.COM_FIELD_LIST, string.char(proxy.COM_FIELD_LIST), { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "FLIST1" then proxy.queries:append(proxy.COM_FIELD_LIST, string.char(proxy.COM_FIELD_LIST) .. "t1\000id\000\000\000", { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "TDUMP" then proxy.queries:append(proxy.COM_TABLE_DUMP, string.char(proxy.COM_TABLE_DUMP), { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "TDUMP1" then proxy.queries:append(proxy.COM_TABLE_DUMP, string.char(proxy.COM_TABLE_DUMP) .. "\004test\002t1", { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY elseif packet:sub(2) == "TDUMP2" then proxy.queries:append(proxy.COM_TABLE_DUMP, string.char(proxy.COM_TABLE_DUMP) .. "\004test\002t2", { resultset_is_needed = true }) return proxy.PROXY_SEND_QUERY end end --- -- adjust the response to match the needs of COM_QUERY -- where neccesary -- -- * some commands return EOF (COM_SHUTDOWN), -- * some are plain-text (COM_STATISTICS) -- -- in the end the client sent us a COM_QUERY and we have to hide -- all those specifics function read_query_result(inj) if inj.id == proxy.COM_SHUTDOWN or inj.id == proxy.COM_SET_OPTION or inj.id == proxy.COM_BINLOG_DUMP or inj.id == proxy.COM_STMT_PREPARE or inj.id == proxy.COM_STMT_FETCH or inj.id == proxy.COM_FIELD_LIST or inj.id == proxy.COM_TABLE_DUMP or inj.id == proxy.COM_DEBUG then -- translate the EOF packet from the COM_SHUTDOWN into a OK packet -- to match the needs of the COM_QUERY we got if inj.resultset.raw:byte() ~= 255 then proxy.response = { type = proxy.MYSQLD_PACKET_OK, } return proxy.PROXY_SEND_RESULT end elseif inj.id == proxy.COM_PING or inj.id == proxy.COM_TIME or inj.id == proxy.COM_PROCESS_KILL or inj.id == proxy.COM_REGISTER_SLAVE or inj.id == proxy.COM_STMT_EXECUTE or inj.id == proxy.COM_STMT_RESET or inj.id == proxy.COM_PROCESS_INFO then -- no change needed elseif inj.id == proxy.COM_STATISTICS then -- the response a human readable plain-text -- -- just turn it into a proper result-set proxy.response = { type = proxy.MYSQLD_PACKET_OK, resultset = { fields = { { name = "statisitics" } }, rows = { { inj.resultset.raw } } } } return proxy.PROXY_SEND_RESULT else -- we don't know them yet, just return ERR to the client to -- match the needs of COM_QUERY print(("got: %q"):format(inj.resultset.raw)) proxy.response = { type = proxy.MYSQLD_PACKET_ERR, } return proxy.PROXY_SEND_RESULT end end