Skip to content

Fix #5272: Add mysql-select_version_forwarding variable for SELECT VERSION()#5277

Merged
renecannao merged 3 commits intov3.0from
v3.0_5272
Jan 8, 2026
Merged

Fix #5272: Add mysql-select_version_forwarding variable for SELECT VERSION()#5277
renecannao merged 3 commits intov3.0from
v3.0_5272

Conversation

@renecannao
Copy link
Contributor

Problem

Since ProxySQL 3.0.4, SELECT VERSION() queries are intercepted and return ProxySQL's mysql-server_version variable instead of proxying to backend servers. This breaks SQLAlchemy and other MariaDB clients that expect "MariaDB" in the version string.

Example error:

Unknown system variable 'transaction_isolation'

This happens because SQLAlchemy doesn't detect MariaDB (the version is "8.0.11" instead of something like "10.11.15-MariaDB") and tries to use MySQL-specific syntax.

Solution

This PR adds a new global variable mysql-select_version_forwarding with 4 modes:

Mode Name Behavior
0 never Always return ProxySQL's version (3.0.4+ behavior)
1 always Always proxy to backend (3.0.3 behavior)
2 smart (fallback to 0) Try backend connection first, else return ProxySQL version
3 smart (fallback to 1) Try backend connection first, else proxy to backend (default)

Implementation Details

New Variable

  • Name: mysql-select_version_forwarding
  • Type: Integer
  • Default: 3 (smart with fallback to proxying)

New Function: get_backend_version_for_hostgroup()

Peeks at backend connection versions without removing them from the connection pool:

  • Iterates through servers in the target hostgroup
  • Checks for free connections using ConnectionsFree->conns_length()
  • Returns server_version from first available connection
  • Properly handles locking using wrlock()/wrunlock()

ProxySQL Backend Detection

To avoid recursion when ProxySQL is behind ProxySQL:

  • Checks if version string contains "ProxySQL"
  • Falls back to appropriate behavior based on mode

Usage Example

-- Default (mode 3) - recommended for MariaDB
SET mysql-select_version_forwarding = 3;

-- Mode 2 - use if you want fast fallback to ProxySQL version
SET mysql-select_version_forwarding = 2;

-- Mode 1 - always proxy (like 3.0.3)
SET mysql-select_version_forwarding = 1;

-- Mode 0 - never proxy (like 3.0.4+)
SET mysql-select_version_forwarding = 0;

Why Mode 3 is Default

Mode 3 ensures SQLAlchemy always gets the real MariaDB version:

  • Fast: Returns backend version immediately if a free connection exists
  • Reliable: If no free connection exists, proxies the query to get the real version
  • No broken behavior: Never returns "8.0.11" (ProxySQL version) for MariaDB backends

Files Changed

  • include/proxysql_structs.h - Variable declarations
  • include/MySQL_Thread.h - Variable in MySQL_Threads_Handler
  • include/MySQL_Session.h - Function declaration
  • lib/MySQL_Thread.cpp - Variable initialization and registration
  • lib/MySQL_Session.cpp - Function implementation and SELECT VERSION() handler

Related Issues

Fixes #5272

…RSION()

Since ProxySQL 3.0.4, SELECT VERSION() queries were intercepted and returned
ProxySQL's mysql-server_version variable instead of proxying to backends.
This broke SQLAlchemy for MariaDB which expects "MariaDB" in the version
string.

This commit adds a new variable `mysql-select_version_forwarding` with 4 modes:
- 0 = never: Always return ProxySQL's version (3.0.4+ behavior)
- 1 = always: Always proxy to backend (3.0.3 behavior)
- 2 = smart (fallback to 0): Try backend connection, else ProxySQL version
- 3 = smart (fallback to 1): Try backend connection, else proxy (default)

The implementation includes:
- New global variable mysql_thread___select_version_forwarding
- New function get_backend_version_for_hostgroup() to peek at backend
  connection versions without removing them from the pool
