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.
proxysql/test/tap/tests/pgsql-basic_tests-t.cpp

657 lines
22 KiB

/**
* @file pgsql-basic_tests-t.cpp
* @brief This test conducts a thorough validation of various PostgreSQL database operations.
* It begins by establishing a valid database connection and confirming successful connectivity.
* Subsequently, the test performs a series of Data Definition Language (DDL) and Data Manipulation Language (DML) operations,
* which include table creation, data insertion, selection, updates, deletions, and transactions.
*/
#include <string>
#include <sstream>
#include "libpq-fe.h"
#include "command_line.h"
#include "tap.h"
#include "utils.h"
#define PQEXEC(conn, query) ({PGresult* res = PQexec(conn, query); \
ExecStatusType status = PQresultStatus(res); \
if (status != PGRES_COMMAND_OK && \
status != PGRES_TUPLES_OK) { \
fprintf(stderr, "File %s, line %d, status %d, %s\n", \
__FILE__, __LINE__, status, PQresultErrorMessage(res)); \
} \
res; \
})
#define PQSENDQUERY(conn,query) ({int send_status = PQsendQuery(conn,query); \
if (send_status != 1) { \
fprintf(stderr, "File %s, line %d, status %d, %s\n", \
__FILE__, __LINE__, status, PQerrorMessage(conn)); \
} \
send_status; \
})
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;
}
// Function to set up the test environment
void setup_database(PGconn* conn) {
PGresult* res;
res = PQEXEC(conn, "DROP TABLE IF EXISTS test_table");
PQclear(res);
res = PQEXEC(conn, "CREATE TABLE test_table (id SERIAL PRIMARY KEY, value TEXT)");
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "Created test_table");
PQclear(res);
res = PQEXEC(conn, "INSERT INTO test_table (value) VALUES ('test1'), ('test2'), ('test3')");
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "Inserted initial records into test_table");
PQclear(res);
}
void test_simple_query(PGconn* conn) {
PGresult* res = PQEXEC(conn, "SELECT 1");
if (PQresultStatus(res) == PGRES_TUPLES_OK) {
ok(1, "Simple SELECT query executed successfully");
int nFields = PQnfields(res);
int nRows = PQntuples(res);
ok(nFields == 1, "Returned one field");
ok(nRows == 1, "Returned one row");
char* result = PQgetvalue(res, 0, 0);
ok(strcmp(result, "1") == 0, "Result is 1");
} else {
ok(0, "Simple SELECT query failed");
}
PQclear(res);
}
void test_insert_query(PGconn* conn) {
PGresult* res = PQEXEC(conn, "INSERT INTO test_table (value) VALUES ('test4')");
if (PQresultStatus(res) == PGRES_COMMAND_OK) {
ok(1, "INSERT query executed successfully");
ok(strcmp(PQcmdTuples(res), "1") == 0, "One row inserted");
} else {
ok(0, "INSERT query failed");
}
PQclear(res);
// Verify insertion
res = PQEXEC(conn, "SELECT value FROM test_table WHERE value = 'test4'");
if (PQresultStatus(res) == PGRES_TUPLES_OK) {
int nRows = PQntuples(res);
ok(nRows == 1, "Inserted row is present");
char* result = PQgetvalue(res, 0, 0);
ok(strcmp(result, "test4") == 0, "Inserted value is correct");
} else {
ok(0, "Failed to verify inserted row");
}
PQclear(res);
}
void test_update_query(PGconn* conn) {
PGresult* res = PQEXEC(conn, "UPDATE test_table SET value = 'updated' WHERE value = 'test2'");
if (PQresultStatus(res) == PGRES_COMMAND_OK) {
ok(1, "UPDATE query executed successfully");
ok(strcmp(PQcmdTuples(res), "1") == 0, "One row updated");
} else {
ok(0, "UPDATE query failed");
}
PQclear(res);
// Verify update
res = PQEXEC(conn, "SELECT value FROM test_table WHERE value = 'updated'");
if (PQresultStatus(res) == PGRES_TUPLES_OK) {
int nRows = PQntuples(res);
ok(nRows == 1, "Updated row is present");
char* result = PQgetvalue(res, 0, 0);
ok(strcmp(result, "updated") == 0, "Updated value is correct");
} else {
ok(0, "Failed to verify updated row");
}
PQclear(res);
}
void test_delete_query(PGconn* conn) {
PGresult* res = PQEXEC(conn, "DELETE FROM test_table WHERE value = 'test3'");
if (PQresultStatus(res) == PGRES_COMMAND_OK) {
ok(1, "DELETE query executed successfully");
ok(strcmp(PQcmdTuples(res), "1") == 0, "One row deleted");
} else {
ok(0, "DELETE query failed");
}
PQclear(res);
// Verify deletion
res = PQEXEC(conn, "SELECT value FROM test_table WHERE value = 'test3'");
if (PQresultStatus(res) == PGRES_TUPLES_OK) {
int nRows = PQntuples(res);
ok(nRows == 0, "Deleted row is no longer present");
} else {
ok(0, "Failed to verify deleted row");
}
PQclear(res);
}
void test_invalid_query(PGconn* conn) {
PGresult* res = PQEXEC(conn, "SELECT * FROM non_existent_table");
ok(PQresultStatus(res) == PGRES_FATAL_ERROR, "Query on non-existent table failed as expected");
PQclear(res);
}
void test_transaction_commit(PGconn* conn) {
PGresult* res = PQEXEC(conn, "BEGIN");
ok(PQtransactionStatus(conn) == PQTRANS_INTRANS, "Connection in Transaction state");
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "BEGIN transaction");
PQclear(res);
res = PQEXEC(conn, "INSERT INTO test_table (value) VALUES ('transaction commit')");
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "INSERT in transaction");
PQclear(res);
res = PQEXEC(conn, "COMMIT");
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "COMMIT transaction");
PQclear(res);
ok(PQtransactionStatus(conn) == PQTRANS_IDLE, "Connection in Idle state");
// Verify commit
res = PQEXEC(conn, "SELECT value FROM test_table WHERE value = 'transaction commit'");
if (PQresultStatus(res) == PGRES_TUPLES_OK) {
int nRows = PQntuples(res);
ok(nRows == 1, "Committed row is present");
char* result = PQgetvalue(res, 0, 0);
ok(strcmp(result, "transaction commit") == 0, "Committed value is correct");
} else {
ok(0, "Failed to verify committed row");
}
PQclear(res);
}
void test_transaction_rollback(PGconn* conn) {
PGresult* res = PQEXEC(conn, "BEGIN");
ok(PQtransactionStatus(conn) == PQTRANS_INTRANS, "Connection in Transaction state");
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "BEGIN transaction");
PQclear(res);
res = PQEXEC(conn, "INSERT INTO test_table (value) VALUES ('transaction rollback')");
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "INSERT in transaction");
PQclear(res);
res = PQEXEC(conn, "ROLLBACK");
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "ROLLBACK transaction");
PQclear(res);
ok(PQtransactionStatus(conn) == PQTRANS_IDLE, "Connection in Idle state");
// Verify rollback
res = PQEXEC(conn, "SELECT value FROM test_table WHERE value = 'transaction rollback'");
if (PQresultStatus(res) == PGRES_TUPLES_OK) {
int nRows = PQntuples(res);
ok(nRows == 0, "Rolled back row is not present");
} else {
ok(0, "Failed to verify rolled back row");
}
PQclear(res);
}
void test_transaction_error(PGconn* conn) {
PGresult* res = PQEXEC(conn, "BEGIN");
ok(PQtransactionStatus(conn) == PQTRANS_INTRANS, "Connection in Transaction state");
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "BEGIN transaction");
PQclear(res);
res = PQEXEC(conn, "SELECT 1/0");
ok(PQresultStatus(res) == PGRES_FATAL_ERROR, "Error result returned");
PQclear(res);
ok(PQtransactionStatus(conn) == PQTRANS_INERROR, "Connection in Error Transaction state");
res = PQEXEC(conn, "ROLLBACK");
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "ROLLBACK transaction");
PQclear(res);
ok(PQtransactionStatus(conn) == PQTRANS_IDLE, "Connection in Idle state");
// Verify rollback
res = PQEXEC(conn, "SELECT value FROM test_table WHERE value = 'transaction rollback'");
if (PQresultStatus(res) == PGRES_TUPLES_OK) {
int nRows = PQntuples(res);
ok(nRows == 0, "Rolled back row is not present");
} else {
ok(0, "Failed to verify rolled back row");
}
PQclear(res);
}
void test_null_value(PGconn* conn) {
PGresult* res = PQEXEC(conn, "INSERT INTO test_table (value) VALUES (NULL)");
if (PQresultStatus(res) == PGRES_COMMAND_OK) {
ok(1, "INSERT NULL value executed successfully");
ok(strcmp(PQcmdTuples(res), "1") == 0, "One row inserted with NULL value");
} else {
ok(0, "INSERT NULL value failed");
}
PQclear(res);
// Verify NULL insertion
res = PQEXEC(conn, "SELECT value FROM test_table WHERE value IS NULL");
if (PQresultStatus(res) == PGRES_TUPLES_OK) {
int nRows = PQntuples(res);
ok(nRows == 1, "Inserted NULL value is present");
} else {
ok(0, "Failed to verify inserted NULL value");
}
PQclear(res);
}
void test_constraint_violation(PGconn* conn) {
PGresult* res = PQEXEC(conn, "INSERT INTO test_table (id, value) VALUES (1, 'duplicate id')");
ok(PQresultStatus(res) == PGRES_FATAL_ERROR, "INSERT with duplicate ID failed as expected");
PQclear(res);
}
void test_multi_statement_transaction(PGconn* conn) {
PGresult* res;
int status;
// Execute multi-statement transaction
status = PQsendQuery(conn, "BEGIN; "
"INSERT INTO test_table (value) VALUES ('multi statement'); "
"UPDATE test_table SET value = 'multi statement updated' WHERE value = 'multi statement'; "
"COMMIT;");
ok(status == 1, "Multi-statement transaction sent");
PQconsumeInput(conn);
while (PQisBusy(conn)) {
PQconsumeInput(conn);
}
// Check result of BEGIN
res = PQgetResult(conn);
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "BEGIN executed successfully");
PQclear(res);
// Check result of INSERT
res = PQgetResult(conn);
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "INSERT executed successfully");
PQclear(res);
// Check result of UPDATE
res = PQgetResult(conn);
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "UPDATE executed successfully");
PQclear(res);
// Check result of COMMIT
res = PQgetResult(conn);
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "COMMIT executed successfully");
PQclear(res);
res = PQgetResult(conn);
ok(PQtransactionStatus(conn) == PQTRANS_IDLE, "Connection in Idle state");
// Verify the results
status = PQsendQuery(conn, "SELECT value FROM test_table WHERE value = 'multi statement updated'");
ok(status == 1, "Verification query sent");
PQconsumeInput(conn);
while (PQisBusy(conn)) {
PQconsumeInput(conn);
}
res = PQgetResult(conn);
if (PQresultStatus(res) == PGRES_TUPLES_OK) {
int nRows = PQntuples(res);
ok(nRows == 1, "Multi-statement transaction committed correctly");
char* result = PQgetvalue(res, 0, 0);
ok(strcmp(result, "multi statement updated") == 0, "Multi-statement transaction result is correct");
} else {
ok(0, "Failed to verify multi-statement transaction");
}
PQclear(res);
PQgetResult(conn);
}
void test_multi_statement_transaction_with_error(PGconn* conn) {
PGresult* res;
int status;
// Execute multi-statement transaction with an error
status = PQSENDQUERY(conn, "BEGIN; "
"INSERT INTO test_table (value) VALUES ('multi statement error'); "
"UPDATE test_table SET value = 'multi statement error updated' WHERE value = 'multi statement error'; "
"INSERT INTO test_table (non_existent_column) VALUES ('error'); "
"COMMIT;");
ok(status == 1, "Multi-statement transaction with error sent");
PQconsumeInput(conn);
while (PQisBusy(conn)) {
PQconsumeInput(conn);
}
// Check result of BEGIN
res = PQgetResult(conn);
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "BEGIN executed successfully");
PQclear(res);
// Check result of INSERT
res = PQgetResult(conn);
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "INSERT executed successfully");
PQclear(res);
// Check result of UPDATE
res = PQgetResult(conn);
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "UPDATE executed successfully");
PQclear(res);
// Check result of erroneous INSERT
res = PQgetResult(conn);
ok(PQresultStatus(res) == PGRES_FATAL_ERROR, "Erroneous INSERT failed as expected");
PQclear(res);
PQgetResult(conn);
// Ensure the transaction is in error state
ok(PQtransactionStatus(conn) == PQTRANS_INERROR, "Connection in Error Transaction state");
// Rollback the transaction
status = PQsendQuery(conn, "ROLLBACK");
ok(status == 1, "ROLLBACK sent");
PQconsumeInput(conn);
while (PQisBusy(conn)) {
PQconsumeInput(conn);
}
res = PQgetResult(conn);
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "ROLLBACK executed successfully");
PQclear(res);
PQgetResult(conn);
ok(PQtransactionStatus(conn) == PQTRANS_IDLE, "Connection in Idle state");
// Verify the results
status = PQsendQuery(conn, "SELECT value FROM test_table WHERE value = 'multi statement error' OR value = 'multi statement error updated'");
ok(status == 1, "Verification query sent");
PQconsumeInput(conn);
while (PQisBusy(conn)) {
PQconsumeInput(conn);
}
res = PQgetResult(conn);
if (PQresultStatus(res) == PGRES_TUPLES_OK) {
int nRows = PQntuples(res);
ok(nRows == 0, "Multi-statement transaction with error rolled back correctly");
} else {
ok(0, "Failed to verify rollback of multi-statement transaction with error");
}
PQclear(res);
PQgetResult(conn);
}
void test_multi_statement_select_insert(PGconn* conn) {
PGresult* res;
int status;
// Execute multi-statement SELECT and INSERT
status = PQsendQuery(conn, "SELECT value FROM test_table WHERE id = 1; "
"INSERT INTO test_table (value) VALUES ('multi statement select insert');");
ok(status == 1, "Multi-statement SELECT and INSERT sent");
PQconsumeInput(conn);
while (PQisBusy(conn)) {
PQconsumeInput(conn);
}
// Check result of SELECT
res = PQgetResult(conn);
ok(PQresultStatus(res) == PGRES_TUPLES_OK, "SELECT executed successfully");
PQclear(res);
// Check result of INSERT
res = PQgetResult(conn);
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "INSERT executed successfully");
PQclear(res);
PQgetResult(conn);
// Verify the results
status = PQsendQuery(conn, "SELECT value FROM test_table WHERE value = 'multi statement select insert'");
ok(status == 1, "Verification query sent");
PQconsumeInput(conn);
while (PQisBusy(conn)) {
PQconsumeInput(conn);
}
res = PQgetResult(conn);
if (PQresultStatus(res) == PGRES_TUPLES_OK) {
int nRows = PQntuples(res);
ok(nRows == 1, "Multi-statement SELECT and INSERT committed correctly");
char* result = PQgetvalue(res, 0, 0);
ok(strcmp(result, "multi statement select insert") == 0, "Multi-statement SELECT and INSERT result is correct");
} else {
ok(0, "Failed to verify multi-statement SELECT and INSERT");
}
PQclear(res);
PQgetResult(conn);
}
void test_multi_statement_delete_update(PGconn* conn) {
PGresult* res;
int status;
// Execute multi-statement DELETE and UPDATE
status = PQsendQuery(conn, "DELETE FROM test_table WHERE value = 'test1'; "
"UPDATE test_table SET value = 'multi statement delete update' WHERE value = 'test4';");
ok(status == 1, "Multi-statement DELETE and UPDATE sent");
PQconsumeInput(conn);
while (PQisBusy(conn)) {
PQconsumeInput(conn);
}
// Check result of DELETE
res = PQgetResult(conn);
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "DELETE executed successfully");
PQclear(res);
// Check result of UPDATE
res = PQgetResult(conn);
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "UPDATE executed successfully");
PQclear(res);
PQgetResult(conn);
// Verify the results
status = PQsendQuery(conn, "SELECT value FROM test_table WHERE value = 'multi statement delete update'");
ok(status == 1, "Verification query sent");
PQconsumeInput(conn);
while (PQisBusy(conn)) {
PQconsumeInput(conn);
}
res = PQgetResult(conn);
if (PQresultStatus(res) == PGRES_TUPLES_OK) {
int nRows = PQntuples(res);
ok(nRows == 1, "Multi-statement DELETE and UPDATE committed correctly");
char* result = PQgetvalue(res, 0, 0);
ok(strcmp(result, "multi statement delete update") == 0, "Multi-statement DELETE and UPDATE result is correct");
} else {
ok(0, "Failed to verify multi-statement DELETE and UPDATE");
}
PQclear(res);
PQgetResult(conn);
}
void test_multi_statement_with_error(PGconn* conn) {
PGresult* res;
int status;
// Execute multi-statement with an error
status = PQsendQuery(conn, "INSERT INTO test_table (value) VALUES ('multi statement error'); "
"UPDATE test_table SET value = 'multi statement error updated' WHERE value = 'multi statement error'; "
"INSERT INTO test_table (non_existent_column) VALUES ('error');");
ok(status == 1, "Multi-statement with error sent");
PQconsumeInput(conn);
while (PQisBusy(conn)) {
PQconsumeInput(conn);
}
// Check result of INSERT
res = PQgetResult(conn);
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "INSERT executed successfully");
PQclear(res);
// Check result of UPDATE
res = PQgetResult(conn);
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "UPDATE executed successfully");
PQclear(res);
// Check result of erroneous INSERT
res = PQgetResult(conn);
ok(PQresultStatus(res) == PGRES_FATAL_ERROR, "Erroneous INSERT failed as expected");
PQclear(res);
PQgetResult(conn);
// Verify the results
status = PQsendQuery(conn, "SELECT value FROM test_table WHERE value = 'multi statement error' OR value = 'multi statement error updated'");
ok(status == 1, "Verification query sent");
PQconsumeInput(conn);
while (PQisBusy(conn)) {
PQconsumeInput(conn);
}
res = PQgetResult(conn);
if (PQresultStatus(res) == PGRES_TUPLES_OK) {
int nRows = PQntuples(res);
ok(nRows == 0, "No rows are inserted or updated");
} else {
ok(0, "Failed to verify rows from multi-statement with error");
}
PQclear(res);
PQgetResult(conn);
}
void test_multi_statement_insert_select_select(PGconn* conn) {
PGresult* res;
int status;
// Execute multi-statement INSERT, SELECT, and SELECT
status = PQsendQuery(conn, "INSERT INTO test_table (value) VALUES ('multi statement select1'), ('multi statement select2'); "
"SELECT value FROM test_table WHERE value = 'multi statement select1'; "
"SELECT value FROM test_table WHERE value = 'multi statement select2';");
ok(status == 1, "Multi-statement INSERT and SELECTs sent");
PQconsumeInput(conn);
while (PQisBusy(conn)) {
PQconsumeInput(conn);
}
// Check result of the INSERT
res = PQgetResult(conn);
ok(PQresultStatus(res) == PGRES_COMMAND_OK, "INSERT executed successfully");
PQclear(res);
// Check result of the first SELECT
res = PQgetResult(conn);
if (PQresultStatus(res) == PGRES_TUPLES_OK) {
int nRows = PQntuples(res);
ok(nRows == 1, "First SELECT executed successfully");
if (nRows > 0) {
char* result = PQgetvalue(res, 0, 0);
ok(strcmp(result, "multi statement select1") == 0, "First SELECT result is correct");
}
} else {
ok(0, "First SELECT failed");
}
PQclear(res);
// Check result of the second SELECT
res = PQgetResult(conn);
if (PQresultStatus(res) == PGRES_TUPLES_OK) {
int nRows = PQntuples(res);
ok(nRows == 1, "Second SELECT executed successfully");
if (nRows > 0) {
char* result = PQgetvalue(res, 0, 0);
ok(strcmp(result, "multi statement select2") == 0, "Second SELECT result is correct");
}
} else {
ok(0, "Second SELECT failed");
}
PQclear(res);
PQgetResult(conn);
}
void teardown_database(PGconn* conn) {
PGresult* res;
res = PQEXEC(conn, "DROP TABLE IF EXISTS test_table");
PQclear(res);
}
void test_invalid_connection(bool with_ssl) {
std::stringstream ss;
ss << "host=invalid_host port=invalid_port dbname=invalid_db user=invalid_user password=invalid_password";
if (with_ssl) {
ss << " sslmode=require";
} else {
ss << " sslmode=disable";
}
PGconn* conn = PQconnectdb(ss.str().c_str());
ok(PQstatus(conn) == CONNECTION_BAD, "Connection failed with invalid parameters");
PQfinish(conn);
}
void execute_tests(bool with_ssl) {
PGconn* conn = create_new_connection(with_ssl);
if (conn == nullptr)
return;
setup_database(conn);
test_simple_query(conn);
test_insert_query(conn);
test_update_query(conn);
test_delete_query(conn);
test_invalid_query(conn);
test_transaction_commit(conn);
test_transaction_rollback(conn);
test_transaction_error(conn);
test_null_value(conn);
test_constraint_violation(conn);
test_multi_statement_transaction(conn);
test_multi_statement_transaction_with_error(conn);
test_multi_statement_select_insert(conn);
test_multi_statement_delete_update(conn);
test_multi_statement_with_error(conn);
test_multi_statement_insert_select_select(conn);
teardown_database(conn);
test_invalid_connection(with_ssl);
PQfinish(conn);
}
int main(int argc, char** argv) {
plan(176); // Total number of tests planned
if (cl.getEnv())
return exit_status();
execute_tests(false); // without SSL
execute_tests(true); // with SSL
return exit_status();
}