modify resultset in read_query_result

Asked by Mat

I hope to modify the query result in function read_query_result(), is it possible to do this?

I use the follow lua code block to send back result, but this only work in read_query() and read_query_result() only got empty set.

================================================================
                proxy.response.type = proxy.MYSQLD_PACKET_OK
                proxy.response.resultset = {
                        fields = {
                                { type = proxy.MYSQL_TYPE_INT24, name = "sn" },
                                { type = proxy.MYSQL_TYPE_STRING, name = "name" },
                                { type = proxy.MYSQL_TYPE_STRING, name = "address" }
                        },
                        rows = {
                                { 1, "Mat", "kaohsiung" },
                                { 2, "taM", "taipei" }
                        }
                }

                return proxy.PROXY_SEND_RESULT
================================================================

The complete code is here:
http://pastebin.com/dRzkuyjd

I really appreciate that if someone could tell me this is possible or not, or give me some urls/example code, to find the solution.
Thank you very much!

Question information

Language:
English Edit question
Status:
Solved
For:
MySQL Proxy Edit question
Assignee:
No assignee Edit question
Solved by:
bkersten
Solved:
Last query:
Last reply:

This question was reopened

  • by Mat
Revision history for this message
bkersten (ben-kersten) said :
#1

I just happened to have discovered this very same bug yesterday. I call it a bug because there is nothing I can see in documentation that suggests this behavior is a 'feature'.

Here is the code snippet I used to show the bug.
========================================================
--
-- When more than one query is appended to the proxy query queue we lose the ability to write responses
-- This happens for both resultsets and error messages
--

function read_query(packet)

 if packet:byte() ~= proxy.COM_QUERY then
  return
 end

 local cmd = string.sub(packet, 2, string.len("test bug")+1)

 if string.lower(cmd) == "test bug" then
  proxy.queries:append(1, string.char(proxy.COM_QUERY) .. "SELECT NOW()", {resultset_is_needed = true}) -- comment out this line and it works
  proxy.queries:append(2, string.char(proxy.COM_QUERY) .. "SELECT NOW()", {resultset_is_needed = true})

  return proxy.PROXY_SEND_QUERY
 end
end

function read_query_result(inj)

 if inj.id then
  print("received result of query: " .. inj.id .. " " .. inj.resultset.query_status)
 end

 if inj.id == 2 then
  print("returning packet")

  --[[ TEST ERROR MESSAGE
  proxy.response = {
   ['type'] = proxy.MYSQLD_PACKET_ERR,
   ['errmsg'] = "why is this happening to me!"
  }
  --]]

  --[[ TEST RESULTSET ]]
  proxy.response = {
   ['resultset'] = {
    ['rows'] = {
     [1] = {
      [1] = 'hammer',
      [2] = 'bobs hardware',
      [3] = 1
     },
     [2] = {
      [1] = 'jigsaw',
      [2] = 'handy hardware',
      [3] = 3
     },
     [3] = {
      [1] = 'measuring tape',
      [2] = 'johns warehouse',
      [3] = 2
     }
    },
    ['fields'] = {
     [1] = {
      ['name'] = 'product_name',
      ['type'] = proxy.MYSQL_TYPE_STRING
     },
     [2] = {
      ['name'] = 'distributor_name',
      ['type'] = proxy.MYSQL_TYPE_STRING
     },
     [3] = {
      ['name'] = 'distributor_id',
      ['type'] = proxy.MYSQL_TYPE_LONG
     }
    }
   },
   ['type'] = MYSQLD_PACKET_OK
  }
  --]]

  return proxy.PROXY_SEND_RESULT
 end

 return proxy.PROXY_IGNORE_RESULT
end

========================================================

Revision history for this message
bkersten (ben-kersten) said :
#2

wow that formatted badly

Revision history for this message
bkersten (ben-kersten) said :
#3

Also just so you know the client completely loses its connection to the proxy when the resultset/errmsg is attempted to be returned.

Revision history for this message
bkersten (ben-kersten) said :
#4

typo in my code above

['type'] = MYSQLD_PACKET_OK

should be

['type'] = proxy.MYSQLD_PACKET_OK

doesn't impact on the bug I'm seeing

Revision history for this message
bkersten (ben-kersten) said :
#5

I have traced the behavior and it appears that after the packet is written to chunks and passed to writev it then loses its connection to the client. This may be an issue with the mysql client (my version is 5.1.48) or the packet we are sending to the client.

I checked out todays version 0.8.1 and traced through to these lines with everything that proceeded them looking alright.

File: network-socket.c
Line 548: len = writev(con->fd, iov, chunk_count); // writes packet data to file descriptor
Line 549: os_errno = errno; // this now contains 4 (EINTR)
Line 597: return NETWORK_SOCKET_SUCCESS; // send looks like it worked