- Modified SELECT VERSION() handler to support all 4 modes
- ProxySQL backend detection to avoid recursion

Mode 3 (default) ensures SQLAlchemy always gets the real MariaDB version
string while maintaining fast response when connections are available.
@gemini-code-assist
Copy link

Summary of Changes

Hello @renecannao, 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 addresses a critical compatibility issue where ProxySQL's interception of SELECT VERSION() queries caused problems for database clients like SQLAlchemy, which rely on specific version string formats (e.g., 'MariaDB') for proper functionality. The solution introduces a new configurable variable, mysql-select_version_forwarding, allowing administrators to precisely control how ProxySQL responds to these queries. This provides options to return ProxySQL's own version, always forward to a backend, or intelligently attempt to retrieve a backend's version from existing connections, thereby significantly improving client compatibility without sacrificing performance.

Highlights

  • New Configuration Variable: Introduced mysql-select_version_forwarding to control how ProxySQL handles SELECT VERSION() queries, addressing client compatibility issues.
  • Flexible Version Reporting: Provides four distinct modes (never, always, smart-fallback-to-proxysql, smart-fallback-to-backend) to determine whether to return ProxySQL's version or forward the query to a backend server.
  • Smart Backend Version Retrieval: Implemented get_backend_version_for_hostgroup() to safely peek at backend server versions from the connection pool without disrupting existing connections.
  • Enhanced Client Compatibility: Resolves issues with clients like SQLAlchemy that expect specific version strings (e.g., 'MariaDB') by allowing ProxySQL to return the actual backend server's version when configured.
  • Default Smart Behavior: The default mode (3) intelligently attempts to retrieve a backend version from a free connection, falling back to proxying the query if no version is readily available, ensuring reliable version reporting.

🧠 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 Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new configuration variable, mysql-select_version_forwarding, to control how ProxySQL responds to SELECT VERSION() queries. The changes implement four distinct modes: always returning ProxySQL's version, always forwarding the query to a backend, or two 'smart' modes that attempt to retrieve a backend's version from an available connection before falling back to either ProxySQL's version or forwarding the query. A new helper function, get_backend_version_for_hostgroup, was added to facilitate retrieving backend server versions. Review comments suggest improving code readability and maintainability by replacing the integer modes with an enum and refactoring the duplicated logic present in the 'smart' modes.

