Skip to content

Commit 77247a1

Browse files
authored
Merge pull request #4895 from sysown/v3.0-4861-mysql-logging_prepared_statement_parameters
[MYSQL] logging of prepared statement parameters #4861
2 parents eba0022 + 231622f commit 77247a1

File tree

7 files changed

+1950
-4
lines changed

7 files changed

+1950
-4
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ src/*pem
5050
binaries/*deb
5151
binaries/*rpm
5252
tools/eventslog_reader_sample
53+
tools/eventlog_reader_to_json
5354
src/proxysql-save.cfg
5455
src/*log*
5556

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# **ProxySQL Prepared Statement Parameter Tracking Reference Manual**
2+
This document describes the changes made to the logging of prepared statement parameters in ProxySQL.
3+
Two logging formats are supported:
4+
- **Binary Format (Format 1)** via `write_query_format_1()`
5+
- **JSON Format (Format 2)** via `write_query_format_2_json()` (also using `extractStmtExecuteMetadataToJson()`)
6+
## **1. Overview**
7+
ProxySQL logs query events in two different formats.
8+
For prepared statements (i.e. `COM_STMT_EXECUTE` events), the logging functions now log extra parameter details.
9+
Both formats encode the number of parameters and, for each parameter, log its type and a human-readable string value, with special handling for `NULL` values.
10+
## **2. Binary Format Details (**`write_query_format_1()`**)**
11+
When logging a prepared statement execution in binary format, the following steps occur:
12+
- **Encoded Basic Query Data:**
13+
Standard fields such as event type, thread ID, username, schema name, client/server info, timestamps, query digest, and the query text itself are encoded using a variable-length encoding scheme.
14+
- **Logging Prepared Statement Parameters:**
15+
If the event type is `PROXYSQL_COM_STMT_EXECUTE` and valid statement metadata is present:
16+
17+
- The number of parameters is encoded using `mysql_encode_length()`.
18+
- A **null bitmap** is constructed with one bit per parameter (set when the parameter is `NULL`).
19+
- For each parameter:
20+
- Two bytes are written to store the parameter's type.
21+
- If the parameter is not `NULL`, the function `getValueForBind()` is used to convert the binary data into a human-readable string.
22+
- The length of the converted value is encoded (again using `mysql_encode_length()`), and then the raw bytes of the converted value are written.
23+
- **Sequential Write Process:**
24+
All the fields are written sequentially. First, the total length of the event record is written (as a fixed-size 8-byte field), then each encoded field and the additional prepared statement parameter details.
25+
## **3. JSON Format Details (**`write_query_format_2_json()`**)**
26+
In JSON format, the logging function produces a JSON object that includes all standard query details plus additional fields for prepared statement parameters:
27+
- **Base JSON Object Information:**
28+
The JSON object includes key fields such as thread_id, username, schemaname, client, server information (if available), timestamps (starttime, endtime), duration, digest, etc.
29+
- **Event Type:**
30+
Depending on the event type, the field `"event"` is set to `"COM_STMT_EXECUTE"` (or `"COM_STMT_PREPARE"` / `"COM_QUERY"`) accordingly.
31+
- **Logging Prepared Statement Parameters:**
32+
If the event type is `PROXYSQL_COM_STMT_EXECUTE` and the session contains valid statement metadata:
33+
34+
- The helper function `extractStmtExecuteMetadataToJson(json &j)` is called.
35+
- This function iterates over each parameter:
36+
- For `NULL` parameters, it logs `"type": "NULL"` and `"value": null`.
37+
- For non-`NULL` parameters, it uses getValueForBind() to obtain the parameter type (e.g., `"INT24"`, `"VARCHAR"`) and its string representation.
38+
- The result is a JSON array attached to the key `"parameters"`, where each element is an object with keys `"type"` and `"value"`.
39+
- **Output:**
40+
Finally the JSON object is dumped as a single line in the log file using `j.dump()`.
41+
## **4. Detailed Example**
42+
For example, consider a prepared statement with two parameters. The resulting JSON log might look like:
43+
44+
```json
45+
{
46+
"hostgroup_id": 3,
47+
"thread_id": 12345,
48+
"event": "COM_STMT_EXECUTE",
49+
"username": "dbuser",
50+
"schemaname": "production",
51+
"client": "192.0.2.1",
52+
"server": "10.0.0.5",
53+
"rows_affected": 1,
54+
"query": "INSERT INTO foo (bar1, bar2) VALUES (?, ?)",
55+
"starttime_timestamp_us": 1617181920000000,
56+
"starttime": "2021-03-31 12:32:00.000000"
57+
"endtime_timestamp_us": 1617181921000000,
58+
"endtime": "2021-03-31 12:32:01.000000",
59+
"duration_us": 1000000,
60+
"digest": "0x0000000000000001", "client_stmt_id": 101,
61+
"parameters": [
62+
{
63+
"type": "INT24",
64+
"value": "42"
65+
},
66+
{
67+
"type": "VARCHAR",
68+
"value": "example value"
69+
}
70+
]
71+
}
72+
```
73+
74+
This JSON structure clearly shows each parameter’s type and value, which aids in debugging and analysis.
75+
## **5. Usage Considerations**
76+
- **Debuggability:**
77+
The enhanced logging allows administrators to see the full details of the parameters that are bound to a prepared statement. This is crucial for troubleshooting and performance analysis.
78+
- **Variable-Length Encoding:**
79+
Both binary and JSON formats rely on helper functions (`mysql_encode_length()` and `getValueForBind()`) to ensure that variable-length integers and parameter values are handled efficiently and unambiguously.
80+
- **Thread Safety and Performance:**
81+
The logging functions acquire write locks just before writing to disk to minimize contention. The design ensures that logging does not adversely impact performance while retaining detailed information.
82+
## **6. Summary**
83+
The changes in both `write_query_format_1()` and `write_query_format_2_json()` provide a comprehensive way to log prepared statement parameters:
84+
- In **binary format**, parameters are logged with a parameter count, a null bitmap, a two-byte parameter type, and then a variable-length encoded value.
85+
- In **JSON format**, parameters appear as an array of objects under the `"parameters"` key, with each object containing `"type"` and `"value"` fields.
86+
87+
These enhancements aim to improve troubleshooting capabilities and ensure that all necessary information is captured for later analysis.

include/MySQL_Logger.hpp

+16-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
#include "cpp.h"
55
#include <atomic>
66

7+
#ifndef PROXYJSON
8+
#define PROXYJSON
9+
#include "../deps/json/json_fwd.hpp"
10+
#endif // PROXYJSON
11+
712
#define PROXYSQL_LOGGER_PTHREAD_MUTEX
813

914
class MySQL_Logger;
@@ -74,7 +79,8 @@ class MySQL_Event {
7479
uint64_t last_insert_id; ///< Last insert ID.
7580
uint64_t rows_sent; ///< Number of rows sent.
7681
uint32_t client_stmt_id; ///< Client statement ID.
77-
char* gtid; ///< GTID.
82+
char * gtid; ///< GTID.
83+
MySQL_Session *session; ///< track creating session
7884
char *errmsg; ///< Error message, if generated by ProxySQL (not if generated by the backend)
7985
unsigned int myerrno; ///< MySQL error number
8086

@@ -90,10 +96,11 @@ class MySQL_Event {
9096
* @param _query_digest The digest of the query.
9197
* @param _client The client address.
9298
* @param _client_len The length of the client address.
99+
* @param sess_ptr Pointer to the session that generated this logging event, if it exists
93100
*
94101
* This constructor initializes the MySQL_Event object with the provided parameters. It does not allocate memory for string members.
95102
*/
96-
MySQL_Event(log_event_type _et, uint32_t _thread_id, char* _username, char* _schemaname, uint64_t _start_time, uint64_t _end_time, uint64_t _query_digest, char* _client, size_t _client_len);
103+
MySQL_Event(log_event_type _et, uint32_t _thread_id, char* _username, char* _schemaname, uint64_t _start_time, uint64_t _end_time, uint64_t _query_digest, char* _client, size_t _client_len, MySQL_Session *sess_ptr = nullptr);
97104

98105
/**
99106
* @brief Copy constructor for the MySQL_Event class.
@@ -217,6 +224,13 @@ class MySQL_Event {
217224
*/
218225
void set_errmsg(const unsigned int _myerrno, const char * _errmsg);
219226

227+
/**
228+
* @brief for STMT_EXECUTE, return the parameters used
229+
* @param j A JSON object where it will add the parameters
230+
* @return nothing
231+
*/
232+
void extractStmtExecuteMetadataToJson(nlohmann::json &j);
233+
220234
/**
221235
* @brief Declares MySQL_Logger as a friend class, granting it access to private members of MySQL_Event.
222236
*/

0 commit comments

Comments
 (0)