File: network-mysqld.c
Line 860: if (ioctl(event_fd, FIONREAD, &b)) { // this returns zero
Line 891: con->state = CON_STATE_CLOSE_CLIENT; // this sets mysql-proxy up to close the connection

On the client side there is an error:
ERROR 2013 (HY000): Lost connection to MySQL server during query

Revision history for this message
Mat (matlinuxer2) said :
#6

After some more testing, I found some some pattern.
In read_query_result(), if there is a PROXY_IGNORE_RESULT before PROXY_SEND_RESULT, the exception do occur and client disconnect.

For example: ( [1],[2],[3] means order of inj.id )

[1] => PROXY_SEND_RESULT
[2] => PROXY_IGNORE_RESULT
[3] => PROXY_IGNORE_RESULT
ok

[1] => PROXY_IGNORE_RESULT
[2] => PROXY_IGNORE_RESULT
[3] =>
ok

[1] => PROXY_IGNORE_RESULT
[2] => PROXY_SEND_RESULT
[3] => PROXY_IGNORE_RESULT
error

[1] => PROXY_IGNORE_RESULT
[2] => PROXY_IGNORE_RESULT
[3] => PROXY_SEND_RESULT
error

I guess some is some condition conflicts between PROXY_IGNORE_RESULT and PROXY_SEND_RESULT in read_query_result()'s hook implementation.

hope the infomation helps.

Revision history for this message
Mat (matlinuxer2) said :
#7

After some more testing, I found some some pattern.
In read_query_result(), if there is a PROXY_IGNORE_RESULT before PROXY_SEND_RESULT, the exception do occur and client disconnect.

For example: ( [1],[2],[3] means order of inj.id )

[1] => PROXY_SEND_RESULT
[2] => PROXY_IGNORE_RESULT
[3] => PROXY_IGNORE_RESULT
ok

[1] => PROXY_IGNORE_RESULT
[2] => PROXY_IGNORE_RESULT
[3] =>
ok

[1] => PROXY_IGNORE_RESULT
[2] => PROXY_SEND_RESULT
[3] => PROXY_IGNORE_RESULT
error

[1] => PROXY_IGNORE_RESULT
[2] => PROXY_IGNORE_RESULT
[3] => PROXY_SEND_RESULT
error

I guess some is some condition conflicts between PROXY_IGNORE_RESULT and PROXY_SEND_RESULT in read_query_result()'s hook implementation.

hope the infomation helps.

Revision history for this message
Mat (matlinuxer2) said :
#8

sorry, I press the wrong button, and mark it solved. ( it doesn't yet... )

Revision history for this message
Best bkersten (ben-kersten) said :
#9

Ok I think I've fixed it.

Comparing the logs when running my sample script, once as listed above and again with query 1 commented out, I can see that the final resultset packets have differing packet ids. The working resultset packets begins at 1 and the buggy packets begins at 0. This is because there is one more call to network_mysqld_queue_reset after the the PROXY_IGNORE_RESULT. I don't understand how the working version gets to a state where its id is 1??? It makes no sense since there was a single call to network_mysqld_queue_reset without a follow up setting of sock->last_packet_id to 0xff in network_mysqld_queue_append. The buggy version however calls network_mysqld_queue_reset one additional time before entering network_mysqld_queue_append.

I don't like the solution but I have found that checking the return value of proxy_lua_read_query_result in proxy_read_query_result and skipping network_mysqld_queue_reset(send_sock); when we receive PROXY_IGNORE_RESULT works.

File: proxy-plugin.c
Lines: 1364-1371

NETWORK_MYSQLD_CON_TRACK_TIME(con, "proxy::ready_query_result::enter_lua");
network_mysqld_lua_stmt_ret ret = proxy_lua_read_query_result(con);
NETWORK_MYSQLD_CON_TRACK_TIME(con, "proxy::ready_query_result::leave_lua");

if (ret != PROXY_IGNORE_RESULT) {
    /** recv_sock might be != con->server now */
    network_mysqld_queue_reset(send_sock);
}

With that you should find the merge select sample in the documentation will actually work :)

Revision history for this message
bkersten (ben-kersten) said :
#10

And the patch :)

=== modified file 'plugins/proxy/proxy-plugin.c'
--- plugins/proxy/proxy-plugin.c 2010-07-29 11:52:52 +0000
+++ plugins/proxy/proxy-plugin.c 2010-08-26 04:22:17 +0000
@@ -1361,11 +1361,13 @@
   network_mysqld_queue_reset(recv_sock);

   NETWORK_MYSQLD_CON_TRACK_TIME(con, "proxy::ready_query_result::enter_lua");
- proxy_lua_read_query_result(con);
+ network_mysqld_lua_stmt_ret ret = proxy_lua_read_query_result(con);
   NETWORK_MYSQLD_CON_TRACK_TIME(con, "proxy::ready_query_result::leave_lua");

- /** recv_sock might be != con->server now */
- network_mysqld_queue_reset(send_sock);
+ if (ret != PROXY_IGNORE_RESULT) {
+ /** recv_sock might be != con->server now */
+ network_mysqld_queue_reset(send_sock);
+ }

   /**
    * if the send-queue is empty, we have nothing to send

Revision history for this message
Mat (matlinuxer2) said :
#11

Verified!

I just apply the patch from your last two replies, rebuild+ test, and IT DO WORK!!!
Would you like to submit your patch to upstream..
I am not related to mysql-proxy, but I do looking forward your patch adopted, and everything got better :-D

p.s. some of my related package seed and test files are stored here:
http://code.google.com/p/gentoo-taiwan/source/browse/trunk/dev-db/mysql-proxy/
http://code.google.com/p/gentoo-taiwan/source/browse/trunk/dev-db/mysql-proxy/files/fix_read_query_result_ignore.patch

Revision history for this message
Mat (matlinuxer2) said :
#12

Thanks bkersten, that solved my question.

Revision history for this message
Jan Kneschke (jan-kneschke) said :
#13

merged to the 0.8 tree and trunk with a extra test-case.