mirror of https://github.com/sysown/proxysql
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
186 lines
5.4 KiB
186 lines
5.4 KiB
/**
|
|
* @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 check_copy_stdin_via_extended_query(PGconn* conn) {
|
|
PGresult* res = PQprepare(conn,
|
|
"copy_stmt",
|
|
"COPY mytable FROM STDIN",
|
|
0,
|
|
NULL);
|
|
|
|
ExecStatusType status = PQresultStatus(res);
|
|
|
|
/* Check that it failed */
|
|
ok(status == PGRES_FATAL_ERROR, "PQprepare fails for COPY FROM STDIN");
|
|
|
|
const char* sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
|
|
|
|
ok(sqlstate && strcmp(sqlstate, "0A000") == 0,
|
|
"SQLSTATE is 0A000 (feature_not_supported)");
|
|
|
|
PQclear(res);
|
|
}
|
|
|
|
void check_listen_via_simple_and_extended_query(PGconn* conn) {
|
|
|
|
PGresult* res = PQexec(conn, "LISTEN mychannel");
|
|
ExecStatusType status = PQresultStatus(res);
|
|
/* Check that it failed */
|
|
ok(status == PGRES_FATAL_ERROR, "PQexec fails for LISTEN");
|
|
const char* sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
|
|
ok(sqlstate && strcmp(sqlstate, "0A000") == 0,
|
|
"SQLSTATE is 0A000 (feature_not_supported)");
|
|
PQclear(res);
|
|
res = PQprepare(conn,
|
|
"listen_stmt",
|
|
"LISTEN mychannel",
|
|
0,
|
|
NULL);
|
|
status = PQresultStatus(res);
|
|
/* Check that it failed */
|
|
ok(status == PGRES_FATAL_ERROR, "PQprepare fails for LISTEN");
|
|
sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
|
|
ok(sqlstate && strcmp(sqlstate, "0A000") == 0,
|
|
"SQLSTATE is 0A000 (feature_not_supported)");
|
|
PQclear(res);
|
|
}
|
|
|
|
void execute_tests(bool with_ssl) {
|
|
PGconn* conn = create_new_connection(with_ssl);
|
|
|
|
if (conn == nullptr)
|
|
return;
|
|
|
|
/* Now, supported features
|
|
* Test 1: Prepared Statement in binary mode
|
|
* check_prepared_statement_binary(conn);
|
|
*
|
|
* Test 2: COPY in binary mode
|
|
* check_copy_binary(conn);
|
|
*/
|
|
|
|
// Test 3: COPY FROM STDIN via extended query
|
|
check_copy_stdin_via_extended_query(conn);
|
|
|
|
// Test 4: LISTEN via simple and extended query
|
|
check_listen_via_simple_and_extended_query(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();
|
|
}
|