Since I am now finally getting to play with MySQL Proxy, I am going to outline some recipes here that I have found/created/modified that may be useful to someone other than me. This is a recipe for profiling queries. It writes the information to the PROXY_LOG_FILE currently name mysql.log. It is a file that will be created in the directory that you run mysql-proxy from. The file itself is mostly commented and should therefore be pretty self-explanatory. It was adapted from the reference documentation listed at the bottom of this entry.
assert(proxy.PROXY_VERSION >= 0x00600, "You need to be running mysql-proxy 0.6.0 or greater") -- Set up the log file local log_file = os.getenv("PROXY_LOG_FILE") if (log_file == nil) then log_file = "mysql.log" end -- Open up our log file in append mode local fh = io.open(log_file, "a+") -- Set some variables local original_query = "" local executed_query = "" local replace = false local comment = "" -- For profilign local profile = 0 -- Push the query onto the Queue function read_query( packet ) if string.byte(packet) == proxy.COM_QUERY then query = string.sub(packet, 2) -- Pull out the comment and store it original_query = string.gsub(query, "%s*%*%*%*%s(.+)%s%*%*%*%s*",'') comment = string.match(query, "%s*%*%*%*%s(.+)%s%*%*%*%s*") -- Add the original packet to the query if we have a comment if (comment) then if string.match(string.upper(comment), '%s*PROFILE') then -- Profile types: -- ALL, BLOCK IO, CONTEXT SWITCHES, IPC, -- MEMORY, PAGE FAULTS, SOURCE, SWAPS -- Set it up for profiling -- Generate a new COM_QUERY packet -- and inject it with a new id (11) original_query = "SET PROFILING=1; " .. original_query end -- End if (PROFILE) end -- End if (comment) executed_query = original_query -- Check for a 'SELECT' typo if string.match(string.upper(original_query), '^%s*SLECT') then executed_query = string.gsub(original_query,'^%s*%w+', 'SELECT') replace = true; -- matches "CD" as first word of the query elseif string.match(string.upper(original_query), '^%s*CD') then executed_query = string.gsub(original_query,'^%s*%w+', 'USE') replace = true end -- Postpend the other profiling strings to the query if (comment and string.match(string.upper(comment), '%s*PROFILE')) then executed_query = executed_query .. "; SHOW " .. comment end -- Actually execute the query here proxy.queries:append(1, string.char(proxy.COM_QUERY) .. executed_query ) return proxy.PROXY_SEND_QUERY else executed_query = "" end end function read_query_result (inj) local res = assert(inj.resultset) local num_cols = string.byte(res.raw, 1) if num_cols > 0 and num_cols < 255 then for row in inj.resultset.rows do row_count = row_count + 1 end end -- Prepend the error tag in the log local error_status = "" if res.query_status and (res.query_status < 0) then error_status = "[ERROR]" end -- Gets the rows affected by the actual query local row_count = 0 if (res.affected_rows) then row_count = res.affected_rows end -- Prepend the comment line in the log if (comment) then fh:write( string.format("%s %6d -- [COMMENT] %s\n", os.date('%Y-%m-%d %H:%M:%S'), proxy.connection.server["thread_id"], comment)) end -- Prepend the typo in the log if (replace) then fh:write( string.format("%s %6d -- [REPLACEMENT] %s\n\t\t\t%s\n", os.date('%Y-%m-%d %H:%M:%S'), proxy.connection.server["thread_id"], ("replaced " .. original_query), ("with " .. executed_query))) end -- Write the query adding the number of rows retrieved and query time fh:write( string.format("%s %6d -- %s %s {%d rows} {%d ms}\n", os.date('%Y-%m-%d %H:%M:%S'), proxy.connection.server["thread_id"], error_status, executed_query, row_count, inj.query_time) ) fh:flush() end
To make this work, simply append 3 asterisks 'PROFILE <profile_type>' and then 3 more asterisks and you will have the profile information returned to you in your query:
1 | *** PROFILE ALL *** SELECT * FROM foo_bar; |
2 tables will be returned. Your results and then the profile of your results.
Reference: Writing LUA Scripts for MySQL Proxy