Skip to content

Refactor PostgreSQL Connection-Level Parameters Handling#4899

Merged
renecannao merged 7 commits intov3.0from
v3.0_refactor_connection_info_param
Apr 10, 2025
Merged

Refactor PostgreSQL Connection-Level Parameters Handling#4899
renecannao merged 7 commits intov3.0from
v3.0_refactor_connection_info_param

Conversation

@rahim-kanji
Copy link
Collaborator

@rahim-kanji rahim-kanji commented Apr 1, 2025

Changes

Previously, ProxySQL enforced a strict subset of PostgreSQL connection parameters as per the official documentation, rejecting any undocumented parameters. However, actual PostgreSQL servers accept additional parameters (e.g., extra_float_digits) and apply them at the connection/session level. To align with PostgreSQL's behavior and improve compatibility, ProxySQL now:

  • Accepts all connection parameters provided by clients, not just the documented subset.
  • Defer parameter validation until after successful authentication to optimize resource usage.

Other changes:

  • Treat client_encoding as normal server parameter/variable.
  • Optimized RESET ALL command handling.

Information regarding the TAP test:

  • Using libpq to test ProxySQL's handling of undocumented parameters isn't possible, as libpq enforces a strict subset of PostgreSQL connection parameters as per the official documentation, rejecting any undocumented parameters. However, actual PostgreSQL servers accept additional parameters (e.g., extra_float_digits) and apply them at the connection/session level. To test this behavior, a raw socket is used to connect to a ProxySQL server and send custom built messages to communicate with ProxySQL. It currently works with plain text password authentication, without ssl support.

  • Failure due to an invalid parameter returned by the PostgreSQL server, differs from ProxySQL's behavior. PostgreSQL returns an error during the connection handshake phase, whereas in ProxySQL, the connection succeeds, but the error is encountered when executing a query. This is behaviour is intentional, as newer PostgreSQL versions may introduce new parameters that ProxySQL is not yet aware of.

Closes #4890
Closes #4898

* Accept all parameters sent by the client, mirroring PostgreSQL's permissive handling.
* Validate and apply parameters only after successful authentication.
  This avoids wasting resources on invalid connections.
Optimized RESET ALL command handling.
Few optimizations.
Code cleanup
Note:
  - Using libpq to test ProxySQL's handling of undocumented parameters isn't possible, as libpq enforces a strict subset of PostgreSQL
    connection parameters as per the official documentation, rejecting any undocumented parameters. However, actual
    PostgreSQL servers accept additional parameters (e.g., extra_float_digits) and apply them at the connection/session level.
    To test this behavior, a raw socket is used to connect to a ProxySQL server and send custom built messages to communicate
    with ProxySQL. It currently works with plain text password authentication, without ssl support.

  - Failure due to an invalid parameter returned by the PostgreSQL server, differs from ProxySQL's behavior.
    PostgreSQL returns an error during the connection handshake phase, whereas in ProxySQL, the connection succeeds,
    but the error is encountered when executing a query.
    This is behaviour is intentional, as newer PostgreSQL versions may introduce new parameters that ProxySQL is not yet aware of.
…ving

direct usage of pgsql_tracked_variables[idx].idx. This change improves
consistency and reduces potential human error.
* Fixed some warning
Copy link
Contributor

@noizu noizu left a comment

Choose a reason for hiding this comment

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

This looks like working code to me.

I would avoid the

#if FALSE

#endif

macros if these serve a purpose for debugging give them a meaningful macro name or at least include a comment in the macro'd out code for why it's macro'd out. // Debug X functionality, etc.

Maybe verify mallocs but it's highly unlikely to fail and the end user has bigger problems at hand if it does.

Consider sticking to // style comments to make commented out code during dev/debug easier. outside of doc generation comments.

Loooks good to me but I am a neophyte to this code base ^_^.


