mirror of https://github.com/sysown/proxysql
Merge pull request #4671 from sysown/v3.0_unsupported_features_error
Graceful handling of unsupported featurespull/4685/head
commit
e4be6c2fbe
@ -1,123 +0,0 @@
|
||||
#ifndef __CLASS_CLIENT_SESSION_H
|
||||
#define __CLASS_CLIENT_SESSION_H
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "proxysql.h"
|
||||
#include "cpp.h"
|
||||
#include "MySQL_Variables.h"
|
||||
|
||||
//#include "../deps/json/json.hpp"
|
||||
//using json = nlohmann::json;
|
||||
|
||||
#ifndef PROXYJSON
|
||||
#define PROXYJSON
|
||||
#include "../deps/json/json_fwd.hpp"
|
||||
#endif // PROXYJSON
|
||||
|
||||
class MySQL_Session;
|
||||
class PgSQL_Session;
|
||||
|
||||
#if 0
|
||||
// this code was moved into Base_Session.h
|
||||
/**
|
||||
* @class Session_Regex
|
||||
* @brief Encapsulates regex operations for session handling.
|
||||
*
|
||||
* This class is used for matching patterns in SQL queries, specifically for
|
||||
* settings like sql_log_bin, sql_mode, and time_zone.
|
||||
* See issues #509 , #815 and #816
|
||||
*/
|
||||
class Session_Regex {
|
||||
private:
|
||||
void* opt;
|
||||
void* re;
|
||||
char* s;
|
||||
public:
|
||||
Session_Regex(char* p);
|
||||
~Session_Regex();
|
||||
bool match(char* m);
|
||||
};
|
||||
#endif // 0
|
||||
|
||||
template <class T>
|
||||
class TypeSelector {
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class TypeSelector<T*> {
|
||||
public:
|
||||
TypeSelector(T* _ptr) : ptr(_ptr) { }
|
||||
~TypeSelector() {}
|
||||
TypeSelector<T*>& operator=(T* _ptr) {
|
||||
ptr = _ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
T* operator->() {
|
||||
return ptr;
|
||||
}
|
||||
bool operator==(T* _ptr) { return (ptr == _ptr); }
|
||||
T& operator*() const noexcept { return *ptr; }
|
||||
explicit operator bool() { return ptr != nullptr; }
|
||||
operator T* () {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
T* ptr;
|
||||
};
|
||||
|
||||
/* Issues with forward class declaration
|
||||
template<class T>
|
||||
using Client_Session = TypeSelector<T*>;
|
||||
*/
|
||||
template <class T>
|
||||
class Client_Session : public TypeSelector<T> {
|
||||
public:
|
||||
//Client_Session(const Client_Session<T>&) = default;
|
||||
//Client_Session(Client_Session<T>&&) = default;
|
||||
//Client_Session& operator=(const Client_Session<T>&) = default;
|
||||
//Client_Session& operator=(Client_Session<T>&&) = default;
|
||||
//~Client_Session() = default;
|
||||
using TypeSelector<T>::TypeSelector;
|
||||
};
|
||||
#define TO_CLIENT_SESSION(sess) Client_Session<decltype(sess)>(sess)
|
||||
|
||||
/* Issues with forward class declaration
|
||||
template<class T>
|
||||
using Query_Info_T = TypeSelector<T*>;
|
||||
*/
|
||||
template <class T>
|
||||
class Query_Info_T : public TypeSelector<T> {
|
||||
public:
|
||||
using TypeSelector<T>::TypeSelector;
|
||||
};
|
||||
#define TO_QUERY_INFO(query_info) Query_Info_T<decltype(query_info)>(query_info)
|
||||
|
||||
/* Issues with forward class declaration
|
||||
template<class T>
|
||||
using Data_Stream_T = TypeSelector<T*>;
|
||||
*/
|
||||
template <class T>
|
||||
class Data_Stream_T : public TypeSelector<T> {
|
||||
public:
|
||||
using TypeSelector<T>::TypeSelector;
|
||||
};
|
||||
#define TO_DATA_STREAM(data_stream) Data_Stream_T<decltype(data_stream)>(data_stream)
|
||||
|
||||
/* Issues with forward class declaration
|
||||
template<class T>
|
||||
using Connection_Info_T = TypeSelector<T*>;
|
||||
*/
|
||||
template <class T>
|
||||
class Connection_Info_T : public TypeSelector<T> {
|
||||
public:
|
||||
using TypeSelector<T>::TypeSelector;
|
||||
};
|
||||
#define TO_CONNECTION_INFO(connection_info) Connection_Info_T<decltype(connection_info)>(connection_info)
|
||||
|
||||
std::string proxysql_session_type_str(enum proxysql_session_type session_type);
|
||||
|
||||
#endif /* __CLASS_CLIENT_SESSION_H */
|
||||
@ -0,0 +1,135 @@
|
||||
/**
|
||||
* @file pgsql-unsupported_feature_test-t.cpp
|
||||
* @brief Ensures that ProxySQL does not crash and maintains the connection/session integrity when unsupported queries are executed.
|
||||
* Currently validates:
|
||||
* 1) Prepare Statement
|
||||
* 2) COPY
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include "libpq-fe.h"
|
||||
#include "command_line.h"
|
||||
#include "tap.h"
|
||||
#include "utils.h"
|
||||
|
||||
CommandLine cl;
|
||||
|
||||
PGconn* create_new_connection(bool with_ssl) {
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "host=" << cl.pgsql_host << " port=" << cl.pgsql_port;
|
||||
ss << " user=" << cl.pgsql_username << " password=" << cl.pgsql_password;
|
||||
|
||||
if (with_ssl) {
|
||||
ss << " sslmode=require";
|
||||
} else {
|
||||
ss << " sslmode=disable";
|
||||
}
|
||||
|
||||
PGconn* conn = PQconnectdb(ss.str().c_str());
|
||||
const bool res = (conn && PQstatus(conn) == CONNECTION_OK);
|
||||
ok(res, "Connection created successfully. %s", PQerrorMessage(conn));
|
||||
|
||||
if (res) return conn;
|
||||
|
||||
PQfinish(conn);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void check_transaction_state(PGconn* conn) {
|
||||
PGresult* res;
|
||||
|
||||
// Check if the transaction is still active
|
||||
res = PQexec(conn, "SELECT 1");
|
||||
ok(PQresultStatus(res) == PGRES_TUPLES_OK && PQtransactionStatus(conn) == PQTRANS_INTRANS,
|
||||
"Transaction state was not affected by the error. %s", PQerrorMessage(conn));
|
||||
PQclear(res);
|
||||
}
|
||||
|
||||
void check_prepared_statement_binary(PGconn* conn) {
|
||||
PGresult* res;
|
||||
const char* paramValues[1] = { "1" };
|
||||
|
||||
// Start a transaction
|
||||
res = PQexec(conn, "BEGIN");
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
|
||||
BAIL_OUT("Could not start transaction. %s", PQerrorMessage(conn));
|
||||
}
|
||||
PQclear(res);
|
||||
|
||||
// Test: Prepare a statement (using binary mode)
|
||||
res = PQprepare(conn, "myplan", "SELECT $1::int", 1, NULL);
|
||||
ok(PQresultStatus(res) != PGRES_COMMAND_OK, "Prepare statement failed. %s", PQerrorMessage(conn));
|
||||
PQclear(res);
|
||||
|
||||
// Execute the prepared statement using binary protocol
|
||||
res = PQexecPrepared(conn, "myplan", 1, paramValues, NULL, NULL, 1); // Binary result format (1)
|
||||
ok(PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK, "Prepare statements are not supported for PostgreSQL: %s", PQerrorMessage(conn));
|
||||
PQclear(res);
|
||||
|
||||
// Check if the transaction state is still active
|
||||
check_transaction_state(conn);
|
||||
|
||||
// End the transaction
|
||||
res = PQexec(conn, "ROLLBACK");
|
||||
PQclear(res);
|
||||
}
|
||||
|
||||
void check_copy_binary(PGconn* conn) {
|
||||
PGresult* res;
|
||||
|
||||
// Start a transaction
|
||||
res = PQexec(conn, "BEGIN");
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
|
||||
BAIL_OUT("Could not start transaction. %s", PQerrorMessage(conn));
|
||||
}
|
||||
PQclear(res);
|
||||
|
||||
// Test: COPY binary format
|
||||
res = PQexec(conn, "COPY (SELECT 1) TO STDOUT (FORMAT BINARY)");
|
||||
ok(PQresultStatus(res) != PGRES_COPY_OUT, "COPY binary command failed to start. %s", PQerrorMessage(conn));
|
||||
PQclear(res);
|
||||
|
||||
// Attempt to fetch data in binary mode, expect it to fail
|
||||
char buffer[256];
|
||||
int ret = PQgetCopyData(conn, (char**)&buffer, 1); // Binary mode (1)
|
||||
ok(ret == -2, "COPY in binary mode should have failed. %s", PQerrorMessage(conn));
|
||||
|
||||
// Check if the transaction state is still active
|
||||
check_transaction_state(conn);
|
||||
|
||||
// End the transaction
|
||||
res = PQexec(conn, "ROLLBACK");
|
||||
PQclear(res);
|
||||
}
|
||||
|
||||
void execute_tests(bool with_ssl) {
|
||||
PGconn* conn = create_new_connection(with_ssl);
|
||||
|
||||
if (conn == nullptr)
|
||||
return;
|
||||
|
||||
// Test 1: Prepared Statement in binary mode
|
||||
check_prepared_statement_binary(conn);
|
||||
|
||||
// Test 2: COPY in binary mode
|
||||
check_copy_binary(conn);
|
||||
|
||||
// Close the connection
|
||||
|
||||
PQfinish(conn);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
plan(7); // Total number of tests planned
|
||||
|
||||
if (cl.getEnv())
|
||||
return exit_status();
|
||||
|
||||
execute_tests(false); // without SSL
|
||||
|
||||
return exit_status();
|
||||
}
|
||||
Loading…
Reference in new issue