FFTO: PgSQL CommandComplete tag parser extraction + unit tests#5515
FFTO: PgSQL CommandComplete tag parser extraction + unit tests#5515renecannao merged 5 commits intov3.0-5473from
Conversation
New files: - include/PgSQLCommandComplete.h: PgSQLCommandResult struct + parse_pgsql_command_complete() declaration - lib/PgSQLCommandComplete.cpp: implementation mirroring extract_pg_rows_affected() from PgSQLFFTO.cpp Parses PostgreSQL CommandComplete tags (INSERT/UPDATE/DELETE/SELECT/ FETCH/MOVE/COPY/MERGE) to extract row counts. Pure function with no FFTO or session dependencies.
17 test cases: DML commands (INSERT/UPDATE/DELETE/COPY/MERGE), SELECT commands (SELECT/FETCH/MOVE), no-row-count commands (CREATE TABLE/DROP INDEX/BEGIN), edge cases (null, empty, whitespace, INSERT with OID, large counts).
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request refactors the PostgreSQL Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds a PostgreSQL CommandComplete tag parser, MySQL protocol utility functions, unit tests, and Makefile updates to build and test the new components. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request successfully extracts the PostgreSQL CommandComplete tag parser into a new, unit-testable function parse_pgsql_command_complete. The new implementation is well-structured and the accompanying unit tests are comprehensive, covering a wide range of commands and edge cases. I have provided a suggestion to improve performance by using std::string_view to avoid heap allocations during parsing. As a follow-up, consider replacing the original extract_pg_rows_affected function in PgSQLFFTO.cpp with a call to this new shared function to eliminate code duplication.
| #include <string> | ||
| #include <cctype> | ||
| #include <cstdlib> |
There was a problem hiding this comment.
To improve performance and adopt modern C++ practices, it's better to use std::string_view and std::from_chars for parsing. This avoids heap allocations. Please update the includes accordingly. <string> and <cstdlib> will no longer be needed for the parsing logic if the next suggestion is applied.
| #include <string> | |
| #include <cctype> | |
| #include <cstdlib> | |
| #include <string_view> | |
| #include <cctype> | |
| #include <charconv> |
| std::string tag(reinterpret_cast<const char*>(payload + begin), len - begin); | ||
|
|
||
| // Extract command type (first token) | ||
| size_t first_space = tag.find(' '); | ||
| if (first_space == std::string::npos) return result; | ||
|
|
||
| std::string command = tag.substr(0, first_space); | ||
|
|
||
| if (command == "SELECT" || command == "FETCH" || command == "MOVE") { | ||
| result.is_select = true; | ||
| } else if (command != "INSERT" && command != "UPDATE" && | ||
| command != "DELETE" && command != "COPY" && | ||
| command != "MERGE") { | ||
| return result; // Unknown command, no row count | ||
| } | ||
|
|
||
| // Extract row count (last token) | ||
| size_t last_space = tag.rfind(' '); | ||
| if (last_space == std::string::npos || last_space + 1 >= tag.size()) return result; | ||
|
|
||
| const char *rows_str = tag.c_str() + last_space + 1; | ||
| char *endptr = nullptr; | ||
| unsigned long long rows = std::strtoull(rows_str, &endptr, 10); | ||
| if (endptr == rows_str || *endptr != '\0') return result; |
There was a problem hiding this comment.
This parsing logic can be optimized to avoid heap allocations from creating std::string objects. By using std::string_view, you can work with non-owning views of the payload. Additionally, std::from_chars is a more performant and safer alternative to strtoull for converting strings to numbers.
std::string_view tag(reinterpret_cast<const char*>(payload + begin), len - begin);
// Extract command type (first token)
size_t first_space = tag.find(' ');
if (first_space == std::string_view::npos) return result;
std::string_view command = tag.substr(0, first_space);
if (command == "SELECT" || command == "FETCH" || command == "MOVE") {
result.is_select = true;
} else if (command != "INSERT" && command != "UPDATE" &&
command != "DELETE" && command != "COPY" &&
command != "MERGE") {
return result; // Unknown command, no row count
}
// Extract row count (last token)
size_t last_space = tag.rfind(' ');
if (last_space == std::string_view::npos || last_space + 1 >= tag.size()) return result;
std::string_view rows_sv = tag.substr(last_space + 1);
unsigned long long rows;
auto res = std::from_chars(rows_sv.data(), rows_sv.data() + rows_sv.size(), rows);
if (res.ec != std::errc() || res.ptr != rows_sv.data() + rows_sv.size()) return result;There was a problem hiding this comment.
Pull request overview
This PR introduces a standalone, unit-testable PostgreSQL CommandComplete tag parser to support FFTO unit testing goals from #5499.
Changes:
- Added
parse_pgsql_command_complete()+PgSQLCommandResultas a pure parser implementation (include/+lib/). - Added a new unit test binary with 17 TAP assertions covering common commands and edge cases.
- Wired the new unit test and library object into the corresponding Makefiles.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
include/PgSQLCommandComplete.h |
Declares the pure CommandComplete tag parser API and result struct. |
lib/PgSQLCommandComplete.cpp |
Implements the parser logic (mirroring existing PgSQLFFTO parsing). |
test/tap/tests/unit/pgsql_command_complete_unit-t.cpp |
Adds unit tests validating rows/is_select extraction for multiple tag formats. |
test/tap/tests/unit/Makefile |
Registers the new unit test binary in UNIT_TESTS. |
lib/Makefile |
Adds the new parser object file to libproxysql.a. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| * Logic mirrors extract_pg_rows_affected() in PgSQLFFTO.cpp. | ||
| * |
There was a problem hiding this comment.
This new parser duplicates the existing CommandComplete parsing logic still implemented as extract_pg_rows_affected() in lib/PgSQLFFTO.cpp. That means the “extraction from PgSQLFFTO” described in the PR isn’t complete, and the two implementations can drift over time. Consider updating PgSQLFFTO to call parse_pgsql_command_complete() (or making extract_pg_rows_affected() a thin wrapper around it) and removing the duplicated parsing code so there is a single source of truth.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
lib/PgSQLCommandComplete.cpp (1)
50-55: Consider checking forstrtoulloverflow.
strtoullsetserrnotoERANGEwhen the value overflowsunsigned long long. While PostgreSQL row counts exceedingULLONG_MAXare unlikely in practice, checkingerrnowould make the parser more robust.🛡️ Optional overflow check
+#include <cerrno> +#include <climits> ... const char *rows_str = tag.c_str() + last_space + 1; char *endptr = nullptr; + errno = 0; unsigned long long rows = std::strtoull(rows_str, &endptr, 10); - if (endptr == rows_str || *endptr != '\0') return result; + if (endptr == rows_str || *endptr != '\0' || errno == ERANGE) return result;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/PgSQLCommandComplete.cpp` around lines 50 - 55, Before calling std::strtoull reset errno to 0, then call std::strtoull(rows_str, &endptr, 10) and after the call check errno for ERANGE; if errno == ERANGE treat it as an overflow (e.g., set rows to ULLONG_MAX or decide to skip setting result.rows) instead of silently accepting a truncated value. Update the block that parses rows_str (variables rows_str, endptr, rows) in PgSQLCommandComplete.cpp to perform the errno reset/check and handle the ERANGE case consistently with existing error handling.test/tap/tests/unit/pgsql_command_complete_unit-t.cpp (1)
52-61: Consider addingis_selectassertion for non-row-count commands.The
test_no_row_count()tests only verifyrows == 0but don't assertis_select == falsefor DDL commands likeCREATE TABLE,DROP INDEX, andBEGIN. This would improve consistency with other test functions that check both fields.🧪 Suggested enhancement
static void test_no_row_count() { auto r = parse("CREATE TABLE"); - ok(r.rows == 0, "CREATE TABLE → rows=0 (no row count)"); + ok(r.rows == 0 && r.is_select == false, "CREATE TABLE → rows=0, not select"); r = parse("DROP INDEX"); - ok(r.rows == 0, "DROP INDEX → rows=0"); + ok(r.rows == 0 && r.is_select == false, "DROP INDEX → rows=0, not select"); r = parse("BEGIN"); - ok(r.rows == 0, "BEGIN → rows=0 (single token, no space)"); + ok(r.rows == 0 && r.is_select == false, "BEGIN → rows=0, not select"); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/tap/tests/unit/pgsql_command_complete_unit-t.cpp` around lines 52 - 61, The test_no_row_count() unit test only asserts r.rows == 0 for parse("CREATE TABLE"), parse("DROP INDEX"), and parse("BEGIN") but doesn't assert r.is_select is false; update test_no_row_count() to also assert r.is_select == false for each parsed result (the result variable r returned by parse()) so non-select DDL/command cases are explicitly validated, keeping the existing rows assertions and using the same ok(...) style assertions used elsewhere.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@lib/PgSQLCommandComplete.cpp`:
- Around line 50-55: Before calling std::strtoull reset errno to 0, then call
std::strtoull(rows_str, &endptr, 10) and after the call check errno for ERANGE;
if errno == ERANGE treat it as an overflow (e.g., set rows to ULLONG_MAX or
decide to skip setting result.rows) instead of silently accepting a truncated
value. Update the block that parses rows_str (variables rows_str, endptr, rows)
in PgSQLCommandComplete.cpp to perform the errno reset/check and handle the
ERANGE case consistently with existing error handling.
In `@test/tap/tests/unit/pgsql_command_complete_unit-t.cpp`:
- Around line 52-61: The test_no_row_count() unit test only asserts r.rows == 0
for parse("CREATE TABLE"), parse("DROP INDEX"), and parse("BEGIN") but doesn't
assert r.is_select is false; update test_no_row_count() to also assert
r.is_select == false for each parsed result (the result variable r returned by
parse()) so non-select DDL/command cases are explicitly validated, keeping the
existing rows assertions and using the same ok(...) style assertions used
elsewhere.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 21c70c0d-b0cf-4b17-bc27-8a1921d4b1f8
📒 Files selected for processing (5)
include/PgSQLCommandComplete.hlib/Makefilelib/PgSQLCommandComplete.cpptest/tap/tests/unit/Makefiletest/tap/tests/unit/pgsql_command_complete_unit-t.cpp
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: CI-builds / builds (debian12,-dbg)
- GitHub Check: CI-builds / builds (ubuntu22,-tap)
- GitHub Check: Agent
- GitHub Check: run / trigger
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-01-20T09:34:19.124Z
Learnt from: yuji-hatakeyama
Repo: sysown/proxysql PR: 5307
File: test/tap/tests/reg_test_5306-show_warnings_with_comment-t.cpp:39-48
Timestamp: 2026-01-20T09:34:19.124Z
Learning: In ProxySQL's TAP test suite, resource leaks (e.g., not calling mysql_close() on early return paths) are commonly tolerated because test processes are short-lived and OS frees resources on exit. This pattern applies to all C++ test files under test/tap/tests. When reviewing, recognize this as a project-wide test convention and focus on test correctness and isolation rather than insisting on fixing such leaks in these test files.
Applied to files:
test/tap/tests/unit/pgsql_command_complete_unit-t.cpp
🪛 Clang (14.0.6)
test/tap/tests/unit/pgsql_command_complete_unit-t.cpp
[error] 11-11: 'tap.h' file not found
(clang-diagnostic-error)
include/PgSQLCommandComplete.h
[error] 14-14: 'cstdint' file not found
(clang-diagnostic-error)
🪛 Cppcheck (2.20.0)
test/tap/tests/unit/pgsql_command_complete_unit-t.cpp
[warning] 86-86: If memory allocation fails, then there is a possible null pointer dereference
(nullPointerOutOfMemory)
[warning] 31-31: If memory allocation fails, then there is a possible null pointer dereference
(nullPointerOutOfMemory)
🔇 Additional comments (6)
test/tap/tests/unit/Makefile (1)
237-237: LGTM!The new unit test target follows the established naming convention and is properly integrated with the existing build infrastructure.
lib/Makefile (1)
111-111: LGTM!The new object file is properly added to the build and follows the existing pattern for PostgreSQL-related modules.
test/tap/tests/unit/pgsql_command_complete_unit-t.cpp (2)
19-22: LGTM!The helper function is well-designed for the test context. The direct call to
parse_pgsql_command_complete()at line 65 for the nullptr test case correctly bypasses this helper to avoid undefined behavior fromstrlen(nullptr).
84-96: LGTM!Test structure is clean with proper
plan(), initialization, categorized test functions, cleanup, and exit status. The test count (17) correctly matches the sum of all assertions.include/PgSQLCommandComplete.h (1)
1-48: LGTM!Well-structured header with clear documentation, appropriate include guards, and a clean pure-function API. The use of
uint64_tfor row counts handles PostgreSQL's potential for very large result sets.lib/PgSQLCommandComplete.cpp (1)
16-57: LGTM!The implementation is clean and handles edge cases well:
- Null/empty input handling
- Whitespace trimming (leading and trailing)
- Command classification for SELECT-like vs DML
- Row count extraction from last token with proper validation
New: include/MySQLProtocolUtils.h, lib/MySQLProtocolUtils.cpp Functions: - mysql_read_lenenc_int(): reads MySQL length-encoded integers from raw buffers (mirrors read_lenenc_int in MySQLFFTO.cpp) - mysql_build_packet(): constructs MySQL wire-format packets (3-byte length + seq_id + payload) for crafting test data
36 test cases covering both MySQL and PgSQL FFTO protocol parsing: MySQL length-encoded integers (11 tests): - 1-byte, 2-byte, 3-byte, 8-byte values - Truncated buffers, empty input MySQL packet building (9 tests): - Normal, large (1000 bytes), empty packets - Header validation (length, seq_id, payload integrity) MySQL OK packet parsing (2 tests): - affected_rows extraction (1-byte and 2-byte lenenc) PgSQL CommandComplete extended (7 tests): - INSERT with OID, large row count, zero rows - All 8 command types in one sweep, 9 DDL commands - Null-terminated payload (real wire format) Fragmentation simulation (6 tests): - Truncated lenenc (partial data) - Multi-packet stream building and header verification
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
test/tap/tests/unit/ffto_protocol_unit-t.cpp (1)
223-224: Use exact-value assertion for fragmented lenenc decode.
ok(val > 0, ...)is too permissive; assert the exact decoded value to catch subtle decode regressions.✅ Stronger assertion
p = full; len = 4; val = mysql_read_lenenc_int(p, len); - ok(val > 0, "fragmented: full 3-byte lenenc decoded successfully"); + ok(val == 1000000, "fragmented: full 3-byte lenenc decoded as 1,000,000");🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/tap/tests/unit/ffto_protocol_unit-t.cpp` around lines 223 - 224, Replace the permissive positive check with an exact-value assertion: when calling mysql_read_lenenc_int(p, len) store the result in val and assert it equals the expected decoded length (use the test framework's equality assertion) instead of ok(val > 0,...). Update the test around mysql_read_lenenc_int, val and len to compare val to the known expected 3-byte length value to catch regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@lib/MySQLProtocolUtils.cpp`:
- Around line 11-41: The function mysql_read_lenenc_int currently advances the
cursor (buf/len) immediately after reading the prefix byte and then returns 0 on
insufficient trailing bytes, making truncation indistinguishable from a
legitimate zero and leaving the cursor advanced; fix this by validating that the
buffer has the required trailing bytes before mutating buf/len (or if you must
read the prefix first, restore buf/len to their original values on any early
return), and only advance buf and decrement len after you have confirmed enough
bytes for the selected length-encoded branch (ensure this change is applied in
mysql_read_lenenc_int for the 0xFC, 0xFD and 0xFE branches).
- Around line 50-59: The mysql_build_packet function currently writes only the
lower 24 bits of payload_len into the 3-byte header and blindly memcpy's
payload, which corrupts packets for payload_len > 0xFFFFFF and risks
dereferencing a null payload; add guard clauses at the start of
mysql_build_packet to (1) validate payload_len is <= 0xFFFFFF and reject/return
an error code (e.g., -1) if it is larger, and (2) ensure that if payload_len > 0
the payload pointer is non-null and otherwise return an error; do not perform
the header write or memcpy when the checks fail so out_buf, seq_id, and memcpy
are only used after validation.
---
Nitpick comments:
In `@test/tap/tests/unit/ffto_protocol_unit-t.cpp`:
- Around line 223-224: Replace the permissive positive check with an exact-value
assertion: when calling mysql_read_lenenc_int(p, len) store the result in val
and assert it equals the expected decoded length (use the test framework's
equality assertion) instead of ok(val > 0,...). Update the test around
mysql_read_lenenc_int, val and len to compare val to the known expected 3-byte
length value to catch regressions.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 83c88556-3ba1-4187-b833-ae29b63ba514
📒 Files selected for processing (5)
include/MySQLProtocolUtils.hlib/Makefilelib/MySQLProtocolUtils.cpptest/tap/tests/unit/Makefiletest/tap/tests/unit/ffto_protocol_unit-t.cpp
✅ Files skipped from review due to trivial changes (2)
- lib/Makefile
- test/tap/tests/unit/Makefile
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: CI-builds / builds (debian12,-dbg)
- GitHub Check: CI-builds / builds (ubuntu22,-tap)
- GitHub Check: run / trigger
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-01-20T09:34:19.124Z
Learnt from: yuji-hatakeyama
Repo: sysown/proxysql PR: 5307
File: test/tap/tests/reg_test_5306-show_warnings_with_comment-t.cpp:39-48
Timestamp: 2026-01-20T09:34:19.124Z
Learning: In ProxySQL's TAP test suite, resource leaks (e.g., not calling mysql_close() on early return paths) are commonly tolerated because test processes are short-lived and OS frees resources on exit. This pattern applies to all C++ test files under test/tap/tests. When reviewing, recognize this as a project-wide test convention and focus on test correctness and isolation rather than insisting on fixing such leaks in these test files.
Applied to files:
test/tap/tests/unit/ffto_protocol_unit-t.cpp
📚 Learning: 2026-01-20T07:40:34.938Z
Learnt from: yuji-hatakeyama
Repo: sysown/proxysql PR: 5307
File: test/tap/tests/reg_test_5306-show_warnings_with_comment-t.cpp:24-28
Timestamp: 2026-01-20T07:40:34.938Z
Learning: In ProxySQL test files, calling `mysql_error(NULL)` after `mysql_init()` failure is safe because the MariaDB client library implementation returns an empty string for NULL handles (not undefined behavior).
Applied to files:
test/tap/tests/unit/ffto_protocol_unit-t.cpp
🪛 Clang (14.0.6)
include/MySQLProtocolUtils.h
[error] 14-14: 'cstdint' file not found
(clang-diagnostic-error)
test/tap/tests/unit/ffto_protocol_unit-t.cpp
[error] 13-13: 'tap.h' file not found
(clang-diagnostic-error)
🪛 Cppcheck (2.20.0)
test/tap/tests/unit/ffto_protocol_unit-t.cpp
[warning] 86-86: If memory allocation fails, then there is a possible null pointer dereference
(nullPointerOutOfMemory)
[warning] 31-31: If memory allocation fails, then there is a possible null pointer dereference
(nullPointerOutOfMemory)
| uint64_t mysql_read_lenenc_int(const unsigned char* &buf, size_t &len) { | ||
| if (len == 0) return 0; | ||
| uint8_t first_byte = buf[0]; | ||
| buf++; len--; | ||
| if (first_byte < 0xFB) return first_byte; | ||
| if (first_byte == 0xFC) { | ||
| if (len < 2) return 0; | ||
| uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8); | ||
| buf += 2; len -= 2; | ||
| return value; | ||
| } | ||
| if (first_byte == 0xFD) { | ||
| if (len < 3) return 0; | ||
| uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8) | ||
| | (static_cast<uint64_t>(buf[2]) << 16); | ||
| buf += 3; len -= 3; | ||
| return value; | ||
| } | ||
| if (first_byte == 0xFE) { | ||
| if (len < 8) return 0; | ||
| uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8) | ||
| | (static_cast<uint64_t>(buf[2]) << 16) | ||
| | (static_cast<uint64_t>(buf[3]) << 24) | ||
| | (static_cast<uint64_t>(buf[4]) << 32) | ||
| | (static_cast<uint64_t>(buf[5]) << 40) | ||
| | (static_cast<uint64_t>(buf[6]) << 48) | ||
| | (static_cast<uint64_t>(buf[7]) << 56); | ||
| buf += 8; len -= 8; | ||
| return value; | ||
| } | ||
| return 0; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, locate and examine the MySQLProtocolUtils.cpp file
fd -type f -name "MySQLProtocolUtils.cpp" | head -20Repository: sysown/proxysql
Length of output: 229
🏁 Script executed:
#!/bin/bash
# Get file size and read the function
if [ -f "lib/MySQLProtocolUtils.cpp" ]; then
wc -l "lib/MySQLProtocolUtils.cpp"
echo "---"
head -50 "lib/MySQLProtocolUtils.cpp"
fiRepository: sysown/proxysql
Length of output: 1434
🏁 Script executed:
#!/bin/bash
# Search for all calls to mysql_read_lenenc_int to understand usage patterns
rg "mysql_read_lenenc_int" -A 3 -B 1Repository: sysown/proxysql
Length of output: 5679
🏁 Script executed:
#!/bin/bash
# Check if there are any retry or partial buffer handling patterns
rg "partial\|retry\|buffer.*fail\|truncat" -t cpp -i | head -40Repository: sysown/proxysql
Length of output: 41
🏁 Script executed:
#!/bin/bash
# Search for MySQL_Protocol.cpp and check the specific lines mentioned
fd -type f -name "MySQL_Protocol.cpp" | head -5Repository: sysown/proxysql
Length of output: 229
🏁 Script executed:
#!/bin/bash
# Look for broader usage context - search for patterns where mysql_read_lenenc_int is called
# to understand if callers manage cursor state or expect rollback
rg -A 5 -B 5 "mysql_read_lenenc_int" lib/ include/ --type cpp | head -100Repository: sysown/proxysql
Length of output: 1366
🏁 Script executed:
#!/bin/bash
# Check if there are any error handling patterns or decode status checks
# around lenenc parsing that might indicate retry expectations
rg "decode.*status\|malformed\|truncat" lib/ --type cpp -i | head -30Repository: sysown/proxysql
Length of output: 41
🏁 Script executed:
#!/bin/bash
# Read the full header file to see documentation and understand expected behavior
cat -n "include/MySQLProtocolUtils.h" | head -50Repository: sysown/proxysql
Length of output: 1898
🏁 Script executed:
#!/bin/bash
# Search for MySQL_Protocol.cpp mentioned in the review comment scratchpad
fd -type f -name "*Protocol*" | grep -i mysqlRepository: sysown/proxysql
Length of output: 229
🏁 Script executed:
#!/bin/bash
# Look for actual parsing code that calls mysql_read_lenenc_int
# Search broader for protocol parsing and decode patterns
rg "mysql_read_lenenc_int" -B 10 -A 10Repository: sysown/proxysql
Length of output: 12681
🏁 Script executed:
#!/bin/bash
# Check for packet parsing and error handling contexts
rg "packet.*decode\|parse.*lenenc\|handle.*truncat" -i | head -40Repository: sysown/proxysql
Length of output: 41
🏁 Script executed:
#!/bin/bash
# Look at the fragmented test more carefully to understand retry expectations
sed -n '200,250p' "test/tap/tests/unit/ffto_protocol_unit-t.cpp"Repository: sysown/proxysql
Length of output: 2088
🏁 Script executed:
#!/bin/bash
# Search for any actual retry or partial buffer handling patterns in the codebase
rg "while.*len|retry|partial.*buffer" | head -30Repository: sysown/proxysql
Length of output: 2160
🏁 Script executed:
#!/bin/bash
# Check if there's actual usage in protocol parsing that demonstrates the issue
rg "mysql_read_lenenc_int" -B 15 -A 15 | grep -A 20 -B 20 "while\|retry\|if.*len\|buffer"Repository: sysown/proxysql
Length of output: 7466
Cursor advances before validating trailing bytes, creating ambiguous return and unsafe retry semantics.
The function consumes the prefix byte and advances buf/len before checking if the required trailing bytes are available. It then returns 0 for both valid-zero values and truncation failures, making them indistinguishable. This prevents callers from safely retrying on partial buffer conditions without external cursor management.
💡 Minimal fix: rollback cursor on failure
uint64_t mysql_read_lenenc_int(const unsigned char* &buf, size_t &len) {
- if (len == 0) return 0;
- uint8_t first_byte = buf[0];
- buf++; len--;
+ if (buf == nullptr || len == 0) return 0;
+ const unsigned char* orig_buf = buf;
+ size_t orig_len = len;
+ uint8_t first_byte = buf[0];
+ buf++; len--;
+ auto fail = [&]() -> uint64_t {
+ buf = orig_buf;
+ len = orig_len;
+ return 0;
+ };
if (first_byte < 0xFB) return first_byte;
if (first_byte == 0xFC) {
- if (len < 2) return 0;
+ if (len < 2) return fail();
uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8);
buf += 2; len -= 2;
return value;
}
if (first_byte == 0xFD) {
- if (len < 3) return 0;
+ if (len < 3) return fail();
uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8)
| (static_cast<uint64_t>(buf[2]) << 16);
buf += 3; len -= 3;
return value;
}
if (first_byte == 0xFE) {
- if (len < 8) return 0;
+ if (len < 8) return fail();
uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8)
| (static_cast<uint64_t>(buf[2]) << 16)
| (static_cast<uint64_t>(buf[3]) << 24)
| (static_cast<uint64_t>(buf[4]) << 32)
| (static_cast<uint64_t>(buf[5]) << 40)
| (static_cast<uint64_t>(buf[6]) << 48)
| (static_cast<uint64_t>(buf[7]) << 56);
buf += 8; len -= 8;
return value;
}
- return 0;
+ return fail();
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| uint64_t mysql_read_lenenc_int(const unsigned char* &buf, size_t &len) { | |
| if (len == 0) return 0; | |
| uint8_t first_byte = buf[0]; | |
| buf++; len--; | |
| if (first_byte < 0xFB) return first_byte; | |
| if (first_byte == 0xFC) { | |
| if (len < 2) return 0; | |
| uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8); | |
| buf += 2; len -= 2; | |
| return value; | |
| } | |
| if (first_byte == 0xFD) { | |
| if (len < 3) return 0; | |
| uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8) | |
| | (static_cast<uint64_t>(buf[2]) << 16); | |
| buf += 3; len -= 3; | |
| return value; | |
| } | |
| if (first_byte == 0xFE) { | |
| if (len < 8) return 0; | |
| uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8) | |
| | (static_cast<uint64_t>(buf[2]) << 16) | |
| | (static_cast<uint64_t>(buf[3]) << 24) | |
| | (static_cast<uint64_t>(buf[4]) << 32) | |
| | (static_cast<uint64_t>(buf[5]) << 40) | |
| | (static_cast<uint64_t>(buf[6]) << 48) | |
| | (static_cast<uint64_t>(buf[7]) << 56); | |
| buf += 8; len -= 8; | |
| return value; | |
| } | |
| return 0; | |
| uint64_t mysql_read_lenenc_int(const unsigned char* &buf, size_t &len) { | |
| if (buf == nullptr || len == 0) return 0; | |
| const unsigned char* orig_buf = buf; | |
| size_t orig_len = len; | |
| uint8_t first_byte = buf[0]; | |
| buf++; len--; | |
| auto fail = [&]() -> uint64_t { | |
| buf = orig_buf; | |
| len = orig_len; | |
| return 0; | |
| }; | |
| if (first_byte < 0xFB) return first_byte; | |
| if (first_byte == 0xFC) { | |
| if (len < 2) return fail(); | |
| uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8); | |
| buf += 2; len -= 2; | |
| return value; | |
| } | |
| if (first_byte == 0xFD) { | |
| if (len < 3) return fail(); | |
| uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8) | |
| | (static_cast<uint64_t>(buf[2]) << 16); | |
| buf += 3; len -= 3; | |
| return value; | |
| } | |
| if (first_byte == 0xFE) { | |
| if (len < 8) return fail(); | |
| uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8) | |
| | (static_cast<uint64_t>(buf[2]) << 16) | |
| | (static_cast<uint64_t>(buf[3]) << 24) | |
| | (static_cast<uint64_t>(buf[4]) << 32) | |
| | (static_cast<uint64_t>(buf[5]) << 40) | |
| | (static_cast<uint64_t>(buf[6]) << 48) | |
| | (static_cast<uint64_t>(buf[7]) << 56); | |
| buf += 8; len -= 8; | |
| return value; | |
| } | |
| return fail(); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/MySQLProtocolUtils.cpp` around lines 11 - 41, The function
mysql_read_lenenc_int currently advances the cursor (buf/len) immediately after
reading the prefix byte and then returns 0 on insufficient trailing bytes,
making truncation indistinguishable from a legitimate zero and leaving the
cursor advanced; fix this by validating that the buffer has the required
trailing bytes before mutating buf/len (or if you must read the prefix first,
restore buf/len to their original values on any early return), and only advance
buf and decrement len after you have confirmed enough bytes for the selected
length-encoded branch (ensure this change is applied in mysql_read_lenenc_int
for the 0xFC, 0xFD and 0xFE branches).
| // 3-byte length (little-endian) + 1-byte sequence | ||
| out_buf[0] = payload_len & 0xFF; | ||
| out_buf[1] = (payload_len >> 8) & 0xFF; | ||
| out_buf[2] = (payload_len >> 16) & 0xFF; | ||
| out_buf[3] = seq_id; | ||
| if (payload && payload_len > 0) { | ||
| memcpy(out_buf + 4, payload, payload_len); | ||
| } | ||
| return payload_len + 4; | ||
| } |
There was a problem hiding this comment.
❓ Verification inconclusive
Script executed:
find . -name "MySQLProtocolUtils.cpp" -type fRepository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
./lib/MySQLProtocolUtils.cpp
Script executed:
git ls-files | grep -i mysqlRepository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
.github/workflows/CI-3p-aiomysql.yml
.github/workflows/CI-3p-mysql-connector-j.yml
.github/workflows/CI-3p-php-pdo-mysql.yml
RAG_POC/sample_mysql.sql
deps/mariadb-client-library/mysql.h.patch
doc/internal/MySQL_Connection.md
doc/internal/MySQL_Logging_Prepared_Statements_Parameters.md
doc/internal/MySQL_Session.md
docker/1backend-complex-config/mysql/Dockerfile
docker/1backend-complex-config/mysql/import_schema.sh
docker/1backend-complex-config/mysql/schema.sql
docker/images/mysql/mysql-empty/Dockerfile
docker/images/mysql/mysql-simple-dump/Dockerfile
docker/images/mysql/mysql-simple-dump/import_schema.sh
docker/images/mysql/mysql-simple-dump/schema.sql
docker/scenarios/repl1/mysql.sh
docker/scenarios/repl1/mysql_add.cnf_
include/MySQLFFTO.hpp
include/MySQLProtocolUtils.h
include/MySQL_Authentication.hpp
include/MySQL_Catalog.h
include/MySQL_Data_Stream.h
include/MySQL_FTS.h
include/MySQL_HostGroups_Manager.h
include/MySQL_LDAP_Authentication.hpp
include/MySQL_Logger.hpp
include/MySQL_Monitor.hpp
include/MySQL_PreparedStatement.h
include/MySQL_Prepared_Stmt_info.h
include/MySQL_Protocol.h
include/MySQL_Query_Cache.h
include/MySQL_Query_Processor.h
include/MySQL_Session.h
include/MySQL_Set_Stmt_Parser.h
include/MySQL_Thread.h
include/MySQL_Tool_Handler.h
include/MySQL_Variables.h
include/MySQL_encode.h
include/mysql_backend.h
include/mysql_connection.h
lib/MySQLFFTO.cpp
lib/MySQLProtocolUtils.cpp
lib/MySQL_Authentication.cpp
lib/MySQL_Catalog.cpp
lib/MySQL_FTS.cpp
lib/MySQL_HostGroups_Manager.cpp
lib/MySQL_Logger.cpp
lib/MySQL_Monitor.cpp
lib/MySQL_PreparedStatement.cpp
lib/MySQL_Protocol.cpp
lib/MySQL_Query_Cache.cpp
lib/MySQL_Query_Processor.cpp
lib/MySQL_ResultSet.cpp
lib/MySQL_Session.cpp
lib/MySQL_Set_Stmt_Parser.cpp
lib/MySQL_Thread.cpp
lib/MySQL_Tool_Handler.cpp
lib/MySQL_Variables.cpp
lib/MySQL_encode.cpp
lib/mysql_backend.cpp
lib/mysql_connection.cpp
lib/mysql_data_stream.cpp
scripts/add_mysql_user.sh
scripts/copy_stackexchange_Posts_mysql_to_sqlite3.py
test-scripts/deps/mysqlbinlog
test/afl_digest_test/afl_mysql_query_digest.cpp
test/deps/mysql-8.4.0-clang17.patch
test/deps/mysql-connector-c-8.4.0/mysql-connector-c
test/deps/mysql-connector-c/mysql-connector-c
test/infra/infra-mariadb10/bin/docker-mysql-post.bash
test/infra/infra-mariadb10/conf/mysql/addition_to_sys.sql
test/infra/infra-mariadb10/conf/mysql/mysql1/my.cnf
test/infra/infra-mariadb10/conf/mysql/mysql2/my.cnf
test/infra/infra-mariadb10/conf/mysql/mysql3/my.cnf
test/infra/infra-mysql57-binlog/.env
test/infra/infra-mysql57-binlog/bin/docker-mysql-post.bash
test/infra/infra-mysql57-binlog/bin/docker-orchestrator-post.bash
test/infra/infra-mysql57-binlog/bin/docker-proxy-post.bash
test/infra/infra-mysql57-binlog/conf/mysql/mysql1/my.cnf
test/infra/infra-mysql57-binlog/conf/mysql/mysql2/my.cnf
test/infra/infra-mysql57-binlog/conf/mysql/mysql3/my.cnf
test/infra/infra-mysql57-binlog/conf/orchestrator/orc1/orchestrator.conf.json
test/infra/infra-mysql57-binlog/conf/orchestrator/orc2/orchestrator.conf.json
test/infra/infra-mysql57-binlog/conf/orchestrator/orc3/orchestrator.conf.json
test/infra/infra-mysql57-binlog/conf/orchestrator/remove-proxysql-host.bash
test/infra/infra-mysql57-binlog/conf/proxysql/infra-config.sql
test/infra/infra-mysql57-binlog/docker-compose-destroy.bash
test/infra/infra-mysql57-binlog/docker-compose-init.bash
test/infra/infra-mysql57-binlog/docker-compose.yml
test/infra/infra-mysql57/.env
test/infra/infra-mysql57/bin/docker-mysql-post.bash
test/infra/infra-mysql57/bin/docker-orchestrator-post.bash
test/infra/infra-mysql57/bin/docker-proxy-post.bash
test/infra/infra-mysql57/bin/local-docker-benchmark.bash
test/infra/infra-mysql57/conf/mysql/mysql1/my.cnf
test/infra/infra-mysql57/conf/mysql/mysql2/my.cnf
test/infra/infra-mysql57/conf/mysql/mysql3/my.cnf
test/infra/infra-mysql57/conf/mysql/ssl/configure_ssl.sh
test/infra/infra-mysql57/conf/orchestrator/orc1/orchestrator.conf.json
test/infra/infra-mysql57/conf/orchestrator/orc2/orchestrator.conf.json
test/infra/infra-mysql57/conf/orchestrator/orc3/orchestrator.conf.json
test/infra/infra-mysql57/conf/orchestrator/remove-proxysql-host.bash
test/infra/infra-mysql57/conf/proxysql/infra-config.sql
test/infra/infra-mysql57/docker-compose-destroy.bash
test/infra/infra-mysql57/docker-compose-init.bash
test/infra/infra-mysql57/docker-compose.yml
test/infra/infra-mysql84/.env
test/infra/infra-mysql84/bin/docker-mysql-post.bash
test/infra/infra-mysql84/bin/docker-orchestrator-post.bash
test/infra/infra-mysql84/bin/docker-proxy-post.bash
test/infra/infra-mysql84/bin/local-docker-benchmark.bash
test/infra/infra-mysql84/conf/mysql/mysql1/my.cnf
test/infra/infra-mysql84/conf/mysql/mysql2/my.cnf
test/infra/infra-mysql84/conf/mysql/mysql3/my.cnf
test/infra/infra-mysql84/conf/mysql/ssl/configure_ssl.sh
test/infra/infra-mysql84/conf/orchestrator/orc1/orchestrator.conf.json
test/infra/infra-mysql84/conf/orchestrator/orc2/orchestrator.conf.json
test/infra/infra-mysql84/conf/orchestrator/orc3/orchestrator.conf.json
test/infra/infra-mysql84/conf/orchestrator/remove-proxysql-host.bash
test/infra/infra-mysql84/conf/proxysql/infra-config.sql
test/infra/infra-mysql84/docker-compose-destroy.bash
test/infra/infra-mysql84/docker-compose-init.bash
test/infra/infra-mysql84/docker-compose.yml
test/scripts/deps/mysqlbinlog
test/tap/groups/ai/mysql-seed.sql
test/tap/groups/mysql-auto_increment_delay_multiplex=0/pre-proxysql.sql
test/tap/groups/mysql-multiplexing=false/pre-proxysql.sql
test/tap/groups/mysql-query_digests=0/pre-proxysql.sql
test/tap/groups/mysql-query_digests_keep_comment=1/pre-proxysql.sql
test/tap/groups/mysql84-gr/infras.lst
test/tap/groups/mysql84-gr/pre-proxysql.bash
test/tap/groups/mysql84/env.sh
test/tap/groups/mysql84/infras.lst
test/tap/groups/mysql84/pre-proxysql.bash
test/tap/groups/mysql90-gr/infras.lst
test/tap/groups/mysql90-gr/pre-proxysql.bash
test/tap/groups/mysql90/infras.lst
test/tap/groups/mysql90/pre-proxysql.bash
test/tap/groups/mysql91-gr/infras.lst
test/tap/groups/mysql91-gr/pre-proxysql.bash
test/tap/groups/mysql91/infras.lst
test/tap/groups/mysql91/pre-proxysql.bash
test/tap/groups/mysql92-gr/infras.lst
test/tap/groups/mysql92-gr/pre-proxysql.bash
test/tap/groups/mysql92/infras.lst
test/tap/groups/mysql92/pre-proxysql.bash
test/tap/groups/mysql93-gr/infras.lst
test/tap/groups/mysql93-gr/pre-proxysql.bash
test/tap/groups/mysql93/infras.lst
test/tap/groups/mysql93/pre-proxysql.bash
test/tap/tests/mcp_mixed_mysql_pgsql_concurrency_stress-t.cpp
test/tap/tests/mcp_mysql_concurrency_stress-t.cpp
test/tap/tests/mysql-fast_forward-t.cpp
test/tap/tests/mysql-init_connect-1-t.cpp
test/tap/tests/mysql-init_connect-2-t.cpp
test/tap/tests/mysql-last_insert_id-t.cpp
test/tap/tests/mysql-mirror1-t.cpp
test/tap/tests/mysql-protocol_compression_level-t.cpp
test/tap/tests/mysql-reg_test_4707_threshold_resultset_size-t.cpp
test/tap/tests/mysql-reg_test_4716_single_semicolon-t.cpp
test/tap/tests/mysql-reg_test_4723_query_cache_stores_empty_result-t.cpp
test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp
test/tap/tests/mysql-select_version_without_backend-t.cpp
test/tap/tests/mysql-set_transaction-t.cpp
test/tap/tests/mysql-set_wait_timeout-t.cpp
test/tap/tests/mysql-show_ssl_version-t.cpp
test/tap/tests/mysql-sql_log_bin-error-t.cpp
test/tap/tests/mysql-test_malformed_packet-t.cpp
test/tap/tests/mysql-test_ssl_CA-t.cpp
test/tap/tests/mysql-watchdog_test-t.cpp
test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp
test/tap/tests/mysql_hostgroup_attributes_config_file-t.cpp
test/tap/tests/mysql_query_logging_memory-t.cpp
test/tap/tests/mysql_reconnect.cpp
test/tap/tests/mysql_stmt_send_long_data-t.cpp
test/tap/tests/mysql_stmt_send_long_data_large-t.cpp
test/tap/tests/reg_test_3606-mysql_warnings-t.cpp
test/tap/tests/reg_test_3992_fast_forward_malformed_packet-mysqlsh-t.cpp
test/tap/tests/reg_test_3992_fast_forward_malformed_packet-pymysql-t.py
test/tap/tests/reg_test_4399-stats_mysql_query_digest-t.cpp
test/tap/tests/reg_test_4402-mysql_fields-t.cpp
test/tap/tests/repro_3404-mysql_close_fd_leak.cpp
test/tap/tests/test_cluster_sync_mysql_servers-t.cpp
test/tap/tests/test_ffto_mysql-t.cpp
test/tap/tests/test_ldap_stats_mysql_users.cpp
test/tap/tests/test_mysql_connect_retries-t.cpp
test/tap/tests/test_mysql_connect_retries_delay-t.cpp
test/tap/tests/test_mysql_hostgroup_attributes-1-t.cpp
test/tap/tests/test_mysql_query_digests_stages-t.cpp
test/tap/tests/test_mysql_query_rules_fast_routing-t.cpp
test/tap/tests/test_mysqlsh-t.cpp
Script executed:
git ls-files lib/ | head -20Repository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
lib/AI_Features_Manager.cpp
lib/AI_Tool_Handler.cpp
lib/AI_Vector_Storage.cpp
lib/Admin_Bootstrap.cpp
lib/Admin_FlushVariables.cpp
lib/Admin_Handler.cpp
lib/Admin_Tool_Handler.cpp
lib/Anomaly_Detector.cpp
lib/BaseHGC.cpp
lib/BaseSrvList.cpp
lib/Base_HostGroups_Manager.cpp
lib/Base_Session.cpp
lib/Base_Thread.cpp
lib/Cache_Tool_Handler.cpp
lib/Chart_bundle_js.cpp
lib/ClickHouse_Authentication.cpp
lib/ClickHouse_Server.cpp
lib/Config_Tool_Handler.cpp
lib/Discovery_Schema.cpp
lib/GTID_Server_Data.cpp
Script executed:
git ls-files | grep -i protocolRepository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
doc/PROMETHEUS_PROTOCOL_LABELS.md
include/MySQLProtocolUtils.h
include/MySQL_Protocol.h
include/PgSQL_Protocol.h
include/proxy_protocol_info.h
lib/MySQLProtocolUtils.cpp
lib/MySQL_Protocol.cpp
lib/PgSQL_Protocol.cpp
lib/proxy_protocol_info.cpp
test/tap/tests/mysql-protocol_compression_level-t.cpp
test/tap/tests/pgsql-extended_query_protocol_query_rules_test-t.cpp
test/tap/tests/pgsql-extended_query_protocol_test-t.cpp
test/tap/tests/test_PROXY_Protocol-t.cpp
test/tap/tests/unit/ffto_protocol_unit-t.cpp
test/tap/tests/unit/protocol_unit-t.cpp
Script executed:
git ls-files lib/Repository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
lib/AI_Features_Manager.cpp
lib/AI_Tool_Handler.cpp
lib/AI_Vector_Storage.cpp
lib/Admin_Bootstrap.cpp
lib/Admin_FlushVariables.cpp
lib/Admin_Handler.cpp
lib/Admin_Tool_Handler.cpp
lib/Anomaly_Detector.cpp
lib/BaseHGC.cpp
lib/BaseSrvList.cpp
lib/Base_HostGroups_Manager.cpp
lib/Base_Session.cpp
lib/Base_Thread.cpp
lib/Cache_Tool_Handler.cpp
lib/Chart_bundle_js.cpp
lib/ClickHouse_Authentication.cpp
lib/ClickHouse_Server.cpp
lib/Config_Tool_Handler.cpp
lib/Discovery_Schema.cpp
lib/GTID_Server_Data.cpp
lib/GenAI_Thread.cpp
lib/HostgroupRouting.cpp
lib/LLM_Bridge.cpp
lib/LLM_Clients.cpp
lib/MCP_Endpoint.cpp
lib/MCP_Thread.cpp
lib/MCP_Tool_Handler.cpp
lib/Makefile
lib/MonitorHealthDecision.cpp
lib/MyHGC.cpp
lib/MySQLFFTO.cpp
lib/MySQLProtocolUtils.cpp
lib/MySQL_Authentication.cpp
lib/MySQL_Catalog.cpp
lib/MySQL_FTS.cpp
lib/MySQL_HostGroups_Manager.cpp
lib/MySQL_Logger.cpp
lib/MySQL_Monitor.cpp
lib/MySQL_PreparedStatement.cpp
lib/MySQL_Protocol.cpp
lib/MySQL_Query_Cache.cpp
lib/MySQL_Query_Processor.cpp
lib/MySQL_ResultSet.cpp
lib/MySQL_Session.cpp
lib/MySQL_Set_Stmt_Parser.cpp
lib/MySQL_Thread.cpp
lib/MySQL_Tool_Handler.cpp
lib/MySQL_Variables.cpp
lib/MySQL_encode.cpp
lib/MySrvC.cpp
lib/MySrvConnList.cpp
lib/Observe_Tool_Handler.cpp
lib/PgSQLCommandComplete.cpp
lib/PgSQLFFTO.cpp
lib/PgSQL_Authentication.cpp
lib/PgSQL_Backend.cpp
lib/PgSQL_Connection.cpp
lib/PgSQL_Data_Stream.cpp
lib/PgSQL_Error_Helper.cpp
lib/PgSQL_ExplicitTxnStateMgr.cpp
lib/PgSQL_Extended_Query_Message.cpp
lib/PgSQL_HostGroups_Manager.cpp
lib/PgSQL_Logger.cpp
lib/PgSQL_Monitor.cpp
lib/PgSQL_PreparedStatement.cpp
lib/PgSQL_Protocol.cpp
lib/PgSQL_Query_Cache.cpp
lib/PgSQL_Query_Processor.cpp
lib/PgSQL_Session.cpp
lib/PgSQL_Set_Stmt_Parser.cpp
lib/PgSQL_Static_Harvester.cpp
lib/PgSQL_Thread.cpp
lib/PgSQL_Variables.cpp
lib/PgSQL_Variables_Validator.cpp
lib/ProxySQL_Admin.cpp
lib/ProxySQL_Admin_Disk_Upgrade.cpp
lib/ProxySQL_Admin_Scheduler.cpp
lib/ProxySQL_Admin_Stats.cpp
lib/ProxySQL_Admin_Tests.cpp
lib/ProxySQL_Admin_Tests2.cpp
lib/ProxySQL_Cluster.cpp
lib/ProxySQL_Config.cpp
lib/ProxySQL_GloVars.cpp
lib/ProxySQL_HTTP_Server.cpp
lib/ProxySQL_MCP_Server.cpp
lib/ProxySQL_Poll.cpp
lib/ProxySQL_RESTAPI_Server.cpp
lib/ProxySQL_Restapi.cpp
lib/ProxySQL_Statistics.cpp
lib/QP_query_digest_stats.cpp
lib/QP_rule_text.cpp
lib/Query_Cache.cpp
lib/Query_Processor.cpp
lib/Query_Tool_Handler.cpp
lib/RAG_Tool_Handler.cpp
lib/SHA-crypt.txt
lib/SpookyV2.cpp
lib/Static_Harvester.cpp
lib/Stats_Tool_Handler.cpp
lib/TSDB_Dashboard_html.cpp
lib/TransactionState.cpp
lib/c_tokenizer.cpp
lib/c_tokenizer_legacy.cpp
lib/configfile.cpp
lib/debug.cpp
lib/font-awesome.min.css.cpp
lib/gen_utils.cpp
lib/log_utils.cpp
lib/main-bundle.min.css.cpp
lib/mysql_backend.cpp
lib/mysql_connection.cpp
lib/mysql_data_stream.cpp
lib/network.cpp
lib/pgsql_tokenizer.cpp
lib/proxy_protocol_info.cpp
lib/proxy_sqlite3_symbols.cpp
lib/proxysql_coredump.cpp
lib/proxysql_find_charset.cpp
lib/proxysql_sslkeylog.cpp
lib/proxysql_utils.cpp
lib/sha256crypt.cpp
lib/sha256crypt.orig.c
lib/sqlite3db.cpp
lib/thread.cpp
Script executed:
rg "mysql_build_packet" --type cppRepository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
test/tap/tests/unit/ffto_protocol_unit-t.cpp:static void test_mysql_build_packet() {
test/tap/tests/unit/ffto_protocol_unit-t.cpp: size_t total = mysql_build_packet(payload, 9, 0, out);
test/tap/tests/unit/ffto_protocol_unit-t.cpp: size_t total = mysql_build_packet(payload.data(), 1000, 5, out.data());
test/tap/tests/unit/ffto_protocol_unit-t.cpp: size_t total = mysql_build_packet(nullptr, 0, 1, out);
test/tap/tests/unit/ffto_protocol_unit-t.cpp: mysql_build_packet(ok_payload, 3, 1, pkt);
test/tap/tests/unit/ffto_protocol_unit-t.cpp: offset += mysql_build_packet(p1, 4, 0, stream + offset);
test/tap/tests/unit/ffto_protocol_unit-t.cpp: offset += mysql_build_packet(p2, 3, 1, stream + offset);
test/tap/tests/unit/ffto_protocol_unit-t.cpp: offset += mysql_build_packet(p3, 5, 2, stream + offset);
test/tap/tests/unit/ffto_protocol_unit-t.cpp: test_mysql_build_packet(); // 4
lib/MySQLProtocolUtils.cpp:size_t mysql_build_packet(
include/MySQLProtocolUtils.h:size_t mysql_build_packet(
Script executed:
rg "mysql_build_packet"Repository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
test/tap/tests/unit/ffto_protocol_unit-t.cpp:static void test_mysql_build_packet() {
test/tap/tests/unit/ffto_protocol_unit-t.cpp: size_t total = mysql_build_packet(payload, 9, 0, out);
test/tap/tests/unit/ffto_protocol_unit-t.cpp: size_t total = mysql_build_packet(payload.data(), 1000, 5, out.data());
test/tap/tests/unit/ffto_protocol_unit-t.cpp: size_t total = mysql_build_packet(nullptr, 0, 1, out);
test/tap/tests/unit/ffto_protocol_unit-t.cpp: mysql_build_packet(ok_payload, 3, 1, pkt);
test/tap/tests/unit/ffto_protocol_unit-t.cpp: offset += mysql_build_packet(p1, 4, 0, stream + offset);
test/tap/tests/unit/ffto_protocol_unit-t.cpp: offset += mysql_build_packet(p2, 3, 1, stream + offset);
test/tap/tests/unit/ffto_protocol_unit-t.cpp: offset += mysql_build_packet(p3, 5, 2, stream + offset);
test/tap/tests/unit/ffto_protocol_unit-t.cpp: test_mysql_build_packet(); // 4
lib/MySQLProtocolUtils.cpp:size_t mysql_build_packet(
include/MySQLProtocolUtils.h:size_t mysql_build_packet(
Script executed:
ls -laRepository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
total 264
drwxr-xr-x 23 jailuser jailuser 1020 Mar 22 11:20 .
drwxr-xr-x 1 jailuser jailuser 180 Mar 22 11:20 ..
-rw-r--r-- 1 jailuser jailuser 13311 Mar 22 11:20 .aider.conf.yml
-rw-r--r-- 1 root root 777 Mar 22 11:20 .clang-tidy
drwxr-xr-x 6 jailuser jailuser 240 Mar 22 11:25 .git
drwxr-xr-x 4 jailuser jailuser 100 Mar 22 11:20 .github
-rw-r--r-- 1 jailuser jailuser 3721 Mar 22 11:20 .gitignore
-rw-r--r-- 1 jailuser jailuser 1834 Mar 22 11:20 Aurora_Automated_Testing.md
-rw-r--r-- 1 jailuser jailuser 16444 Mar 22 11:20 CHANGELOG.md
-rw-r--r-- 1 jailuser jailuser 6258 Mar 22 11:20 CLAUDE.md
-rw-r--r-- 1 jailuser jailuser 3101 Mar 22 11:20 CONTRIBUTING.md
-rw-r--r-- 1 jailuser jailuser 8337 Mar 22 11:20 FAQ.md
-rw-r--r-- 1 jailuser jailuser 7160 Mar 22 11:20 INSTALL.md
-rw-r--r-- 1 jailuser jailuser 35121 Mar 22 11:20 LICENSE
-rw-r--r-- 1 jailuser jailuser 19780 Mar 22 11:20 Makefile
-rw-r--r-- 1 jailuser jailuser 8239 Mar 22 11:20 ProxySQL-3.0.4-Release-Notes.md
-rw-r--r-- 1 jailuser jailuser 4294 Mar 22 11:20 RAG_COMPLETION_SUMMARY.md
-rw-r--r-- 1 jailuser jailuser 2531 Mar 22 11:20 RAG_FILE_SUMMARY.md
-rw-r--r-- 1 jailuser jailuser 4308 Mar 22 11:20 RAG_IMPLEMENTATION_COMPLETE.md
-rw-r--r-- 1 jailuser jailuser 4320 Mar 22 11:20 RAG_IMPLEMENTATION_SUMMARY.md
drwxr-xr-x 2 jailuser jailuser 360 Mar 22 11:20 RAG_POC
-rw-r--r-- 1 jailuser jailuser 16028 Mar 22 11:20 README.md
-rw-r--r-- 1 jailuser jailuser 3024 Mar 22 11:20 RELEASE_NOTES_WORKFLOW.md
-rw-r--r-- 1 jailuser jailuser 3496 Mar 22 11:20 RUNNING.md
-rw-r--r-- 1 jailuser jailuser 1011 Mar 22 11:20 Read_Only_Testing.md
drwxr-xr-x 2 jailuser jailuser 60 Mar 22 11:20 binaries
-rw-r--r-- 1 jailuser jailuser 1597 Mar 22 11:20 bounty.md
-rw-r--r-- 1 jailuser jailuser 85 Mar 22 11:20 build_images.py
drwxr-xr-x 2 jailuser jailuser 80 Mar 22 11:20 common_mk
-rw-r--r-- 1 jailuser jailuser 958 Mar 22 11:20 compile_examples.txt
drwxr-xr-x 25 jailuser jailuser 520 Mar 22 11:20 deps
drwxr-xr-x 2 jailuser jailuser 100 Mar 22 11:20 diamond
drwxr-xr-x 14 jailuser jailuser 780 Mar 22 11:20 doc
drwxr-xr-x 5 jailuser jailuser 100 Mar 22 11:20 docker
-rwxr-xr-x 1 jailuser jailuser 12876 Mar 22 11:20 docker-compose.yml
drwxr-xr-x 4 jailuser jailuser 100 Mar 22 11:20 etc
drwxr-xr-x 2 jailuser jailuser 160 Mar 22 11:20 genai_prototype
drwxr-xr-x 2 jailuser jailuser 2720 Mar 22 11:20 include
drwxr-xr-x 2 jailuser jailuser 2520 Mar 22 11:20 lib
drwxr-xr-x 2 jailuser jailuser 60 Mar 22 11:20 microbench
drwxr-xr-x 2 jailuser jailuser 60 Mar 22 11:20 notes
-rw-r--r-- 1 jailuser jailuser 445 Mar 22 11:20 proxysql-tests.ini.example
-rw-r--r-- 1 jailuser jailuser 11705 Mar 22 11:20 proxysql_notes.txt
-rwxr-xr-x 1 jailuser jailuser 5635 Mar 22 11:20 scenarios.py
drwxr-xr-x 7 jailuser jailuser 420 Mar 22 11:20 scripts
-rw-r--r-- 1 jailuser jailuser 5578 Mar 22 11:20 simple_discovery.py
drwxr-xr-x 2 jailuser jailuser 200 Mar 22 11:20 src
drwxr-xr-x 3 jailuser jailuser 60 Mar 22 11:20 systemd
drwxr-xr-x 12 jailuser jailuser 280 Mar 22 11:20 test
drwxr-xr-x 3 jailuser jailuser 60 Mar 22 11:20 test-scripts
drwxr-xr-x 2 jailuser jailuser 160 Mar 22 11:20 tools
Script executed:
fd "Protocol" --type fRepository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
include/MySQLProtocolUtils.h
include/MySQL_Protocol.h
include/PgSQL_Protocol.h
lib/MySQLProtocolUtils.cpp
lib/MySQL_Protocol.cpp
lib/PgSQL_Protocol.cpp
test/tap/tests/test_PROXY_Protocol-t.cpp
Script executed:
cat lib/MySQLProtocolUtils.cpp 2>/dev/null | head -100Repository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
/**
* `@file` MySQLProtocolUtils.cpp
* `@brief` Implementation of MySQL protocol utility functions.
*
* `@see` MySQLProtocolUtils.h
*/
`#include` "MySQLProtocolUtils.h"
`#include` <cstring>
uint64_t mysql_read_lenenc_int(const unsigned char* &buf, size_t &len) {
if (len == 0) return 0;
uint8_t first_byte = buf[0];
buf++; len--;
if (first_byte < 0xFB) return first_byte;
if (first_byte == 0xFC) {
if (len < 2) return 0;
uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8);
buf += 2; len -= 2;
return value;
}
if (first_byte == 0xFD) {
if (len < 3) return 0;
uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8)
| (static_cast<uint64_t>(buf[2]) << 16);
buf += 3; len -= 3;
return value;
}
if (first_byte == 0xFE) {
if (len < 8) return 0;
uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8)
| (static_cast<uint64_t>(buf[2]) << 16)
| (static_cast<uint64_t>(buf[3]) << 24)
| (static_cast<uint64_t>(buf[4]) << 32)
| (static_cast<uint64_t>(buf[5]) << 40)
| (static_cast<uint64_t>(buf[6]) << 48)
| (static_cast<uint64_t>(buf[7]) << 56);
buf += 8; len -= 8;
return value;
}
return 0;
}
size_t mysql_build_packet(
const unsigned char *payload,
uint32_t payload_len,
uint8_t seq_id,
unsigned char *out_buf)
{
// 3-byte length (little-endian) + 1-byte sequence
out_buf[0] = payload_len & 0xFF;
out_buf[1] = (payload_len >> 8) & 0xFF;
out_buf[2] = (payload_len >> 16) & 0xFF;
out_buf[3] = seq_id;
if (payload && payload_len > 0) {
memcpy(out_buf + 4, payload, payload_len);
}
return payload_len + 4;
}
Script executed:
rg "payload_len" --type cpp | head -20Repository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
lib/MySQLProtocolUtils.cpp: uint32_t payload_len,
lib/MySQLProtocolUtils.cpp: out_buf[0] = payload_len & 0xFF;
lib/MySQLProtocolUtils.cpp: out_buf[1] = (payload_len >> 8) & 0xFF;
lib/MySQLProtocolUtils.cpp: out_buf[2] = (payload_len >> 16) & 0xFF;
lib/MySQLProtocolUtils.cpp: if (payload && payload_len > 0) {
lib/MySQLProtocolUtils.cpp: memcpy(out_buf + 4, payload, payload_len);
lib/MySQLProtocolUtils.cpp: return payload_len + 4;
lib/PgSQL_Extended_Query_Message.cpp: uint32_t payload_len = 0;
lib/PgSQL_Extended_Query_Message.cpp: if (!get_uint32be(packet + offset, &payload_len)) {
lib/PgSQL_Extended_Query_Message.cpp: if (payload_len != pkt_len - 1) {
lib/PgSQL_Extended_Query_Message.cpp: uint32_t payload_len = 0;
lib/PgSQL_Extended_Query_Message.cpp: if (!get_uint32be(packet + offset, &payload_len)) {
lib/PgSQL_Extended_Query_Message.cpp: if (payload_len != pkt_len - 1) {
lib/PgSQL_Extended_Query_Message.cpp: uint32_t payload_len = 0;
lib/PgSQL_Extended_Query_Message.cpp: if (!get_uint32be(packet + offset, &payload_len)) {
lib/PgSQL_Extended_Query_Message.cpp: if (payload_len != pkt_len - 1) {
lib/PgSQL_Extended_Query_Message.cpp: uint32_t payload_len = 0;
lib/PgSQL_Extended_Query_Message.cpp: if (!get_uint32be(packet + offset, &payload_len)) {
lib/PgSQL_Extended_Query_Message.cpp: if (payload_len != pkt_len - 1) {
lib/PgSQL_Extended_Query_Message.cpp: uint32_t payload_len = 0;
Script executed:
rg "memcpy.*payload" --type cppRepository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
lib/MySQLProtocolUtils.cpp: memcpy(out_buf + 4, payload, payload_len);
lib/mysql_data_stream.cpp: memcpy((unsigned char *)queueIN.pkt.ptr+sizeof(mysql_hdr), queue_r_ptr(queueIN), 3); // copy 3 bytes, the length of the uncompressed payload
lib/MySQL_Query_Cache.cpp: memcpy(payload_temp, &warnings, sizeof(uint16_t));
lib/MySQL_Query_Cache.cpp: memcpy((payload + 1), &warnings, sizeof(uint16_t));
Script executed:
fd "\.(cpp|h)$" --type f | head -30Repository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
RAG_POC/rag_ingest.cpp
deps/libscram/include/scram-internal.h
deps/libscram/include/scram.h
deps/sqlite3/sqlite-vec-source/sqlite-vec.h
genai_prototype/genai_demo.cpp
genai_prototype/genai_demo_event.cpp
include/AI_Features_Manager.h
include/AI_Tool_Handler.h
include/AI_Vector_Storage.h
include/Admin_Tool_Handler.h
include/Admin_ifaces.h
include/Anomaly_Detector.h
include/Base_HostGroups_Manager.h
include/Base_Session.h
include/Base_Thread.h
include/Cache_Tool_Handler.h
include/ClickHouse_Server.h
include/Command_Counter.h
include/Config_Tool_Handler.h
include/ConnectionPoolDecision.h
include/Discovery_Schema.h
include/GTID_Server_Data.h
include/GenAI_Thread.h
include/HostgroupRouting.h
include/LLM_Bridge.h
include/MCP_Endpoint.h
include/MCP_Thread.h
include/MCP_Tool_Handler.h
include/MonitorHealthDecision.h
include/MySQLProtocolUtils.h
Script executed:
ls -la lib/ 2>/dev/null || echo "lib directory not found"Repository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
total 7004
drwxr-xr-x 2 jailuser jailuser 2520 Mar 22 11:20 .
drwxr-xr-x 23 jailuser jailuser 1020 Mar 22 11:20 ..
-rw-r--r-- 1 jailuser jailuser 16628 Mar 22 11:20 AI_Features_Manager.cpp
-rw-r--r-- 1 jailuser jailuser 6621 Mar 22 11:20 AI_Tool_Handler.cpp
-rw-r--r-- 1 jailuser jailuser 1018 Mar 22 11:20 AI_Vector_Storage.cpp
-rw-r--r-- 1 jailuser jailuser 59785 Mar 22 11:20 Admin_Bootstrap.cpp
-rw-r--r-- 1 jailuser jailuser 61130 Mar 22 11:20 Admin_FlushVariables.cpp
-rw-r--r-- 1 jailuser jailuser 240330 Mar 22 11:20 Admin_Handler.cpp
-rw-r--r-- 1 jailuser jailuser 4058 Mar 22 11:20 Admin_Tool_Handler.cpp
-rw-r--r-- 1 jailuser jailuser 31371 Mar 22 11:20 Anomaly_Detector.cpp
-rw-r--r-- 1 jailuser jailuser 4051 Mar 22 11:20 BaseHGC.cpp
-rw-r--r-- 1 jailuser jailuser 1770 Mar 22 11:20 BaseSrvList.cpp
-rw-r--r-- 1 jailuser jailuser 196422 Mar 22 11:20 Base_HostGroups_Manager.cpp
-rw-r--r-- 1 jailuser jailuser 30397 Mar 22 11:20 Base_Session.cpp
-rw-r--r-- 1 jailuser jailuser 22148 Mar 22 11:20 Base_Thread.cpp
-rw-r--r-- 1 jailuser jailuser 4462 Mar 22 11:20 Cache_Tool_Handler.cpp
-rw-r--r-- 1 jailuser jailuser 546903 Mar 22 11:20 Chart_bundle_js.cpp
-rw-r--r-- 1 jailuser jailuser 13606 Mar 22 11:20 ClickHouse_Authentication.cpp
-rw-r--r-- 1 jailuser jailuser 61042 Mar 22 11:20 ClickHouse_Server.cpp
-rw-r--r-- 1 jailuser jailuser 7360 Mar 22 11:20 Config_Tool_Handler.cpp
-rw-r--r-- 1 jailuser jailuser 117045 Mar 22 11:20 Discovery_Schema.cpp
-rw-r--r-- 1 jailuser jailuser 15290 Mar 22 11:20 GTID_Server_Data.cpp
-rw-r--r-- 1 jailuser jailuser 59106 Mar 22 11:20 GenAI_Thread.cpp
-rw-r--r-- 1 jailuser jailuser 1676 Mar 22 11:20 HostgroupRouting.cpp
-rw-r--r-- 1 jailuser jailuser 11354 Mar 22 11:20 LLM_Bridge.cpp
-rw-r--r-- 1 jailuser jailuser 22318 Mar 22 11:20 LLM_Clients.cpp
-rw-r--r-- 1 jailuser jailuser 20608 Mar 22 11:20 MCP_Endpoint.cpp
-rw-r--r-- 1 jailuser jailuser 12702 Mar 22 11:20 MCP_Thread.cpp
-rw-r--r-- 1 jailuser jailuser 1636 Mar 22 11:20 MCP_Tool_Handler.cpp
-rw-r--r-- 1 jailuser jailuser 5805 Mar 22 11:20 Makefile
-rw-r--r-- 1 jailuser jailuser 2853 Mar 22 11:20 MonitorHealthDecision.cpp
-rw-r--r-- 1 jailuser jailuser 13622 Mar 22 11:20 MyHGC.cpp
-rw-r--r-- 1 jailuser jailuser 12304 Mar 22 11:20 MySQLFFTO.cpp
-rw-r--r-- 1 jailuser jailuser 1591 Mar 22 11:20 MySQLProtocolUtils.cpp
-rw-r--r-- 1 jailuser jailuser 27952 Mar 22 11:20 MySQL_Authentication.cpp
-rw-r--r-- 1 jailuser jailuser 19910 Mar 22 11:20 MySQL_Catalog.cpp
-rw-r--r-- 1 jailuser jailuser 25861 Mar 22 11:20 MySQL_FTS.cpp
-rw-r--r-- 1 jailuser jailuser 297234 Mar 22 11:20 MySQL_HostGroups_Manager.cpp
-rw-r--r-- 1 jailuser jailuser 77618 Mar 22 11:20 MySQL_Logger.cpp
-rw-r--r-- 1 jailuser jailuser 333917 Mar 22 11:20 MySQL_Monitor.cpp
-rw-r--r-- 1 jailuser jailuser 31800 Mar 22 11:20 MySQL_PreparedStatement.cpp
-rw-r--r-- 1 jailuser jailuser 110757 Mar 22 11:20 MySQL_Protocol.cpp
-rw-r--r-- 1 jailuser jailuser 10224 Mar 22 11:20 MySQL_Query_Cache.cpp
-rw-r--r-- 1 jailuser jailuser 34802 Mar 22 11:20 MySQL_Query_Processor.cpp
-rw-r--r-- 1 jailuser jailuser 18417 Mar 22 11:20 MySQL_ResultSet.cpp
-rw-r--r-- 1 jailuser jailuser 371282 Mar 22 11:20 MySQL_Session.cpp
-rw-r--r-- 1 jailuser jailuser 24644 Mar 22 11:20 MySQL_Set_Stmt_Parser.cpp
-rw-r--r-- 1 jailuser jailuser 254037 Mar 22 11:20 MySQL_Thread.cpp
-rw-r--r-- 1 jailuser jailuser 41841 Mar 22 11:20 MySQL_Tool_Handler.cpp
-rw-r--r-- 1 jailuser jailuser 27149 Mar 22 11:20 MySQL_Variables.cpp
-rw-r--r-- 1 jailuser jailuser 7092 Mar 22 11:20 MySQL_encode.cpp
-rw-r--r-- 1 jailuser jailuser 5190 Mar 22 11:20 MySrvC.cpp
-rw-r--r-- 1 jailuser jailuser 12498 Mar 22 11:20 MySrvConnList.cpp
-rw-r--r-- 1 jailuser jailuser 4481 Mar 22 11:20 Observe_Tool_Handler.cpp
-rw-r--r-- 1 jailuser jailuser 1738 Mar 22 11:20 PgSQLCommandComplete.cpp
-rw-r--r-- 1 jailuser jailuser 13107 Mar 22 11:20 PgSQLFFTO.cpp
-rw-r--r-- 1 jailuser jailuser 22964 Mar 22 11:20 PgSQL_Authentication.cpp
-rw-r--r-- 1 jailuser jailuser 551 Mar 22 11:20 PgSQL_Backend.cpp
-rw-r--r-- 1 jailuser jailuser 101629 Mar 22 11:20 PgSQL_Connection.cpp
-rw-r--r-- 1 jailuser jailuser 42071 Mar 22 11:20 PgSQL_Data_Stream.cpp
-rw-r--r-- 1 jailuser jailuser 16573 Mar 22 11:20 PgSQL_Error_Helper.cpp
-rw-r--r-- 1 jailuser jailuser 17191 Mar 22 11:20 PgSQL_ExplicitTxnStateMgr.cpp
-rw-r--r-- 1 jailuser jailuser 18716 Mar 22 11:20 PgSQL_Extended_Query_Message.cpp
-rw-r--r-- 1 jailuser jailuser 168643 Mar 22 11:20 PgSQL_HostGroups_Manager.cpp
-rw-r--r-- 1 jailuser jailuser 52674 Mar 22 11:20 PgSQL_Logger.cpp
-rw-r--r-- 1 jailuser jailuser 79293 Mar 22 11:20 PgSQL_Monitor.cpp
-rw-r--r-- 1 jailuser jailuser 22063 Mar 22 11:20 PgSQL_PreparedStatement.cpp
-rw-r--r-- 1 jailuser jailuser 84284 Mar 22 11:20 PgSQL_Protocol.cpp
-rw-r--r-- 1 jailuser jailuser 1876 Mar 22 11:20 PgSQL_Query_Cache.cpp
-rw-r--r-- 1 jailuser jailuser 47636 Mar 22 11:20 PgSQL_Query_Processor.cpp
-rw-r--r-- 1 jailuser jailuser 284549 Mar 22 11:20 PgSQL_Session.cpp
-rw-r--r-- 1 jailuser jailuser 6269 Mar 22 11:20 PgSQL_Set_Stmt_Parser.cpp
-rw-r--r-- 1 jailuser jailuser 22376 Mar 22 11:20 PgSQL_Static_Harvester.cpp
-rw-r--r-- 1 jailuser jailuser 226855 Mar 22 11:20 PgSQL_Thread.cpp
-rw-r--r-- 1 jailuser jailuser 10310 Mar 22 11:20 PgSQL_Variables.cpp
-rw-r--r-- 1 jailuser jailuser 18981 Mar 22 11:20 PgSQL_Variables_Validator.cpp
-rw-r--r-- 1 jailuser jailuser 367924 Mar 22 11:20 ProxySQL_Admin.cpp
-rw-r--r-- 1 jailuser jailuser 46011 Mar 22 11:20 ProxySQL_Admin_Disk_Upgrade.cpp
-rw-r--r-- 1 jailuser jailuser 5337 Mar 22 11:20 ProxySQL_Admin_Scheduler.cpp
-rw-r--r-- 1 jailuser jailuser 134969 Mar 22 11:20 ProxySQL_Admin_Stats.cpp
-rw-r--r-- 1 jailuser jailuser 5242 Mar 22 11:20 ProxySQL_Admin_Tests.cpp
-rw-r--r-- 1 jailuser jailuser 51659 Mar 22 11:20 ProxySQL_Admin_Tests2.cpp
-rw-r--r-- 1 jailuser jailuser 213104 Mar 22 11:20 ProxySQL_Cluster.cpp
-rw-r--r-- 1 jailuser jailuser 87278 Mar 22 11:20 ProxySQL_Config.cpp
-rw-r--r-- 1 jailuser jailuser 24183 Mar 22 11:20 ProxySQL_GloVars.cpp
-rw-r--r-- 1 jailuser jailuser 33434 Mar 22 11:20 ProxySQL_HTTP_Server.cpp
-rw-r--r-- 1 jailuser jailuser 10316 Mar 22 11:20 ProxySQL_MCP_Server.cpp
-rw-r--r-- 1 jailuser jailuser 10741 Mar 22 11:20 ProxySQL_Poll.cpp
-rw-r--r-- 1 jailuser jailuser 20309 Mar 22 11:20 ProxySQL_RESTAPI_Server.cpp
-rw-r--r-- 1 jailuser jailuser 2674 Mar 22 11:20 ProxySQL_Restapi.cpp
-rw-r--r-- 1 jailuser jailuser 77345 Mar 22 11:20 ProxySQL_Statistics.cpp
-rw-r--r-- 1 jailuser jailuser 4829 Mar 22 11:20 QP_query_digest_stats.cpp
-rw-r--r-- 1 jailuser jailuser 633 Mar 22 11:20 QP_rule_text.cpp
-rw-r--r-- 1 jailuser jailuser 22150 Mar 22 11:20 Query_Cache.cpp
-rw-r--r-- 1 jailuser jailuser 99447 Mar 22 11:20 Query_Processor.cpp
-rw-r--r-- 1 jailuser jailuser 98297 Mar 22 11:20 Query_Tool_Handler.cpp
-rw-r--r-- 1 jailuser jailuser 89831 Mar 22 11:20 RAG_Tool_Handler.cpp
-rw-r--r-- 1 jailuser jailuser 61586 Mar 22 11:20 SHA-crypt.txt
-rw-r--r-- 1 jailuser jailuser 8529 Mar 22 11:20 SpookyV2.cpp
-rw-r--r-- 1 jailuser jailuser 44063 Mar 22 11:20 Static_Harvester.cpp
-rw-r--r-- 1 jailuser jailuser 128857 Mar 22 11:20 Stats_Tool_Handler.cpp
-rw-r--r-- 1 jailuser jailuser 6967 Mar 22 11:20 TSDB_Dashboard_html.cpp
-rw-r--r-- 1 jailuser jailuser 1383 Mar 22 11:20 TransactionState.cpp
-rw-r--r-- 1 jailuser jailuser 88371 Mar 22 11:20 c_tokenizer.cpp
-rw-r--r-- 1 jailuser jailuser 13865 Mar 22 11:20 c_tokenizer_legacy.cpp
-rw-r--r-- 1 jailuser jailuser 2023 Mar 22 11:20 configfile.cpp
-rw-r--r-- 1 jailuser jailuser 19210 Mar 22 11:20 debug.cpp
-rw-r--r-- 1 jailuser jailuser 33070 Mar 22 11:20 font-awesome.min.css.cpp
-rw-r--r-- 1 jailuser jailuser 12786 Mar 22 11:20 gen_utils.cpp
-rw-r--r-- 1 jailuser jailuser 3534 Mar 22 11:20 log_utils.cpp
-rw-r--r-- 1 jailuser jailuser 44145 Mar 22 11:20 main-bundle.min.css.cpp
-rw-r--r-- 1 jailuser jailuser 584 Mar 22 11:20 mysql_backend.cpp
-rw-r--r-- 1 jailuser jailuser 110159 Mar 22 11:20 mysql_connection.cpp
-rw-r--r-- 1 jailuser jailuser 66287 Mar 22 11:20 mysql_data_stream.cpp
-rw-r--r-- 1 jailuser jailuser 4256 Mar 22 11:20 network.cpp
-rw-r--r-- 1 jailuser jailuser 107532 Mar 22 11:20 pgsql_tokenizer.cpp
-rw-r--r-- 1 jailuser jailuser 14677 Mar 22 11:20 proxy_protocol_info.cpp
-rw-r--r-- 1 jailuser jailuser 3902 Mar 22 11:20 proxy_sqlite3_symbols.cpp
-rw-r--r-- 1 jailuser jailuser 3103 Mar 22 11:20 proxysql_coredump.cpp
-rw-r--r-- 1 jailuser jailuser 4492 Mar 22 11:20 proxysql_find_charset.cpp
-rw-r--r-- 1 jailuser jailuser 7567 Mar 22 11:20 proxysql_sslkeylog.cpp
-rw-r--r-- 1 jailuser jailuser 25244 Mar 22 11:20 proxysql_utils.cpp
-rw-r--r-- 1 jailuser jailuser 17818 Mar 22 11:20 sha256crypt.cpp
-rw-r--r-- 1 jailuser jailuser 21970 Mar 22 11:20 sha256crypt.orig.c
-rw-r--r-- 1 jailuser jailuser 38397 Mar 22 11:20 sqlite3db.cpp
-rw-r--r-- 1 jailuser jailuser 2048 Mar 22 11:20 thread.cpp
Script executed:
git ls-files | wc -l && git ls-files | head -50Repository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
1475
.aider.conf.yml
.github/ISSUE_TEMPLATE/bug_report.md
.github/issue_template.md
.github/workflows/CI-3p-aiomysql.yml
.github/workflows/CI-3p-django-framework.yml
.github/workflows/CI-3p-laravel-framework.yml
.github/workflows/CI-3p-mariadb-connector-c.yml
.github/workflows/CI-3p-mysql-connector-j.yml
.github/workflows/CI-3p-pgjdbc.yml
.github/workflows/CI-3p-php-pdo-mysql.yml
.github/workflows/CI-3p-php-pdo-pgsql.yml
.github/workflows/CI-3p-postgresql.yml
.github/workflows/CI-3p-sqlalchemy.yml
.github/workflows/CI-basictests.yml
.github/workflows/CI-builds.yml
.github/workflows/CI-codeql.yml
.github/workflows/CI-maketest.yml
.github/workflows/CI-package-build.yml
.github/workflows/CI-repltests.yml
.github/workflows/CI-selftests.yml
.github/workflows/CI-shuntest.yml
.github/workflows/CI-taptests-asan.yml
.github/workflows/CI-taptests-groups.yml
.github/workflows/CI-taptests-ssl.yml
.github/workflows/CI-taptests.yml
.github/workflows/CI-trigger.yml
.gitignore
Aurora_Automated_Testing.md
CHANGELOG.md
CLAUDE.md
CONTRIBUTING.md
FAQ.md
INSTALL.md
LICENSE
Makefile
ProxySQL-3.0.4-Release-Notes.md
RAG_COMPLETION_SUMMARY.md
RAG_FILE_SUMMARY.md
RAG_IMPLEMENTATION_COMPLETE.md
RAG_IMPLEMENTATION_SUMMARY.md
RAG_POC/CHUNKING_AND_EMBEDDING_GUIDE.md
RAG_POC/EMBEDDING_TEST_PLAN.md
RAG_POC/INGEST_USAGE_GUIDE.md
RAG_POC/Makefile
RAG_POC/architecture-data-model.md
RAG_POC/architecture-runtime-retrieval.md
RAG_POC/embeddings-design.md
RAG_POC/mcp-tools.md
RAG_POC/rag_ingest.cpp
RAG_POC/rag_system_prompt.md
MySQL protocol packet header format 3-byte payload length limit 0xFFFFFF
The MySQL protocol packet header is 4 bytes long: the first 3 bytes encode the payload length as a little-endian unsigned integer (maximum value 0xFFFFFF or 16,777,215 bytes, or 16MB-1 byte), followed by 1 byte for the sequence ID. The payload follows the header. For payloads larger than 0xFFFFFF bytes, the data is split across multiple packets, where packets with length 0xFFFFFF continue the data, and the sequence ID increments for each subsequent packet until the final one.
Citations:
- 1: https://dev.mysql.com/doc/dev/mysql-server/8.4.7/page_protocol_basic_packets.html
- 2: https://mariadb.com/docs/server/reference/clientserver-protocol/0-packet
- 3: https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_packets.html
Script executed:
grep -r "mysql_build_packet" --include="*.cpp" --include="*.h" -B 3 -A 3 | head -100Repository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
include/MySQLProtocolUtils.h- * `@param` out_buf Output buffer (must be at least payload_len + 4).
include/MySQLProtocolUtils.h- * `@return` Total packet size (payload_len + 4).
include/MySQLProtocolUtils.h- */
include/MySQLProtocolUtils.h:size_t mysql_build_packet(
include/MySQLProtocolUtils.h- const unsigned char *payload,
include/MySQLProtocolUtils.h- uint32_t payload_len,
include/MySQLProtocolUtils.h- uint8_t seq_id,
--
lib/MySQLProtocolUtils.cpp- return 0;
lib/MySQLProtocolUtils.cpp-}
lib/MySQLProtocolUtils.cpp-
lib/MySQLProtocolUtils.cpp:size_t mysql_build_packet(
lib/MySQLProtocolUtils.cpp- const unsigned char *payload,
lib/MySQLProtocolUtils.cpp- uint32_t payload_len,
lib/MySQLProtocolUtils.cpp- uint8_t seq_id,
--
test/tap/tests/unit/ffto_protocol_unit-t.cpp-// 2. MySQL: packet building
test/tap/tests/unit/ffto_protocol_unit-t.cpp-// ============================================================================
test/tap/tests/unit/ffto_protocol_unit-t.cpp-
test/tap/tests/unit/ffto_protocol_unit-t.cpp:static void test_mysql_build_packet() {
test/tap/tests/unit/ffto_protocol_unit-t.cpp- unsigned char payload[] = {0x03, 'S', 'E', 'L', 'E', 'C', 'T', ' ', '1'};
test/tap/tests/unit/ffto_protocol_unit-t.cpp- unsigned char out[13];
test/tap/tests/unit/ffto_protocol_unit-t.cpp: size_t total = mysql_build_packet(payload, 9, 0, out);
test/tap/tests/unit/ffto_protocol_unit-t.cpp- ok(total == 13, "build packet: total size 13");
test/tap/tests/unit/ffto_protocol_unit-t.cpp- ok(out[0] == 9 && out[1] == 0 && out[2] == 0, "build packet: length = 9");
test/tap/tests/unit/ffto_protocol_unit-t.cpp- ok(out[3] == 0, "build packet: seq_id = 0");
--
test/tap/tests/unit/ffto_protocol_unit-t.cpp- // Build a packet with 1000-byte payload
test/tap/tests/unit/ffto_protocol_unit-t.cpp- std::vector<unsigned char> payload(1000, 'X');
test/tap/tests/unit/ffto_protocol_unit-t.cpp- std::vector<unsigned char> out(1004);
test/tap/tests/unit/ffto_protocol_unit-t.cpp: size_t total = mysql_build_packet(payload.data(), 1000, 5, out.data());
test/tap/tests/unit/ffto_protocol_unit-t.cpp- ok(total == 1004, "large packet: total size 1004");
test/tap/tests/unit/ffto_protocol_unit-t.cpp- ok(out[0] == 0xE8 && out[1] == 0x03 && out[2] == 0x00,
test/tap/tests/unit/ffto_protocol_unit-t.cpp- "large packet: length = 1000 (little-endian)");
--
test/tap/tests/unit/ffto_protocol_unit-t.cpp-
test/tap/tests/unit/ffto_protocol_unit-t.cpp-static void test_mysql_build_empty_packet() {
test/tap/tests/unit/ffto_protocol_unit-t.cpp- unsigned char out[4];
test/tap/tests/unit/ffto_protocol_unit-t.cpp: size_t total = mysql_build_packet(nullptr, 0, 1, out);
test/tap/tests/unit/ffto_protocol_unit-t.cpp- ok(total == 4, "empty packet: header only");
test/tap/tests/unit/ffto_protocol_unit-t.cpp- ok(out[0] == 0 && out[1] == 0 && out[2] == 0, "empty packet: length = 0");
test/tap/tests/unit/ffto_protocol_unit-t.cpp-}
--
test/tap/tests/unit/ffto_protocol_unit-t.cpp- // affected_rows = 42
test/tap/tests/unit/ffto_protocol_unit-t.cpp- unsigned char ok_payload[] = {0x00, 42, 0}; // OK, affected=42, last_insert=0
test/tap/tests/unit/ffto_protocol_unit-t.cpp- unsigned char pkt[7];
test/tap/tests/unit/ffto_protocol_unit-t.cpp: mysql_build_packet(ok_payload, 3, 1, pkt);
test/tap/tests/unit/ffto_protocol_unit-t.cpp-
test/tap/tests/unit/ffto_protocol_unit-t.cpp- // Parse affected_rows from the OK packet payload
test/tap/tests/unit/ffto_protocol_unit-t.cpp- const unsigned char *pos = ok_payload + 1;
--
test/tap/tests/unit/ffto_protocol_unit-t.cpp- size_t offset = 0;
test/tap/tests/unit/ffto_protocol_unit-t.cpp-
test/tap/tests/unit/ffto_protocol_unit-t.cpp- unsigned char p1[] = {0x03, 'S', 'E', 'L'};
test/tap/tests/unit/ffto_protocol_unit-t.cpp: offset += mysql_build_packet(p1, 4, 0, stream + offset);
test/tap/tests/unit/ffto_protocol_unit-t.cpp-
test/tap/tests/unit/ffto_protocol_unit-t.cpp- unsigned char p2[] = {0x01, 0x02, 0x03};
test/tap/tests/unit/ffto_protocol_unit-t.cpp: offset += mysql_build_packet(p2, 3, 1, stream + offset);
test/tap/tests/unit/ffto_protocol_unit-t.cpp-
test/tap/tests/unit/ffto_protocol_unit-t.cpp- unsigned char p3[] = {0xFE, 0x00, 0x00, 0x00, 0x00};
test/tap/tests/unit/ffto_protocol_unit-t.cpp: offset += mysql_build_packet(p3, 5, 2, stream + offset);
test/tap/tests/unit/ffto_protocol_unit-t.cpp-
test/tap/tests/unit/ffto_protocol_unit-t.cpp- ok(offset == 4+4 + 3+4 + 5+4, "multi-packet: total stream size correct");
test/tap/tests/unit/ffto_protocol_unit-t.cpp-
--
test/tap/tests/unit/ffto_protocol_unit-t.cpp- test_mysql_lenenc_truncated(); // 2
test/tap/tests/unit/ffto_protocol_unit-t.cpp-
test/tap/tests/unit/ffto_protocol_unit-t.cpp- // MySQL packet building
test/tap/tests/unit/ffto_protocol_unit-t.cpp: test_mysql_build_packet(); // 4
test/tap/tests/unit/ffto_protocol_unit-t.cpp- test_mysql_build_large_packet(); // 3
test/tap/tests/unit/ffto_protocol_unit-t.cpp- test_mysql_build_empty_packet(); // 2
test/tap/tests/unit/ffto_protocol_unit-t.cpp-
Script executed:
sed -n '1,200p' test/tap/tests/unit/ffto_protocol_unit-t.cpp | grep -A 50 "test_mysql_build_packet"Repository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
static void test_mysql_build_packet() {
unsigned char payload[] = {0x03, 'S', 'E', 'L', 'E', 'C', 'T', ' ', '1'};
unsigned char out[13];
size_t total = mysql_build_packet(payload, 9, 0, out);
ok(total == 13, "build packet: total size 13");
ok(out[0] == 9 && out[1] == 0 && out[2] == 0, "build packet: length = 9");
ok(out[3] == 0, "build packet: seq_id = 0");
ok(memcmp(out + 4, payload, 9) == 0, "build packet: payload intact");
}
static void test_mysql_build_large_packet() {
// Build a packet with 1000-byte payload
std::vector<unsigned char> payload(1000, 'X');
std::vector<unsigned char> out(1004);
size_t total = mysql_build_packet(payload.data(), 1000, 5, out.data());
ok(total == 1004, "large packet: total size 1004");
ok(out[0] == 0xE8 && out[1] == 0x03 && out[2] == 0x00,
"large packet: length = 1000 (little-endian)");
ok(out[3] == 5, "large packet: seq_id = 5");
}
static void test_mysql_build_empty_packet() {
unsigned char out[4];
size_t total = mysql_build_packet(nullptr, 0, 1, out);
ok(total == 4, "empty packet: header only");
ok(out[0] == 0 && out[1] == 0 && out[2] == 0, "empty packet: length = 0");
}
// ============================================================================
// 3. MySQL: OK packet affected_rows extraction
// ============================================================================
static void test_mysql_ok_affected_rows() {
// Build an OK packet: 0x00 + affected_rows(lenenc) + last_insert_id(lenenc)
// affected_rows = 42
unsigned char ok_payload[] = {0x00, 42, 0}; // OK, affected=42, last_insert=0
unsigned char pkt[7];
mysql_build_packet(ok_payload, 3, 1, pkt);
// Parse affected_rows from the OK packet payload
const unsigned char *pos = ok_payload + 1;
size_t rem = 2;
uint64_t affected = mysql_read_lenenc_int(pos, rem);
ok(affected == 42, "OK packet: affected_rows = 42");
}
static void test_mysql_ok_large_affected_rows() {
// affected_rows = 300 (needs 0xFC prefix)
unsigned char ok_payload[] = {0x00, 0xFC, 0x2C, 0x01, 0};
const unsigned char *pos = ok_payload + 1;
size_t rem = 4;
Script executed:
cat include/MySQLProtocolUtils.hRepository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
/**
* `@file` MySQLProtocolUtils.h
* `@brief` Pure MySQL protocol utility functions for unit testability.
*
* Extracted from MySQLFFTO for testing. These are low-level protocol
* parsing helpers that operate on raw byte buffers.
*
* `@see` FFTO unit testing (GitHub issue `#5499`)
*/
`#ifndef` MYSQL_PROTOCOL_UTILS_H
`#define` MYSQL_PROTOCOL_UTILS_H
`#include` <cstdint>
`#include` <cstddef>
/**
* `@brief` Read a MySQL length-encoded integer from a buffer.
*
* MySQL length-encoded integers use 1-9 bytes:
* - 0x00-0xFA: 1 byte (value itself)
* - 0xFC: 2 bytes follow (uint16)
* - 0xFD: 3 bytes follow (uint24)
* - 0xFE: 8 bytes follow (uint64)
*
* `@param` buf [in/out] Pointer to current position; advanced past the integer.
* `@param` len [in/out] Remaining buffer length; decremented.
* `@return` Decoded 64-bit integer value (0 on error/truncation).
*/
uint64_t mysql_read_lenenc_int(const unsigned char* &buf, size_t &len);
/**
* `@brief` Build a MySQL protocol packet header + payload.
*
* Constructs a complete MySQL wire-format packet: 3-byte length +
* 1-byte sequence number + payload.
*
* `@param` payload Payload data.
* `@param` payload_len Length of payload.
* `@param` seq_id Packet sequence number.
* `@param` out_buf Output buffer (must be at least payload_len + 4).
* `@return` Total packet size (payload_len + 4).
*/
size_t mysql_build_packet(
const unsigned char *payload,
uint32_t payload_len,
uint8_t seq_id,
unsigned char *out_buf
);
`#endif` // MYSQL_PROTOCOL_UTILS_H
Script executed:
grep -r "mysql_build_packet" --include="*.cpp" | grep -v "test" | grep -v ".cpp:.*mysql_build_packet(" | head -5Repository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
Script executed:
cat -n lib/MySQLProtocolUtils.cppRepository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
1 /**
2 * `@file` MySQLProtocolUtils.cpp
3 * `@brief` Implementation of MySQL protocol utility functions.
4 *
5 * `@see` MySQLProtocolUtils.h
6 */
7
8 `#include` "MySQLProtocolUtils.h"
9 `#include` <cstring>
10
11 uint64_t mysql_read_lenenc_int(const unsigned char* &buf, size_t &len) {
12 if (len == 0) return 0;
13 uint8_t first_byte = buf[0];
14 buf++; len--;
15 if (first_byte < 0xFB) return first_byte;
16 if (first_byte == 0xFC) {
17 if (len < 2) return 0;
18 uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8);
19 buf += 2; len -= 2;
20 return value;
21 }
22 if (first_byte == 0xFD) {
23 if (len < 3) return 0;
24 uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8)
25 | (static_cast<uint64_t>(buf[2]) << 16);
26 buf += 3; len -= 3;
27 return value;
28 }
29 if (first_byte == 0xFE) {
30 if (len < 8) return 0;
31 uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8)
32 | (static_cast<uint64_t>(buf[2]) << 16)
33 | (static_cast<uint64_t>(buf[3]) << 24)
34 | (static_cast<uint64_t>(buf[4]) << 32)
35 | (static_cast<uint64_t>(buf[5]) << 40)
36 | (static_cast<uint64_t>(buf[6]) << 48)
37 | (static_cast<uint64_t>(buf[7]) << 56);
38 buf += 8; len -= 8;
39 return value;
40 }
41 return 0;
42 }
43
44 size_t mysql_build_packet(
45 const unsigned char *payload,
46 uint32_t payload_len,
47 uint8_t seq_id,
48 unsigned char *out_buf)
49 {
50 // 3-byte length (little-endian) + 1-byte sequence
51 out_buf[0] = payload_len & 0xFF;
52 out_buf[1] = (payload_len >> 8) & 0xFF;
53 out_buf[2] = (payload_len >> 16) & 0xFF;
54 out_buf[3] = seq_id;
55 if (payload && payload_len > 0) {
56 memcpy(out_buf + 4, payload, payload_len);
57 }
58 return payload_len + 4;
59 }
Script executed:
grep -A 100 "test_mysql_build_packet" test/tap/tests/unit/ffto_protocol_unit-t.cpp | head -60Repository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
static void test_mysql_build_packet() {
unsigned char payload[] = {0x03, 'S', 'E', 'L', 'E', 'C', 'T', ' ', '1'};
unsigned char out[13];
size_t total = mysql_build_packet(payload, 9, 0, out);
ok(total == 13, "build packet: total size 13");
ok(out[0] == 9 && out[1] == 0 && out[2] == 0, "build packet: length = 9");
ok(out[3] == 0, "build packet: seq_id = 0");
ok(memcmp(out + 4, payload, 9) == 0, "build packet: payload intact");
}
static void test_mysql_build_large_packet() {
// Build a packet with 1000-byte payload
std::vector<unsigned char> payload(1000, 'X');
std::vector<unsigned char> out(1004);
size_t total = mysql_build_packet(payload.data(), 1000, 5, out.data());
ok(total == 1004, "large packet: total size 1004");
ok(out[0] == 0xE8 && out[1] == 0x03 && out[2] == 0x00,
"large packet: length = 1000 (little-endian)");
ok(out[3] == 5, "large packet: seq_id = 5");
}
static void test_mysql_build_empty_packet() {
unsigned char out[4];
size_t total = mysql_build_packet(nullptr, 0, 1, out);
ok(total == 4, "empty packet: header only");
ok(out[0] == 0 && out[1] == 0 && out[2] == 0, "empty packet: length = 0");
}
// ============================================================================
// 3. MySQL: OK packet affected_rows extraction
// ============================================================================
static void test_mysql_ok_affected_rows() {
// Build an OK packet: 0x00 + affected_rows(lenenc) + last_insert_id(lenenc)
// affected_rows = 42
unsigned char ok_payload[] = {0x00, 42, 0}; // OK, affected=42, last_insert=0
unsigned char pkt[7];
mysql_build_packet(ok_payload, 3, 1, pkt);
// Parse affected_rows from the OK packet payload
const unsigned char *pos = ok_payload + 1;
size_t rem = 2;
uint64_t affected = mysql_read_lenenc_int(pos, rem);
ok(affected == 42, "OK packet: affected_rows = 42");
}
static void test_mysql_ok_large_affected_rows() {
// affected_rows = 300 (needs 0xFC prefix)
unsigned char ok_payload[] = {0x00, 0xFC, 0x2C, 0x01, 0};
const unsigned char *pos = ok_payload + 1;
size_t rem = 4;
uint64_t affected = mysql_read_lenenc_int(pos, rem);
ok(affected == 300, "OK packet: affected_rows = 300 (2-byte lenenc)");
}
// ============================================================================
// 4. PgSQL: CommandComplete — extended tests
// ============================================================================
static void test_pgsql_insert_with_oid() {
Script executed:
python3 << 'EOF'
with open('test/tap/tests/unit/ffto_protocol_unit-t.cpp', 'r') as f:
content = f.read()
# Find the test_mysql_build_packet function
start = content.find('static void test_mysql_build_packet()')
if start != -1:
end = content.find('\n}', start) + 2
test_func = content[start:end]
print(test_func)
EOFRepository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
static void test_mysql_build_packet() {
unsigned char payload[] = {0x03, 'S', 'E', 'L', 'E', 'C', 'T', ' ', '1'};
unsigned char out[13];
size_t total = mysql_build_packet(payload, 9, 0, out);
ok(total == 13, "build packet: total size 13");
ok(out[0] == 9 && out[1] == 0 && out[2] == 0, "build packet: length = 9");
ok(out[3] == 0, "build packet: seq_id = 0");
ok(memcmp(out + 4, payload, 9) == 0, "build packet: payload intact");
}
Script executed:
rg "0xFFFFFF|16.*777|16.*MB" --type cpp | head -20Repository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
test/tap/tests/mysql_stmt_send_long_data_large-t.cpp:This test needs to be extended to send packets smaller than 4MB, but also 16MB or larger.
test/tap/tests/reg_test_3690-admin_large_pkts-t.cpp: * 2. Fills this table with random length strings, varying from small to higher than 0xFFFFFF size.
test/tap/tests/reg_test_3690-admin_large_pkts-t.cpp: row_lens.push_back(small_value_dst(gen) + 0xFFFFFF*2);
test/tap/tests/reg_test_3690-admin_large_pkts-t.cpp: row_lens.push_back(small_value_dst(gen) + 0xFFFFFF);
test/tap/tests/set_testing-240.h: ss << 0xFFFFFFFFFFFFFFFF;
test/tap/tests/set_testing.h: ss << 0xFFFFFFFFFFFFFFFF;
test/tap/tests/test_ps_large_result-t.cpp: ok(true, "16MB row query is executed");
test/tap/tests/test_ps_large_result-t.cpp: ok(true, "16MB result binded");
test/tap/tests/test_ps_large_result-t.cpp: ok(true, "16MB result stored");
test/tap/tests/test_ps_large_result-t.cpp: ok(row_count3==10, "Fetched 10 rows. Some of them are 16MB long");
test/tap/tests/unit/ffto_protocol_unit-t.cpp: ok(mysql_read_lenenc_int(p, len) == 0xFFFFFFFF, "lenenc 8-byte: → 4294967295");
test/tap/tests/test_com_reset_connection_com_change_user-t.cpp: ss << 0xFFFFFFFFFFFFFFFF;
test/tap/tests/unit/protocol_unit-t.cpp: ok(len == 0xFFFFFFFF && bytes == 9,
test/tap/tests/unit/protocol_unit-t.cpp: // 3-byte range (65536 - 16777215)
test/tap/tests/unit/protocol_unit-t.cpp: // 8-byte range (>= 16777216)
test/tap/tests/unit/protocol_unit-t.cpp: enc_len = mysql_encode_length(16777216, hd);
test/tap/tests/unit/protocol_unit-t.cpp: "encode_length: 16777216 → 9 bytes (0xFE prefix)");
test/tap/tests/unit/protocol_unit-t.cpp: uint64_t test_values[] = {0, 1, 250, 251, 1000, 65535, 65536, 16777215, 16777216, 100000000ULL};
test/tap/tests/unit/protocol_unit-t.cpp: hdr.pkt_length = 0xFFFFFF;
test/tap/tests/unit/protocol_unit-t.cpp: ok(hdr.pkt_length == 0xFFFFFF,
Script executed:
rg "payload.*size.*limit|max.*payload|0xFFFFFF" --type cpp | head -20Repository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
test/tap/tests/set_testing-240.h: ss << 0xFFFFFFFFFFFFFFFF;
test/tap/tests/test_com_reset_connection_com_change_user-t.cpp: ss << 0xFFFFFFFFFFFFFFFF;
test/tap/tests/set_testing.h: ss << 0xFFFFFFFFFFFFFFFF;
test/tap/tests/reg_test_3690-admin_large_pkts-t.cpp: * 2. Fills this table with random length strings, varying from small to higher than 0xFFFFFF size.
test/tap/tests/reg_test_3690-admin_large_pkts-t.cpp: row_lens.push_back(small_value_dst(gen) + 0xFFFFFF*2);
test/tap/tests/reg_test_3690-admin_large_pkts-t.cpp: row_lens.push_back(small_value_dst(gen) + 0xFFFFFF);
lib/MySQL_FTS.cpp: snprintf(hash_suffix, sizeof(hash_suffix), "%08zx", hash_value & 0xFFFFFFFF);
lib/mysql_connection.cpp: if (r->length > 0xFFFFFF) {
lib/mysql_connection.cpp: total_size+=(r->length / 0xFFFFFF) * sizeof(mysql_hdr);
lib/mysql_connection.cpp: if (r->length > 0xFFFFFF) {
lib/mysql_connection.cpp: total_size+=(r->length / 0xFFFFFF) * sizeof(mysql_hdr);
lib/mysql_connection.cpp: if (ir->length > 0xFFFFFF) {
lib/mysql_connection.cpp: total_size+=(ir->length / 0xFFFFFF) * sizeof(mysql_hdr);
lib/proxy_protocol_info.cpp: uint32_t subnet = ntohl(subnet_addr.v4.s_addr) & (0xFFFFFFFF << (32 - mask));
lib/PgSQL_Protocol.cpp: column_value_len = -1; /*0xFFFFFFFF*/
lib/PgSQL_Session.cpp: if (pkt.size == (0xFFFFFF + sizeof(mysql_hdr))) { // there are more packets
lib/PgSQL_Session.cpp: if (pkt.size == (0xFFFFFF + sizeof(mysql_hdr))) {
lib/MySQL_ResultSet.cpp: if (r->length > 0xFFFFFF) {
lib/MySQL_ResultSet.cpp: total_size+=(r->length / 0xFFFFFF) * sizeof(mysql_hdr);
lib/MySQL_ResultSet.cpp: if (r->length > 0xFFFFFF) {
Script executed:
wc -l lib/MySQL_Protocol.cppRepository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
3069 lib/MySQL_Protocol.cpp
Script executed:
grep -B 10 "mysql_build_packet" test/tap/tests/unit/ffto_protocol_unit-t.cpp | grep -A 10 "1000\|0xFFFFFF"Repository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
// Build a packet with 1000-byte payload
std::vector<unsigned char> payload(1000, 'X');
std::vector<unsigned char> out(1004);
size_t total = mysql_build_packet(payload.data(), 1000, 5, out.data());
ok(total == 1004, "large packet: total size 1004");
ok(out[0] == 0xE8 && out[1] == 0x03 && out[2] == 0x00,
"large packet: length = 1000 (little-endian)");
ok(out[3] == 5, "large packet: seq_id = 5");
}
static void test_mysql_build_empty_packet() {
unsigned char out[4];
size_t total = mysql_build_packet(nullptr, 0, 1, out);
--
// ============================================================================
// 3. MySQL: OK packet affected_rows extraction
Script executed:
sed -n '/static void test_mysql_build_packet/,/^}/p' test/tap/tests/unit/ffto_protocol_unit-t.cppRepository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
static void test_mysql_build_packet() {
unsigned char payload[] = {0x03, 'S', 'E', 'L', 'E', 'C', 'T', ' ', '1'};
unsigned char out[13];
size_t total = mysql_build_packet(payload, 9, 0, out);
ok(total == 13, "build packet: total size 13");
ok(out[0] == 9 && out[1] == 0 && out[2] == 0, "build packet: length = 9");
ok(out[3] == 0, "build packet: seq_id = 0");
ok(memcmp(out + 4, payload, 9) == 0, "build packet: payload intact");
}
Script executed:
find . -name "*.md" -type f -exec grep -l "payload\|packet.*size" {} \; | head -5Repository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
./CHANGELOG.md
./RAG_POC/architecture-runtime-retrieval.md
./doc/ffto_design.md
./doc/ssl_keylog/ssl_keylog_user_guide.md
./scripts/mcp/DiscoveryAgent/ClaudeCode_Headless/prompts/two_phase_discovery_prompt.md
stderr:
find: 'grep' terminated by signal 13
Script executed:
head -100 lib/MySQL_Protocol.cpp | grep -i "build\|packet\|payload"Repository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
`#ifdef` max_allowed_packet
`#undef` max_allowed_packet
Script executed:
grep -B 5 "mysql_build_packet" test/tap/tests/unit/ffto_protocol_unit-t.cpp | grep -E "payload_len|1000|4|3|9"Repository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
unsigned char payload[] = {0x03, 'S', 'E', 'L', 'E', 'C', 'T', ' ', '1'};
unsigned char out[13];
size_t total = mysql_build_packet(payload, 9, 0, out);
// Build a packet with 1000-byte payload
std::vector<unsigned char> payload(1000, 'X');
std::vector<unsigned char> out(1004);
size_t total = mysql_build_packet(payload.data(), 1000, 5, out.data());
ok(out[3] == 5, "large packet: seq_id = 5");
unsigned char out[4];
// affected_rows = 42
unsigned char ok_payload[] = {0x00, 42, 0}; // OK, affected=42, last_insert=0
mysql_build_packet(ok_payload, 3, 1, pkt);
// Build 3 packets sequentially (simulating a multi-packet stream)
unsigned char stream[64];
unsigned char p1[] = {0x03, 'S', 'E', 'L'};
offset += mysql_build_packet(p1, 4, 0, stream + offset);
unsigned char p2[] = {0x01, 0x02, 0x03};
offset += mysql_build_packet(p2, 3, 1, stream + offset);
unsigned char p3[] = {0xFE, 0x00, 0x00, 0x00, 0x00};
offset += mysql_build_packet(p3, 5, 2, stream + offset);
test_mysql_lenenc_3byte(); // 1
test_mysql_build_packet(); // 4
Script executed:
grep -A 5 "size_t mysql_build_packet" lib/MySQLProtocolUtils.cppRepository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
size_t mysql_build_packet(
const unsigned char *payload,
uint32_t payload_len,
uint8_t seq_id,
unsigned char *out_buf)
{
Script executed:
head -50 lib/MySQLProtocolUtils.cppRepository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
/**
* `@file` MySQLProtocolUtils.cpp
* `@brief` Implementation of MySQL protocol utility functions.
*
* `@see` MySQLProtocolUtils.h
*/
`#include` "MySQLProtocolUtils.h"
`#include` <cstring>
uint64_t mysql_read_lenenc_int(const unsigned char* &buf, size_t &len) {
if (len == 0) return 0;
uint8_t first_byte = buf[0];
buf++; len--;
if (first_byte < 0xFB) return first_byte;
if (first_byte == 0xFC) {
if (len < 2) return 0;
uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8);
buf += 2; len -= 2;
return value;
}
if (first_byte == 0xFD) {
if (len < 3) return 0;
uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8)
| (static_cast<uint64_t>(buf[2]) << 16);
buf += 3; len -= 3;
return value;
}
if (first_byte == 0xFE) {
if (len < 8) return 0;
uint64_t value = buf[0] | (static_cast<uint64_t>(buf[1]) << 8)
| (static_cast<uint64_t>(buf[2]) << 16)
| (static_cast<uint64_t>(buf[3]) << 24)
| (static_cast<uint64_t>(buf[4]) << 32)
| (static_cast<uint64_t>(buf[5]) << 40)
| (static_cast<uint64_t>(buf[6]) << 48)
| (static_cast<uint64_t>(buf[7]) << 56);
buf += 8; len -= 8;
return value;
}
return 0;
}
size_t mysql_build_packet(
const unsigned char *payload,
uint32_t payload_len,
uint8_t seq_id,
unsigned char *out_buf)
{
// 3-byte length (little-endian) + 1-byte sequence
Script executed:
cat include/MySQLProtocolUtils.h | grep -A 10 "mysql_build_packet"Repository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
size_t mysql_build_packet(
const unsigned char *payload,
uint32_t payload_len,
uint8_t seq_id,
unsigned char *out_buf
);
`#endif` // MYSQL_PROTOCOL_UTILS_H
Script executed:
cat -n include/MySQLProtocolUtils.hRepository: sysown/proxysql
Repository: sysown/proxysql
Exit code: 0
stdout:
1 /**
2 * `@file` MySQLProtocolUtils.h
3 * `@brief` Pure MySQL protocol utility functions for unit testability.
4 *
5 * Extracted from MySQLFFTO for testing. These are low-level protocol
6 * parsing helpers that operate on raw byte buffers.
7 *
8 * `@see` FFTO unit testing (GitHub issue `#5499`)
9 */
10
11 `#ifndef` MYSQL_PROTOCOL_UTILS_H
12 `#define` MYSQL_PROTOCOL_UTILS_H
13
14 `#include` <cstdint>
15 `#include` <cstddef>
16
17 /**
18 * `@brief` Read a MySQL length-encoded integer from a buffer.
19 *
20 * MySQL length-encoded integers use 1-9 bytes:
21 * - 0x00-0xFA: 1 byte (value itself)
22 * - 0xFC: 2 bytes follow (uint16)
23 * - 0xFD: 3 bytes follow (uint24)
24 * - 0xFE: 8 bytes follow (uint64)
25 *
26 * `@param` buf [in/out] Pointer to current position; advanced past the integer.
27 * `@param` len [in/out] Remaining buffer length; decremented.
28 * `@return` Decoded 64-bit integer value (0 on error/truncation).
29 */
30 uint64_t mysql_read_lenenc_int(const unsigned char* &buf, size_t &len);
31
32 /**
33 * `@brief` Build a MySQL protocol packet header + payload.
34 *
35 * Constructs a complete MySQL wire-format packet: 3-byte length +
36 * 1-byte sequence number + payload.
37 *
38 * `@param` payload Payload data.
39 * `@param` payload_len Length of payload.
40 * `@param` seq_id Packet sequence number.
41 * `@param` out_buf Output buffer (must be at least payload_len + 4).
42 * `@return` Total packet size (payload_len + 4).
43 */
44 size_t mysql_build_packet(
45 const unsigned char *payload,
46 uint32_t payload_len,
47 uint8_t seq_id,
48 unsigned char *out_buf
49 );
50
51 `#endif` // MYSQL_PROTOCOL_UTILS_H
Add bounds checking to prevent silent header truncation for oversized payloads.
payload_len accepts a 32-bit value but the MySQL packet header encodes only the lower 24 bits. Calling mysql_build_packet() with payload_len > 0xFFFFFF silently truncates the header while copying the full payload, corrupting packet boundaries. Additionally, no validation prevents null payload dereferencing when payload_len > 0.
The codebase handles large payloads correctly at higher protocol layers, but this low-level utility lacks input validation. Add bounds checks:
Suggested guard clauses
size_t mysql_build_packet(
const unsigned char *payload,
uint32_t payload_len,
uint8_t seq_id,
unsigned char *out_buf)
{
+ if (payload_len > 0xFFFFFF) return 0;
+ if (payload_len > 0 && payload == nullptr) return 0;
+
// 3-byte length (little-endian) + 1-byte sequence
out_buf[0] = payload_len & 0xFF;
out_buf[1] = (payload_len >> 8) & 0xFF;
out_buf[2] = (payload_len >> 16) & 0xFF;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/MySQLProtocolUtils.cpp` around lines 50 - 59, The mysql_build_packet
function currently writes only the lower 24 bits of payload_len into the 3-byte
header and blindly memcpy's payload, which corrupts packets for payload_len >
0xFFFFFF and risks dereferencing a null payload; add guard clauses at the start
of mysql_build_packet to (1) validate payload_len is <= 0xFFFFFF and
reject/return an error code (e.g., -1) if it is larger, and (2) ensure that if
payload_len > 0 the payload pointer is non-null and otherwise return an error;
do not perform the header write or memcpy when the checks fail so out_buf,
seq_id, and memcpy are only used after validation.
Signed-off-by: René Cannaò <rene@proxysql.com>
|


Implements #5499. Extracts parse_pgsql_command_complete() from PgSQLFFTO — 17 tests covering INSERT/UPDATE/DELETE/SELECT/FETCH/MOVE/COPY/MERGE/CREATE TABLE/edge cases.
Summary by CodeRabbit
New Features
Tests
Chores