// Generate parameter array
constexpr const char* param_name[] = {
#define PARAM(name, val) name,
Copy link
Contributor

Choose a reason for hiding this comment

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

would

#define PARAM(name, val)  (name) ,

break the macro logic?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks for pointing this out. In the current context, the macro:
#define PARAM(name, val) name,
is intended to extract just the name part, which is a plain c-string, not a complex expression. Since name is not an expression and there’s no operator precedence or evaluation involved, parentheses are not strictly necessary here.

static const Param_Name_Validation target_session_attrs {(const char*[]){"any","read-write","read-only","primary","standby","prefer-standby",nullptr},0 };
static const Param_Name_Validation load_balance_hosts {(const char*[]){"disable","random",nullptr},-1};
static const std::unordered_map<std::string_view, const Param_Name_Validation*> PgSQL_Param_Name_Str = {
#define PARAM(name, val) {name, val},
Copy link
Contributor

Choose a reason for hiding this comment

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

same question would {(name), (val)}, // break the macro logic.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

(check previous reply)

#undef PARAM
};

#if 0
Copy link
Contributor

Choose a reason for hiding this comment

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

if this isn't removed later on consider a macro with a meaningful name #if DEBUGGING_X

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

(check comments section)

#define PG_EVENT_EXCEPT 0x04
#define PG_EVENT_TIMEOUT 0x08

#if 0
Copy link
Contributor

Choose a reason for hiding this comment

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

magic number, magic exclusion.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

(check comments section)

};
#endif

class PgSQL_Conn_Param {
Copy link
Contributor

Choose a reason for hiding this comment

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

methods are straight forward but a doxygen compatible or whatever we use class doc section might be nice for other code readers. PPgSQL_Conn_Param is straight forward but it is also undocumented params now so might be worth mentioning.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, noted. The class is now documented.

return result;
}

#if 0
Copy link
Contributor

Choose a reason for hiding this comment

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

use a const or define for macro commented out sections like this and or add a comment on why it's disabled.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

(check comments section)

if (validation != nullptr && validation->accepted_values) {
const char** accepted_value = validation->accepted_values;
while (accepted_value != nullptr) {
while (*accepted_value) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nice

@@ -0,0 +1,909 @@
/**
* @file pgsql-connection_parameters_test-t-t.cpp
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we consider an alternative to libpq if not already doing so for validation here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We can, for normal cases, but what we're doing here is somewhat taboo, and other connectors might have the same limitations as libpq. I know the Java Connector also bypasses some rules, but I'm not sure if it's as lenient when the client does the same.

};


int main(int argc, char** argv) {
Copy link
Contributor

Choose a reason for hiding this comment

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

does the testing framework generate branch/line coverage or is it necessary to manually ensure new logic is covered?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, we do have code coverage in place. However, if you're referring to test-wise code coverage (i.e., ensuring every test case covers specific branches or lines of code), then no, that's not automatically handled. We rely on manual verification to ensure that new logic is adequately covered by tests.

@@ -244,7 +244,7 @@ enum mysql_variable_name {
};

/* NOTE:
Copy link
Contributor

Choose a reason for hiding this comment

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

use // style comments to allow commenting out code during dev with out resorting to macro #if logic.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

(check comments section)

@rahim-kanji rahim-kanji force-pushed the v3.0_refactor_connection_info_param branch from 958cf7d to 982be8b Compare April 10, 2025 07:00
@rahim-kanji
Copy link
Collaborator Author

rahim-kanji commented Apr 10, 2025

Thank you for the feedback!

Regarding the use of #if 0, these blocks are currently retained for reference during development and are intended to be cleaned up in a future iteration. Since they are temporary, they haven’t been given specific macro names or explanations.

On the comment style, I appreciate the suggestion. As there's no strict in-house guideline, I typically use // for brief comments (one or two or three lines) and resort to /* */ for longer, multi-line comments. It’s largely a matter of personal preference at this stage.

@renecannao renecannao merged commit 6df3d6b into v3.0 Apr 10, 2025
30 of 152 checks passed
@renecannao renecannao deleted the v3.0_refactor_connection_info_param branch March 7, 2026 20:31
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.

Remove Special Handling for client_encoding Refactor PostgreSQL Connection-Level Parameters Handling

3 participants