if ((pkt->size==SELECT_MYSQL_VERSION_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_MYSQL_VERSION,(char *)pkt->ptr+5,pkt->size-5)==0) ||
(pkt->size==SELECT_MYSQL_VERSION_FUNC_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_MYSQL_VERSION_FUNC,(char *)pkt->ptr+5,pkt->size-5)==0)) {
char *version_to_return = NULL;
int mode = mysql_thread___select_version_forwarding; // 0=never, 1=always, 2=smart(fallback to 0), 3=smart(fallback to 1)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The modes 0, 1, 2, 3 for mysql-select_version_forwarding are used as magic numbers. To improve readability and maintainability, consider defining an enum for these modes. This would make the code self-documenting and less prone to errors. For example:

enum SelectVersionForwardingMode {
    NEVER = 0,
    ALWAYS = 1,
    SMART_FALLBACK_INTERNAL = 2,
    SMART_FALLBACK_PROXY = 3
};

This enum could be defined in a shared header like include/MySQL_Thread.h to be accessible where needed.

Comment on lines +7070 to +7109
else if (mode == 2) {
// smart (fallback to 0): try to get version from backend connection, else use ProxySQL version
int target_hg = (current_hostgroup >= 0) ? current_hostgroup : default_hostgroup;

if (target_hg >= 0) {
version_to_return = get_backend_version_for_hostgroup(target_hg);

// Check if backend is ProxySQL (to avoid recursion)
if (version_to_return && strstr(version_to_return, "ProxySQL")) {
version_to_return = NULL;
}
}

// Fallback to ProxySQL version if no backend version found
if (!version_to_return) {
version_to_return = mysql_thread___server_version;
}
}
else if (mode == 3) {
// smart (fallback to 1): try to get version from backend connection, else proxy to backend
int target_hg = (current_hostgroup >= 0) ? current_hostgroup : default_hostgroup;

if (target_hg >= 0) {
version_to_return = get_backend_version_for_hostgroup(target_hg);

// Check if backend is ProxySQL (to avoid recursion)
if (version_to_return && strstr(version_to_return, "ProxySQL")) {
version_to_return = NULL;
}
}

// Fallback: if no backend version found, proxy to backend (mode 1 behavior)
if (!version_to_return) {
return false;
}
}
else {
// mode 0 (never): use ProxySQL's version
version_to_return = mysql_thread___server_version;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for modes 2 and 3 is very similar, leading to some code duplication. This can be refactored to make the code more concise and easier to maintain by extracting the common logic for finding a backend version.

                else if (mode == 2 || mode == 3) {
                        // smart modes
                        int target_hg = (current_hostgroup >= 0) ? current_hostgroup : default_hostgroup;

                        if (target_hg >= 0) {
                                version_to_return = get_backend_version_for_hostgroup(target_hg);

                                // Check if backend is ProxySQL (to avoid recursion)
                                if (version_to_return && strstr(version_to_return, "ProxySQL")) {
                                        version_to_return = NULL;
                                }
                        }

                        if (!version_to_return) {
                                if (mode == 2) { // fallback to 0
                                        version_to_return = mysql_thread___server_version;
                                } else { // mode == 3, fallback to 1
                                        return false;
                                }
                        }
                }
                else { // mode == 0
                        // mode 0 (never): use ProxySQL's version
                        version_to_return = mysql_thread___server_version;
                }

…ling

- Add SelectVersionForwardingMode enum to replace magic numbers (0,1,2,3)
- Refactor modes 2 and 3 to eliminate code duplication
- Improve code readability and maintainability

Addresses feedback from gemini-code-assist on PR #5277
@renecannao renecannao added this to the Release 3.0.5 milestone Jan 7, 2026
The test was failing because the default mode 3 (smart with fallback to
proxying) tries to proxy the query when no backends exist, causing the
query to fail.

Changes:
- Split test into two scenarios:
  1. Mode 3 (explicitly set): Expected to FAIL with no backends
  2. Mode 2: Expected to SUCCEED with no backends (fallback to internal)
- Added extensive documentation explaining:
  * All 4 modes of mysql-select_version_forwarding
  * Why mode 3 fails with no backends (correct behavior)
  * Why mode 2 succeeds with no backends (fallback to internal version)
  * Test execution flow and why both scenarios matter
- Refactored test into test_mode() helper function for clarity
@sonarqubecloud
Copy link

sonarqubecloud bot commented Jan 7, 2026

@mirostauder
Copy link
Contributor

retest this please

1 similar comment
@renecannao
Copy link
Contributor Author

retest this please

@renecannao renecannao merged commit 591e1bc into v3.0 Jan 8, 2026
142 of 154 checks passed
janhorstmann added a commit to osism/container-images-kolla that referenced this pull request Jan 29, 2026
This reverts commit 186ea8b.

ProxySQL version 3.0.5 has been released with a fix for this issue
([1]).

[1]
sysown/proxysql#5277
janhorstmann added a commit to osism/container-images-kolla that referenced this pull request Jan 29, 2026
This reverts commit 186ea8b.

ProxySQL version 3.0.5 has been released with a fix for this issue
([1]).

[1]
sysown/proxysql#5277

Signed-off-by: Jan Horstmann <horstmann@osism.tech>
berendt pushed a commit to osism/container-images-kolla that referenced this pull request Feb 2, 2026
This reverts commit 186ea8b.

ProxySQL version 3.0.5 has been released with a fix for this issue
([1]).

[1]
sysown/proxysql#5277

Signed-off-by: Jan Horstmann <horstmann@osism.tech>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ProxySQL 3.0.4 doesn't proxy SELECT VERSION() and breaks clients for MariaDB

2 participants