Merge pull request #20 from sysown/v2.x_postgres_backend

Added generic error handling, transaction detection and handling
v2.x_pg_PrepStmtBase_240714
René Cannaò 2 years ago committed by GitHub
commit 2eb1d0d1c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

7
deps/Makefile vendored

@ -304,7 +304,8 @@ pcre: pcre/pcre/.libs/libpcre.a
postgresql/postgresql/src/interfaces/libpq/libpq.a : libssl/openssl/libssl.a
cd postgresql && rm -rf postgresql-*/ || true
cd postgresql && tar -zxf postgresql-*.tar.gz
cd postgresql/postgresql && LD_LIBRARY_PATH=$(shell pwd)/libssl/openssl ./configure --with-ssl=openssl --with-includes=$(shell pwd)/libssl/openssl/include/ --with-libraries=$(shell pwd)/libssl/openssl/ --without-readline
cd postgresql/postgresql && patch -p0 < ../get_result_from_pgconn.patch
cd postgresql/postgresql && LD_LIBRARY_PATH="$(shell pwd)/libssl/openssl" ./configure --with-ssl=openssl --with-includes="$(shell pwd)/libssl/openssl/include/" --with-libraries="$(shell pwd)/libssl/openssl/" --without-readline
cd postgresql/postgresql/src/interfaces/libpq && CC=${CC} CXX=${CXX} ${MAKE} MAKELEVEL=0
#cd postgresql/postgresql && CC=${CC} CXX=${CXX} ${MAKE} -f src/interfaces/libpq/Makefile all
@ -314,14 +315,14 @@ libusual/libusual/.libs/libusual.a: libssl/openssl/libssl.a
cd libusual && rm -rf libusual-*/ || true
cd libusual && tar -zxf libusual-*.tar.gz
cd libusual/libusual && ./autogen.sh
cd libusual/libusual && LD_LIBRARY_PATH=$(shell pwd)/libssl/openssl ./configure --with-openssl=$(shell pwd)/libssl/openssl/ --disable-shared
cd libusual/libusual && ./configure --with-openssl="$(shell pwd)/libssl/openssl/" --disable-shared
cd libusual/libusual && CC=${CC} CXX=${CXX} ${MAKE}
libusual: libusual/libusual/.libs/libusual.a
libscram/lib/libscram.a: libssl/openssl/libssl.a postgresql/postgresql/src/interfaces/libpq/libpq.a
cd libscram && rm -rf lib/* || true
cd libscram && CC=${CC} CXX=${CXX} ${MAKE} LIBOPENSSL_DIR=$(shell pwd)/libssl/openssl/ POSTGRESQL_DIR=$(shell pwd)/postgresql/postgresql/
cd libscram && CC=${CC} CXX=${CXX} ${MAKE} LIBOPENSSL_DIR="$(shell pwd)/libssl/openssl" POSTGRESQL_DIR="$(shell pwd)/postgresql/postgresql/"
libscram: libscram/lib/libscram.a

@ -0,0 +1,40 @@
diff --git src/interfaces/libpq/fe-exec.c src/interfaces/libpq/fe-exec.c
index fa9d6aad..cd5cd23d 100644
--- src/interfaces/libpq/fe-exec.c
+++ src/interfaces/libpq/fe-exec.c
@@ -4467,3 +4467,20 @@ PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen)
*retbuflen = buflen;
return tmpbuf;
}
+
+/*
+ * PQgetResultFromPGconn
+ * Get error result from PGconn
+ */
+const PGresult *
+PQgetResultFromPGconn(PGconn *conn)
+{
+ if (!conn)
+ return NULL;
+
+ if (conn->asyncStatus != PGASYNC_IDLE)
+ return NULL;
+
+ return conn->result;
+}
+
diff --git src/interfaces/libpq/libpq-fe.h src/interfaces/libpq/libpq-fe.h
index 7476dbe0..472d0083 100644
--- src/interfaces/libpq/libpq-fe.h
+++ src/interfaces/libpq/libpq-fe.h
@@ -668,6 +668,9 @@ extern PQsslKeyPassHook_OpenSSL_type PQgetSSLKeyPassHook_OpenSSL(void);
extern void PQsetSSLKeyPassHook_OpenSSL(PQsslKeyPassHook_OpenSSL_type hook);
extern int PQdefaultSSLKeyPassHook_OpenSSL(char *buf, int size, PGconn *conn);
+/* Get PGresult directly from PGconn. WARNING: DO NOT RELEASE THIS RESULT */
+extern const PGresult *PQgetResultFromPGconn(PGconn *conn);
+
#ifdef __cplusplus
}
#endif

@ -1,5 +1,5 @@
#ifndef __CLASS_POSTGRESQL_BACKEND_H
#define __CLASS_POSTGRESQL_BACKEND_H
#ifndef __CLASS_PGSQL_BACKEND_H
#define __CLASS_PGSQL_BACKEND_H
#include "proxysql.h"
#include "cpp.h"
@ -24,4 +24,4 @@ class PgSQL_Backend
void reset(); //< A method that resets and releases resources associated with this backend instance
};
#endif /* __CLASS_POSTGRESQL_BACKEND_H */
#endif /* __CLASS_PGSQLL_BACKEND_H */

@ -1,8 +1,9 @@
#ifndef __CLASS_POSTGRESQL_CONNECTION_H
#define __CLASS_POSTGRESQL_CONNECTION_H
#ifndef __CLASS_PGSQL_CONNECTION_H
#define __CLASS_PGSQL_CONNECTION_H
#include "proxysql.h"
#include "cpp.h"
#include "PgSQL_Error_Helper.h"
#ifndef PROXYJSON
#define PROXYJSON
@ -449,22 +450,8 @@ class PgSQL_Connection_Placeholder {
void process_rows_in_ASYNC_STMT_EXECUTE_STORE_RESULT_CONT(unsigned long long& processed_bytes);
void async_free_result();
/**
* @brief Returns if the connection is **for sure**, known to be in an active transaction.
* @details The function considers two things:
* 1. If 'server_status' is flagged with 'SERVER_STATUS_IN_TRANS'.
* 2. If the connection has 'autcommit=0' and 'autocommit_false_is_transaction' is set.
* @return True if the connection is known to be in a transaction, or equivalent state.
*/
bool IsKnownActiveTransaction();
/**
* @brief Returns if the connection is in a **potential transaction**.
* @details This function is a more strict version of 'IsKnownActiveTransaction', which also considers
* connections which holds 'unknown_transaction_status' as potentially active transactions.
* @return True if the connection is in potentially in an active transaction.
*/
bool IsActiveTransaction();
bool IsServerOffline();
bool IsAutoCommit();
bool AutocommitFalse_AndSavepoint();
bool MultiplexDisabled(bool check_delay_token = true);
@ -489,14 +476,23 @@ private:
// these will be removed
MySQL_ResultSet *MyRS;
MySQL_ResultSet *MyRS_reuse;
};
enum PG_ERROR_TYPE {
PG_NO_ERROR,
PG_CONNECT_FAILED,
PG_QUERY_FAILED,
PG_RESULT_FAILED,
bool IsServerOffline();
/**
* @brief Returns if the connection is **for sure**, known to be in an active transaction.
* @details The function considers two things:
* 1. If 'server_status' is flagged with 'SERVER_STATUS_IN_TRANS'.
* 2. If the connection has 'autcommit=0' and 'autocommit_false_is_transaction' is set.
* @return True if the connection is known to be in a transaction, or equivalent state.
*/
bool IsKnownActiveTransaction();
/**
* @brief Returns if the connection is in a **potential transaction**.
* @details This function is a more strict version of 'IsKnownActiveTransaction', which also considers
* connections which holds 'unknown_transaction_status' as potentially active transactions.
* @return True if the connection is in potentially in an active transaction.
*/
bool IsActiveTransaction();
};
class PgSQL_Connection : public PgSQL_Connection_Placeholder {
@ -521,41 +517,86 @@ public:
void compute_unknown_transaction_status();
void async_free_result();
void flush();
bool IsActiveTransaction();
bool IsKnownActiveTransaction();
bool IsServerOffline();
bool is_connection_in_reusable_state() const;
int get_server_version() {
return PQserverVersion(pgsql_conn);
}
std::string get_error_code_from_result() const;
int get_protocol_version() {
return PQprotocolVersion(pgsql_conn);
}
bool is_error_present() const {
return err_type != PG_NO_ERROR;
if (error_info.severity == PGSQL_ERROR_SEVERITY::ERRSEVERITY_FATAL ||
error_info.severity == PGSQL_ERROR_SEVERITY::ERRSEVERITY_ERROR ||
error_info.severity == PGSQL_ERROR_SEVERITY::ERRSEVERITY_PANIC) {
return true;
}
return false;
}
PGSQL_ERROR_SEVERITY get_error_severity() const {
return error_info.severity;
}
PG_ERROR_TYPE get_error_type() const {
return err_type;
PGSQL_ERROR_CATEGORY get_error_category() const {
return error_info.category;
}
std::string get_error_message() const {
return err_msg;
const std::string& get_error_message() const {
return error_info.message;
}
void set_error(PG_ERROR_TYPE _err_type, const std::string& _err_msg) {
err_type = _err_type;
err_msg = _err_msg;
std::string get_error_code_with_message() const {
return ("[" + std::string(error_info.sqlstate) + "] " + error_info.message);
}
const char* get_error_code_str() const {
return error_info.sqlstate;
}
PGSQL_ERROR_CODES get_error_code() const {
return error_info.code;
}
void set_error(const char* code, const char* message, bool is_fatal) {
PgSQL_Error_Helper::fill_error_info(error_info, code, message, is_fatal ? "FATAL" : "ERROR");
}
void set_error(PGSQL_ERROR_CODES code, const char* message, bool is_fatal) {
PgSQL_Error_Helper::fill_error_info(error_info, code, message, is_fatal ?
PGSQL_ERROR_SEVERITY::ERRSEVERITY_FATAL : PGSQL_ERROR_SEVERITY::ERRSEVERITY_ERROR);
}
void set_error_from_result(const PGresult* result, uint16_t ext_fields = 0) {
if (result) {
PgSQL_Error_Helper::fill_error_info(error_info, result, ext_fields);
} else {
const char* errmsg = PQerrorMessage(pgsql_conn);
set_error(PGSQL_ERROR_CODES::ERRCODE_RAISE_EXCEPTION, errmsg ? errmsg : "Unknown error", true);
//PgSQL_Error_Helper::fill_error_info_from_error_message(error_info, errmsg);
}
}
void reset_error() {
err_type = PG_NO_ERROR;
err_msg.clear();
reset_error_info(error_info, false);
}
PGresult* get_last_result() const {
return last_result;
}
void set_last_result(PGresult* res) {
void set_last_result(PGresult* result) {
if (last_result) {
PQclear(last_result);
}
last_result = res;
last_result = result;
}
void reset_last_result() {
@ -565,66 +606,18 @@ public:
}
}
void optimize() {}
//PgSQL_Conn_Param conn_params;
//PgSQL_Conn_Param conn_params;
PgSQL_ErrorInfo error_info;
PGconn* pgsql_conn;
PGresult* last_result;
PgSQL_Query_Result* query_result;
PgSQL_Query_Result* query_result_reuse;
PG_ERROR_TYPE err_type;
std::string err_msg;
bool first_result;
//PgSQL_SrvC* parent;
//PgSQL_Connection_userinfo* userinfo;
//PgSQL_Data_Stream* myds;
//int fd;
/*std::string get_error_code(PG_ERROR_TYPE* errtype = NULL) {
assert(pgsql_conn);
std::string error_code = PGCONN_NO_ERROR;
ConnStatusType status = PQstatus(pgsql_conn);
if (status == CONNECTION_BAD) {
if (errtype) *errtype = PG_CONNECTION_ERROR;
error_code = PQparameterStatus(pgsql_conn, "SQLSTATE");
}
else if (pgsql_result != NULL) {
ExecStatusType status = PQresultStatus(pgsql_result);
if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
if (errtype) *errtype = PG_SERVER_ERROR;
error_code = PQresultErrorField(pgsql_result, PG_DIAG_SQLSTATE);
}
}
return error_code;
}
std::string get_error_message(PG_ERROR_TYPE* errtype = NULL) {
assert(pgsql_conn);
std::string error_message{};
ConnStatusType status = PQstatus(pgsql_conn);
if (status == CONNECTION_BAD) {
if (errtype) *errtype = PG_CONNECTION_ERROR;
error_message = PQerrorMessage(pgsql_conn);
}
else if (pgsql_result != NULL) {
ExecStatusType status = PQresultStatus(pgsql_result);
if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
if (errtype) *errtype = PG_SERVER_ERROR;
error_message = PQresultErrorMessage(pgsql_result);
}
}
return error_message;
}*/
};
#endif /* __CLASS_POSTGRESQL_CONNECTION_H */
#endif /* __CLASS_PGSQL_CONNECTION_H */

@ -1,5 +1,5 @@
#ifndef __CLASS_POSTGRESQL_DATA_STREAM_H
#define __CLASS_POSTGRESQL_DATA_STREAM_H
#ifndef __CLASS_PGSQL_DATA_STREAM_H
#define __CLASS_PGSQL_DATA_STREAM_H
#include "proxysql.h"
#include "cpp.h"
@ -58,7 +58,7 @@ public:
}
};
enum pgsql_sslstatus { POSTGRESQL_SSLSTATUS_OK, POSTGRESQL_SSLSTATUS_WANT_IO, POSTGRESQL_SSLSTATUS_FAIL };
enum pgsql_sslstatus { PGSQL_SSLSTATUS_OK, PGSQL_SSLSTATUS_WANT_IO, PGSQL_SSLSTATUS_FAIL };
class PgSQL_Data_Stream
{
@ -269,4 +269,4 @@ public:
void reset_connection();
};
#endif /* __CLASS_MYSQL_DATA_STREAM_H */
#endif /* __CLASS_PGSQL_DATA_STREAM_H */

@ -0,0 +1,672 @@
#ifndef __CLASS_PGSQL_ERROR_HELPER_H
#define __CLASS_PGSQL_ERROR_HELPER_H
#include <string>
#define PGSQL_ERROR_FIELD_TEXT 0x0001
#define PGSQL_ERROR_FIELD_DETAIL 0x0002
#define PGSQL_ERROR_FIELD_HINT 0x0004
#define PGSQL_ERROR_FIELD_POSITION 0x0008
#define PGSQL_ERROR_FIELD_INTERNAL_POSITION 0x0010
#define PGSQL_ERROR_FIELD_INTERNAL_QUERY 0x0020
#define PGSQL_ERROR_FIELD_CONTEXT 0x0040
#define PGSQL_ERROR_FIELD_SCHEMA_NAME 0x0080
#define PGSQL_ERROR_FIELD_TABLE_NAME 0x0100
#define PGSQL_ERROR_FIELD_COLUMN_NAME 0x0200
#define PGSQL_ERROR_FIELD_DATA_TYPE_NAME 0x0400
#define PGSQL_ERROR_FIELD_CONSTRAINT_NAME 0x0800
#define PGSQL_ERROR_FIELD_FILE 0x1000
#define PGSQL_ERROR_FIELD_LINE 0x2000
#define PGSQL_ERROR_FIELD_ROUTINE 0x4000
#define PGSQL_ERROR_FIELD_ALL 0xFFFF
// these are standard SQLSTATES
enum class PGSQL_ERROR_CODES : uint8_t {
ERRCODE_SUCCESSFUL_COMPLETION,
ERRCODE_WARNING,
ERRCODE_DYNAMIC_RESULT_SETS_RETURNED,
ERRCODE_IMPLICIT_ZERO_BIT_PADDING,
ERRCODE_NULL_VALUE_ELIMINATED_IN_SET_FUNCTION,
ERRCODE_PRIVILEGE_NOT_GRANTED,
ERRCODE_PRIVILEGE_NOT_REVOKED,
ERRCODE_STRING_DATA_RIGHT_TRUNCATION,
ERRCODE_DEPRECATED_FEATURE,
ERRCODE_NO_DATA,
ERRCODE_NO_ADDITIONAL_DYNAMIC_RESULT_SETS_RETURNED,
ERRCODE_SQL_STATEMENT_NOT_YET_COMPLETE,
ERRCODE_CONNECTION_EXCEPTION,
ERRCODE_CONNECTION_DOES_NOT_EXIST,
ERRCODE_CONNECTION_FAILURE,
ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION,
ERRCODE_SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION,
ERRCODE_TRANSACTION_RESOLUTION_UNKNOWN,
ERRCODE_PROTOCOL_VIOLATION,
ERRCODE_TRIGGERED_ACTION_EXCEPTION,
ERRCODE_FEATURE_NOT_SUPPORTED,
ERRCODE_INVALID_TRANSACTION_INITIATION,
ERRCODE_LOCATOR_EXCEPTION,
ERRCODE_INVALID_LOCATOR_SPECIFICATION,
ERRCODE_INVALID_GRANTOR,
ERRCODE_INVALID_GRANT_OPERATION,
ERRCODE_INVALID_ROLE_SPECIFICATION,
ERRCODE_CARDINALITY_VIOLATION,
ERRCODE_DATA_EXCEPTION,
ERRCODE_ARRAY_ELEMENT_ERROR,
ERRCODE_ARRAY_SUBSCRIPT_ERROR,
ERRCODE_CHARACTER_NOT_IN_REPERTOIRE,
ERRCODE_DATETIME_FIELD_OVERFLOW,
ERRCODE_DIVISION_BY_ZERO,
ERRCODE_ERROR_IN_ASSIGNMENT,
ERRCODE_ESCAPE_CHARACTER_CONFLICT,
ERRCODE_INDICATOR_OVERFLOW,
ERRCODE_INTERVAL_FIELD_OVERFLOW,
ERRCODE_INVALID_ARGUMENT_FOR_LOG,
ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION,
ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION,
ERRCODE_INVALID_CHARACTER_VALUE_FOR_CAST,
ERRCODE_INVALID_DATETIME_FORMAT,
ERRCODE_INVALID_ESCAPE_CHARACTER,
ERRCODE_INVALID_ESCAPE_OCTET,
ERRCODE_INVALID_ESCAPE_SEQUENCE,
ERRCODE_NONSTANDARD_USE_OF_ESCAPE_CHARACTER,
ERRCODE_INVALID_INDICATOR_PARAMETER_VALUE,
ERRCODE_INVALID_PARAMETER_VALUE,
ERRCODE_INVALID_REGULAR_EXPRESSION,
ERRCODE_INVALID_ROW_COUNT_IN_LIMIT_CLAUSE,
ERRCODE_INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE,
ERRCODE_INVALID_TABLESAMPLE_ARGUMENT,
ERRCODE_INVALID_TABLESAMPLE_REPEAT,
ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE,
ERRCODE_INVALID_USE_OF_ESCAPE_CHARACTER,
ERRCODE_MOST_SPECIFIC_TYPE_MISMATCH,
ERRCODE_NULL_VALUE_NOT_ALLOWED,
ERRCODE_NULL_VALUE_NO_INDICATOR_PARAMETER,
ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE,
ERRCODE_STRING_DATA_LENGTH_MISMATCH,
ERRCODE_SUBSTRING_ERROR,
ERRCODE_TRIM_ERROR,
ERRCODE_UNTERMINATED_C_STRING,
ERRCODE_ZERO_LENGTH_CHARACTER_STRING,
ERRCODE_FLOATING_POINT_EXCEPTION,
ERRCODE_INVALID_TEXT_REPRESENTATION,
ERRCODE_INVALID_BINARY_REPRESENTATION,
ERRCODE_BAD_COPY_FILE_FORMAT,
ERRCODE_UNTRANSLATABLE_CHARACTER,
ERRCODE_NOT_AN_XML_DOCUMENT,
ERRCODE_INVALID_XML_DOCUMENT,
ERRCODE_INVALID_XML_CONTENT,
ERRCODE_INVALID_XML_COMMENT,
ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION,
ERRCODE_INTEGRITY_CONSTRAINT_VIOLATION,
ERRCODE_RESTRICT_VIOLATION,
ERRCODE_NOT_NULL_VIOLATION,
ERRCODE_FOREIGN_KEY_VIOLATION,
ERRCODE_UNIQUE_VIOLATION,
ERRCODE_CHECK_VIOLATION,
ERRCODE_EXCLUSION_VIOLATION,
ERRCODE_INVALID_CURSOR_STATE,
ERRCODE_INVALID_TRANSACTION_STATE,
ERRCODE_ACTIVE_SQL_TRANSACTION,
ERRCODE_BRANCH_TRANSACTION_ALREADY_ACTIVE,
ERRCODE_HELD_CURSOR_REQUIRES_SAME_ISOLATION_LEVEL,
ERRCODE_INAPPROPRIATE_ACCESS_MODE_FOR_BRANCH_TRANSACTION,
ERRCODE_INAPPROPRIATE_ISOLATION_LEVEL_FOR_BRANCH_TRANSACTION,
ERRCODE_NO_ACTIVE_SQL_TRANSACTION_FOR_BRANCH_TRANSACTION,
ERRCODE_READ_ONLY_SQL_TRANSACTION,
ERRCODE_SCHEMA_AND_DATA_STATEMENT_MIXING_NOT_SUPPORTED,
ERRCODE_NO_ACTIVE_SQL_TRANSACTION,
ERRCODE_IN_FAILED_SQL_TRANSACTION,
ERRCODE_IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
ERRCODE_INVALID_SQL_STATEMENT_NAME,
ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION,
ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION,
ERRCODE_INVALID_PASSWORD,
ERRCODE_DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST,
ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST,
ERRCODE_INVALID_TRANSACTION_TERMINATION,
ERRCODE_SQL_ROUTINE_EXCEPTION,
ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT,
ERRCODE_S_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED,
ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED,
ERRCODE_S_R_E_READING_SQL_DATA_NOT_PERMITTED,
ERRCODE_INVALID_CURSOR_NAME,
ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
ERRCODE_E_R_E_CONTAINING_SQL_NOT_PERMITTED,
ERRCODE_E_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED,
ERRCODE_E_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED,
ERRCODE_E_R_E_READING_SQL_DATA_NOT_PERMITTED,
ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION,
ERRCODE_E_R_I_E_INVALID_SQLSTATE_RETURNED,
ERRCODE_E_R_I_E_NULL_VALUE_NOT_ALLOWED,
ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED,
ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED,
ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED,
ERRCODE_SAVEPOINT_EXCEPTION,
ERRCODE_S_E_INVALID_SPECIFICATION,
ERRCODE_INVALID_CATALOG_NAME,
ERRCODE_INVALID_SCHEMA_NAME,
ERRCODE_TRANSACTION_ROLLBACK,
ERRCODE_T_R_INTEGRITY_CONSTRAINT_VIOLATION,
ERRCODE_T_R_SERIALIZATION_FAILURE,
ERRCODE_T_R_STATEMENT_COMPLETION_UNKNOWN,
ERRCODE_T_R_DEADLOCK_DETECTED,
ERRCODE_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION,
ERRCODE_SYNTAX_ERROR,
ERRCODE_INSUFFICIENT_PRIVILEGE,
ERRCODE_CANNOT_COERCE,
ERRCODE_GROUPING_ERROR,
ERRCODE_WINDOWING_ERROR,
ERRCODE_INVALID_RECURSION,
ERRCODE_INVALID_FOREIGN_KEY,
ERRCODE_INVALID_NAME,
ERRCODE_NAME_TOO_LONG,
ERRCODE_RESERVED_NAME,
ERRCODE_DATATYPE_MISMATCH,
ERRCODE_INDETERMINATE_DATATYPE,
ERRCODE_COLLATION_MISMATCH,
ERRCODE_INDETERMINATE_COLLATION,
ERRCODE_WRONG_OBJECT_TYPE,
ERRCODE_GENERATED_ALWAYS,
ERRCODE_UNDEFINED_COLUMN,
ERRCODE_UNDEFINED_CURSOR,
ERRCODE_UNDEFINED_DATABASE,
ERRCODE_UNDEFINED_FUNCTION,
ERRCODE_UNDEFINED_PSTATEMENT,
ERRCODE_UNDEFINED_SCHEMA,
ERRCODE_UNDEFINED_TABLE,
ERRCODE_UNDEFINED_PARAMETER,
ERRCODE_UNDEFINED_OBJECT,
ERRCODE_DUPLICATE_COLUMN,
ERRCODE_DUPLICATE_CURSOR,
ERRCODE_DUPLICATE_DATABASE,
ERRCODE_DUPLICATE_FUNCTION,
ERRCODE_DUPLICATE_PSTATEMENT,
ERRCODE_DUPLICATE_SCHEMA,
ERRCODE_DUPLICATE_TABLE,
ERRCODE_DUPLICATE_ALIAS,
ERRCODE_DUPLICATE_OBJECT,
ERRCODE_AMBIGUOUS_COLUMN,
ERRCODE_AMBIGUOUS_FUNCTION,
ERRCODE_AMBIGUOUS_PARAMETER,
ERRCODE_AMBIGUOUS_ALIAS,
ERRCODE_INVALID_COLUMN_REFERENCE,
ERRCODE_INVALID_COLUMN_DEFINITION,
ERRCODE_INVALID_CURSOR_DEFINITION,
ERRCODE_INVALID_DATABASE_DEFINITION,
ERRCODE_INVALID_FUNCTION_DEFINITION,
ERRCODE_INVALID_PSTATEMENT_DEFINITION,
ERRCODE_INVALID_SCHEMA_DEFINITION,
ERRCODE_INVALID_TABLE_DEFINITION,
ERRCODE_INVALID_OBJECT_DEFINITION,
ERRCODE_WITH_CHECK_OPTION_VIOLATION,
ERRCODE_INSUFFICIENT_RESOURCES,
ERRCODE_DISK_FULL,
ERRCODE_OUT_OF_MEMORY,
ERRCODE_TOO_MANY_CONNECTIONS,
ERRCODE_CONFIGURATION_LIMIT_EXCEEDED,
ERRCODE_PROGRAM_LIMIT_EXCEEDED,
ERRCODE_STATEMENT_TOO_COMPLEX,
ERRCODE_TOO_MANY_COLUMNS,
ERRCODE_TOO_MANY_ARGUMENTS,
ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE,
ERRCODE_OBJECT_IN_USE,
ERRCODE_CANT_CHANGE_RUNTIME_PARAM,
ERRCODE_LOCK_NOT_AVAILABLE,
ERRCODE_UNSAFE_NEW_ENUM_VALUE_USAGE,
ERRCODE_OPERATOR_INTERVENTION,
ERRCODE_QUERY_CANCELED,
ERRCODE_ADMIN_SHUTDOWN,
ERRCODE_CRASH_SHUTDOWN,
ERRCODE_CANNOT_CONNECT_NOW,
ERRCODE_DATABASE_DROPPED,
ERRCODE_IDLE_SESSION_TIMEOUT,
ERRCODE_SYSTEM_ERROR,
ERRCODE_IO_ERROR,
ERRCODE_UNDEFINED_FILE,
ERRCODE_DUPLICATE_FILE,
ERRCODE_CONFIG_FILE_ERROR,
ERRCODE_LOCK_FILE_EXISTS,
ERRCODE_FDW_ERROR,
ERRCODE_FDW_COLUMN_NAME_NOT_FOUND,
ERRCODE_FDW_DYNAMIC_PARAMETER_VALUE_NEEDED,
ERRCODE_FDW_FUNCTION_SEQUENCE_ERROR,
ERRCODE_FDW_INCONSISTENT_DESCRIPTOR_INFORMATION,
ERRCODE_FDW_INVALID_ATTRIBUTE_VALUE,
ERRCODE_FDW_INVALID_COLUMN_NAME,
ERRCODE_FDW_INVALID_COLUMN_NUMBER,
ERRCODE_FDW_INVALID_DATA_TYPE,
ERRCODE_FDW_INVALID_DATA_TYPE_DESCRIPTORS,
ERRCODE_FDW_INVALID_DESCRIPTOR_FIELD_IDENTIFIER,
ERRCODE_FDW_INVALID_HANDLE,
ERRCODE_FDW_INVALID_OPTION_INDEX,
ERRCODE_FDW_INVALID_OPTION_NAME,
ERRCODE_FDW_INVALID_STRING_LENGTH_OR_BUFFER_LENGTH,
ERRCODE_FDW_INVALID_STRING_FORMAT,
ERRCODE_FDW_INVALID_USE_OF_NULL_POINTER,
ERRCODE_FDW_TOO_MANY_HANDLES,
ERRCODE_FDW_OUT_OF_MEMORY,
ERRCODE_FDW_NO_SCHEMAS,
ERRCODE_FDW_OPTION_NAME_NOT_FOUND,
ERRCODE_FDW_REPLY_HANDLE,
ERRCODE_FDW_SCHEMA_NOT_FOUND,
ERRCODE_FDW_TABLE_NOT_FOUND,
ERRCODE_FDW_UNABLE_TO_CREATE_EXECUTION,
ERRCODE_FDW_UNABLE_TO_CREATE_REPLY,
ERRCODE_FDW_UNABLE_TO_ESTABLISH_CONNECTION,
ERRCODE_PLPGSQL_ERROR,
ERRCODE_RAISE_EXCEPTION,
ERRCODE_ASSERT_FAILURE,
ERRCODE_INTERNAL_ERROR,
ERRCODE_DATA_CORRUPTED,
ERRCODE_INDEX_CORRUPTED,
// Add more error codes here if needed. Make sure to update error_code_str also.
ERRCODE_UNKNOWN,
PGSQL_ERROR_CODES_COUNT // This should always be the last entry
};
// Enum to represent different error types
enum class PGSQL_ERROR_CLASS : uint8_t {
ERRCLASS_UNKNOWN_ERROR,
ERRCLASS_SUCCESS,
ERRCLASS_WARNING,
ERRCLASS_NO_DATA,
ERRCLASS_SQL_STATEMENT_NOT_YET_COMPLETE,
ERRCLASS_CONNECTION_EXCEPTION,
ERRCLASS_TRIGGERED_ACTION_EXCEPTION,
ERRCLASS_FEATURE_NOT_SUPPORTED,
ERRCLASS_INVALID_TRANSACTION_INITIATION,
ERRCLASS_LOCATOR_EXCEPTION,
ERRCLASS_INVALID_GRANTOR,
ERRCLASS_INVALID_ROLE_SPECIFICATION,
ERRCLASS_DIAGNOSTICS_EXCEPTION,
ERRCLASS_CASE_NOT_FOUND,
ERRCLASS_CARDINALITY_VIOLATION,
ERRCLASS_DATA_EXCEPTION,
ERRCLASS_INTEGRITY_CONSTRAINT_VIOLATION,
ERRCLASS_INVALID_CURSOR_STATE,
ERRCLASS_INVALID_TRANSACTION_STATE,
ERRCLASS_INVALID_SQL_STATEMENT_NAME,
ERRCLASS_TRIGGERED_DATA_CHANGE_VIOLATION,
ERRCLASS_INVALID_AUTHORIZATION_SPECIFICATION,
ERRCLASS_DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST,
ERRCLASS_INVALID_TRANSACTION_TERMINATION,
ERRCLASS_SQL_ROUTINE_EXCEPTION,
ERRCLASS_INVALID_CURSOR_NAME,
ERRCLASS_EXTERNAL_ROUTINE_EXCEPTION,
ERRCLASS_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION,
ERRCLASS_SAVEPOINT_EXCEPTION,
ERRCLASS_INVALID_CATALOG_NAME,
ERRCLASS_INVALID_SCHEMA_NAME,
ERRCLASS_TRANSACTION_ROLLBACK,
ERRCLASS_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION,
ERRCLASS_WITH_CHECK_OPTION_VIOLATION,
ERRCLASS_INSUFFICIENT_RESOURCES,
ERRCLASS_PROGRAM_LIMIT_EXCEEDED,
ERRCLASS_OBJECT_NOT_IN_PREREQUISITE_STATE,
ERRCLASS_OPERATOR_INTERVENTION,
ERRCLASS_SYSTEM_ERROR_UNSPECIFIED,
ERRCLASS_CONFIG_FILE_ERROR,
ERRCLASS_CRASH_SHUTDOWN,
ERRCLASS_PROTOCOL_VIOLATION,
ERRCLASS_FOREIGN_DATA_WRAPPER_ERROR,
ERRCLASS_PLPGSQL_ERROR,
ERRCLASS_INTERNAL_ERROR,
ERRCLASS_MYSQL_SPECIFIC_ERROR
};
// Enum to represent error categories
enum class PGSQL_ERROR_CATEGORY : uint8_t {
ERRCATEGORY_UNKNOWN_CATEGORY,
ERRCATEGORY_STATUS, // For success and warnings, which are not errors.
ERRCATEGORY_CLIENT_ERROR,
ERRCATEGORY_CONNECTION_ERROR,
ERRCATEGORY_AUTHORIZATION_ERROR,
ERRCATEGORY_RESOURCE_ERROR,
ERRCATEGORY_CONFIGURATION_ERROR,
ERRCATEGORY_SYNTAX_ERROR,
ERRCATEGORY_FEATURE_NOT_SUPPORTED,
ERRCATEGORY_TRANSACTION_ERROR,
ERRCATEGORY_DATA_ERROR,
ERRCATEGORY_ROUTINE_ERROR,
ERRCATEGORY_CURSOR_ERROR,
ERRCATEGORY_EXTERNAL_ROUTINE_ERROR,
ERRCATEGORY_RESOURCE_LIMIT_ERROR,
ERRCATEGORY_OBJECT_STATE_ERROR,
ERRCATEGORY_OPERATOR_INTERVENTION_ERROR,
ERRCATEGORY_FDW_ERROR,
ERRCATEGORY_PLPGSQL_ERROR,
ERRCATEGORY_INTERNAL_ERROR_CATEGORY
};
enum class PGSQL_ERROR_SEVERITY : uint8_t {
ERRSEVERITY_UNKNOWN_SEVERITY,
ERRSEVERITY_FATAL,
ERRSEVERITY_PANIC,
ERRSEVERITY_ERROR,
ERRSEVERITY_WARNING,
ERRSEVERITY_NOTICE,
ERRSEVERITY_DEBUG,
ERRSEVERITY_INFO,
ERRSEVERITY_LOG,
PGSQL_ERROR_SEVERITY_COUNT
};
struct PgSQL_ErrorInfo_Ext {
// bitmap fields present in the error message
PGSQL_ERROR_SEVERITY text;
std::string detail;
std::string hint;
std::string position;
std::string internal_position;
std::string internal_query;
std::string context;
std::string schema_name;
std::string table_name;
std::string column_name;
std::string datatype_name;
std::string constraint_name;
std::string source_file;
std::string source_line;
std::string source_function;
void reset();
};
struct pg_result;
typedef struct pg_result PGresult;
struct PgSQL_ErrorInfo {
PGSQL_ERROR_SEVERITY severity = PGSQL_ERROR_SEVERITY::ERRSEVERITY_UNKNOWN_SEVERITY;
PGSQL_ERROR_CODES code = PGSQL_ERROR_CODES::ERRCODE_SUCCESSFUL_COMPLETION;
PGSQL_ERROR_CLASS type = PGSQL_ERROR_CLASS::ERRCLASS_UNKNOWN_ERROR;
PGSQL_ERROR_CATEGORY category = PGSQL_ERROR_CATEGORY::ERRCATEGORY_UNKNOWN_CATEGORY;
char sqlstate[5 + 1] = {}; // 5 bytes for SQLSTATE + 1 for null terminator
PgSQL_ErrorInfo_Ext* ext_info = NULL;
std::string message;
};
void reset_error_info(PgSQL_ErrorInfo& err_info, bool release_extented);
class PgSQL_Error_Helper {
public:
static constexpr const char* get_error_code(PGSQL_ERROR_CODES code) {
return error_code_str[static_cast<uint8_t>(code)];
}
static constexpr const char* get_severity(PGSQL_ERROR_SEVERITY severity) {
return severity_str[static_cast<uint8_t>(severity)];
}
static void fill_error_info(PgSQL_ErrorInfo& err_info, const PGresult* result, uint16_t ext_fields);
static void fill_error_info(PgSQL_ErrorInfo& err_info, const char* code, const char* msg, const char* severity);
static void fill_error_info(PgSQL_ErrorInfo& err_info, PGSQL_ERROR_CODES code, const char* msg, PGSQL_ERROR_SEVERITY severity);
//static void fill_error_info_from_error_message(PgSQL_ErrorInfo& err_info, const char* error_msg);
private:
static PGSQL_ERROR_CODES identify_error_code(const char* code);
static PGSQL_ERROR_CLASS identify_error_class(const char* code);
static PGSQL_ERROR_CATEGORY categorize_error_class(PGSQL_ERROR_CLASS err_class);
static PGSQL_ERROR_SEVERITY identify_error_severity(const char* severity);
static void fill_extended_error_info(PgSQL_ErrorInfo& err_info, const PGresult* result, uint16_t ext_fields);
/* All the error codes from https://www.postgresql.org/docs/current/errcodes-appendix.html */
static constexpr const char* error_code_str[] = {
"00000", // ERRCODE_SUCCESSFUL_COMPLETION
"01000", // ERRCODE_WARNING
"0100C", // ERRCODE_DYNAMIC_RESULT_SETS_RETURNED
"01008", // ERRCODE_IMPLICIT_ZERO_BIT_PADDING
"01003", // ERRCODE_NULL_VALUE_ELIMINATED_IN_SET_FUNCTION
"01007", // ERRCODE_PRIVILEGE_NOT_GRANTED
"01006", // ERRCODE_PRIVILEGE_NOT_REVOKED
"01004", // ERRCODE_STRING_DATA_RIGHT_TRUNCATION
"01P01", // ERRCODE_DEPRECATED_FEATURE
"02000", // ERRCODE_NO_DATA
"02001", // ERRCODE_NO_ADDITIONAL_DYNAMIC_RESULT_SETS_RETURNED
"03000", // ERRCODE_SQL_STATEMENT_NOT_YET_COMPLETE
"08000", // ERRCODE_CONNECTION_EXCEPTION
"08003", // ERRCODE_CONNECTION_DOES_NOT_EXIST
"08006", // ERRCODE_CONNECTION_FAILURE
"08001", // ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION
"08004", // ERRCODE_SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION
"08007", // ERRCODE_TRANSACTION_RESOLUTION_UNKNOWN
"08P01", // ERRCODE_PROTOCOL_VIOLATION
"09000", // ERRCODE_TRIGGERED_ACTION_EXCEPTION
"0A000", // ERRCODE_FEATURE_NOT_SUPPORTED
"0B000", // ERRCODE_INVALID_TRANSACTION_INITIATION
"0F000", // ERRCODE_LOCATOR_EXCEPTION
"0F001", // ERRCODE_INVALID_LOCATOR_SPECIFICATION
"0L000", // ERRCODE_INVALID_GRANTOR
"0LP01", // ERRCODE_INVALID_GRANT_OPERATION
"0P000", // ERRCODE_INVALID_ROLE_SPECIFICATION
"21000", // ERRCODE_CARDINALITY_VIOLATION
"22000", // ERRCODE_DATA_EXCEPTION
"2202E", // ERRCODE_ARRAY_ELEMENT_ERROR
"2202E", // ERRCODE_ARRAY_SUBSCRIPT_ERROR
"22021", // ERRCODE_CHARACTER_NOT_IN_REPERTOIRE
"22008", // ERRCODE_DATETIME_FIELD_OVERFLOW
"22012", // ERRCODE_DIVISION_BY_ZERO
"22005", // ERRCODE_ERROR_IN_ASSIGNMENT
"2200B", // ERRCODE_ESCAPE_CHARACTER_CONFLICT
"22022", // ERRCODE_INDICATOR_OVERFLOW
"22015", // ERRCODE_INTERVAL_FIELD_OVERFLOW
"2201E", // ERRCODE_INVALID_ARGUMENT_FOR_LOG
"2201F", // ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION
"2201G", // ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION
"22018", // ERRCODE_INVALID_CHARACTER_VALUE_FOR_CAST
"22007", // ERRCODE_INVALID_DATETIME_FORMAT
"22019", // ERRCODE_INVALID_ESCAPE_CHARACTER
"2200D", // ERRCODE_INVALID_ESCAPE_OCTET
"22025", // ERRCODE_INVALID_ESCAPE_SEQUENCE
"22P06", // ERRCODE_NONSTANDARD_USE_OF_ESCAPE_CHARACTER
"22010", // ERRCODE_INVALID_INDICATOR_PARAMETER_VALUE
"22023", // ERRCODE_INVALID_PARAMETER_VALUE
"2201B", // ERRCODE_INVALID_REGULAR_EXPRESSION
"2201W", // ERRCODE_INVALID_ROW_COUNT_IN_LIMIT_CLAUSE
"2201X", // ERRCODE_INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE
"2202H", // ERRCODE_INVALID_TABLESAMPLE_ARGUMENT
"2202G", // ERRCODE_INVALID_TABLESAMPLE_REPEAT
"22009", // ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE
"2200C", // ERRCODE_INVALID_USE_OF_ESCAPE_CHARACTER
"2200G", // ERRCODE_MOST_SPECIFIC_TYPE_MISMATCH
"22004", // ERRCODE_NULL_VALUE_NOT_ALLOWED
"22002", // ERRCODE_NULL_VALUE_NO_INDICATOR_PARAMETER
"22003", // ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE
"22026", // ERRCODE_STRING_DATA_LENGTH_MISMATCH
"22011", // ERRCODE_SUBSTRING_ERROR
"22027", // ERRCODE_TRIM_ERROR
"22024", // ERRCODE_UNTERMINATED_C_STRING
"2200F", // ERRCODE_ZERO_LENGTH_CHARACTER_STRING
"22P01", // ERRCODE_FLOATING_POINT_EXCEPTION
"22P02", // ERRCODE_INVALID_TEXT_REPRESENTATION
"22P03", // ERRCODE_INVALID_BINARY_REPRESENTATION
"22P04", // ERRCODE_BAD_COPY_FILE_FORMAT
"22P05", // ERRCODE_UNTRANSLATABLE_CHARACTER
"2200L", // ERRCODE_NOT_AN_XML_DOCUMENT
"2200M", // ERRCODE_INVALID_XML_DOCUMENT
"2200N", // ERRCODE_INVALID_XML_CONTENT
"2200S", // ERRCODE_INVALID_XML_COMMENT
"2200T", // ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION
"23000", // ERRCODE_INTEGRITY_CONSTRAINT_VIOLATION
"23001", // ERRCODE_RESTRICT_VIOLATION
"23502", // ERRCODE_NOT_NULL_VIOLATION
"23503", // ERRCODE_FOREIGN_KEY_VIOLATION
"23505", // ERRCODE_UNIQUE_VIOLATION
"23514", // ERRCODE_CHECK_VIOLATION
"23P01", // ERRCODE_EXCLUSION_VIOLATION
"24000", // ERRCODE_INVALID_CURSOR_STATE
"25000", // ERRCODE_INVALID_TRANSACTION_STATE
"25001", // ERRCODE_ACTIVE_SQL_TRANSACTION
"25002", // ERRCODE_BRANCH_TRANSACTION_ALREADY_ACTIVE
"25008", // ERRCODE_HELD_CURSOR_REQUIRES_SAME_ISOLATION_LEVEL
"25003", // ERRCODE_INAPPROPRIATE_ACCESS_MODE_FOR_BRANCH_TRANSACTION
"25004", // ERRCODE_INAPPROPRIATE_ISOLATION_LEVEL_FOR_BRANCH_TRANSACTION
"25005", // ERRCODE_NO_ACTIVE_SQL_TRANSACTION_FOR_BRANCH_TRANSACTION
"25006", // ERRCODE_READ_ONLY_SQL_TRANSACTION
"25007", // ERRCODE_SCHEMA_AND_DATA_STATEMENT_MIXING_NOT_SUPPORTED
"25P01", // ERRCODE_NO_ACTIVE_SQL_TRANSACTION
"25P02", // ERRCODE_IN_FAILED_SQL_TRANSACTION
"25P03", // ERRCODE_IDLE_IN_TRANSACTION_SESSION_TIMEOUT
"26000", // ERRCODE_INVALID_SQL_STATEMENT_NAME
"27000", // ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION
"28000", // ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION
"28P01", // ERRCODE_INVALID_PASSWORD
"2B000", // ERRCODE_DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST
"2BP01", // ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST
"2D000", // ERRCODE_INVALID_TRANSACTION_TERMINATION
"2F000", // ERRCODE_SQL_ROUTINE_EXCEPTION
"2F005", // ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT
"2F002", // ERRCODE_S_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED
"2F003", // ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED
"2F004", // ERRCODE_S_R_E_READING_SQL_DATA_NOT_PERMITTED
"34000", // ERRCODE_INVALID_CURSOR_NAME
"38000", // ERRCODE_EXTERNAL_ROUTINE_EXCEPTION
"38001", // ERRCODE_E_R_E_CONTAINING_SQL_NOT_PERMITTED
"38002", // ERRCODE_E_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED
"38003", // ERRCODE_E_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED
"38004", // ERRCODE_E_R_E_READING_SQL_DATA_NOT_PERMITTED
"39000", // ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION
"39001", // ERRCODE_E_R_I_E_INVALID_SQLSTATE_RETURNED
"39004", // ERRCODE_E_R_I_E_NULL_VALUE_NOT_ALLOWED
"39P01", // ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED
"39P02", // ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED
"39P03", // ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED
"3B000", // ERRCODE_SAVEPOINT_EXCEPTION
"3B001", // ERRCODE_S_E_INVALID_SPECIFICATION
"3D000", // ERRCODE_INVALID_CATALOG_NAME
"3F000", // ERRCODE_INVALID_SCHEMA_NAME
"40000", // ERRCODE_TRANSACTION_ROLLBACK
"40002", // ERRCODE_T_R_INTEGRITY_CONSTRAINT_VIOLATION
"40001", // ERRCODE_T_R_SERIALIZATION_FAILURE
"40003", // ERRCODE_T_R_STATEMENT_COMPLETION_UNKNOWN
"40P01", // ERRCODE_T_R_DEADLOCK_DETECTED
"42000", // ERRCODE_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION
"42601", // ERRCODE_SYNTAX_ERROR
"42501", // ERRCODE_INSUFFICIENT_PRIVILEGE
"42846", // ERRCODE_CANNOT_COERCE
"42803", // ERRCODE_GROUPING_ERROR
"42P20", // ERRCODE_WINDOWING_ERROR
"42P19", // ERRCODE_INVALID_RECURSION
"42830", // ERRCODE_INVALID_FOREIGN_KEY
"42602", // ERRCODE_INVALID_NAME
"42622", // ERRCODE_NAME_TOO_LONG
"42939", // ERRCODE_RESERVED_NAME
"42804", // ERRCODE_DATATYPE_MISMATCH
"42P18", // ERRCODE_INDETERMINATE_DATATYPE
"42P21", // ERRCODE_COLLATION_MISMATCH
"42P22", // ERRCODE_INDETERMINATE_COLLATION
"42809", // ERRCODE_WRONG_OBJECT_TYPE
"428C9", // ERRCODE_GENERATED_ALWAYS
"42703", // ERRCODE_UNDEFINED_COLUMN
"34000", // ERRCODE_UNDEFINED_CURSOR
"3D000", // ERRCODE_UNDEFINED_DATABASE
"42883", // ERRCODE_UNDEFINED_FUNCTION
"26000", // ERRCODE_UNDEFINED_PSTATEMENT
"3F000", // ERRCODE_UNDEFINED_SCHEMA
"42P01", // ERRCODE_UNDEFINED_TABLE
"42P02", // ERRCODE_UNDEFINED_PARAMETER
"42704", // ERRCODE_UNDEFINED_OBJECT
"42701", // ERRCODE_DUPLICATE_COLUMN
"42P03", // ERRCODE_DUPLICATE_CURSOR
"42P04", // ERRCODE_DUPLICATE_DATABASE
"42723", // ERRCODE_DUPLICATE_FUNCTION
"42P05", // ERRCODE_DUPLICATE_PSTATEMENT
"42P06", // ERRCODE_DUPLICATE_SCHEMA
"42P07", // ERRCODE_DUPLICATE_TABLE
"42712", // ERRCODE_DUPLICATE_ALIAS
"42710", // ERRCODE_DUPLICATE_OBJECT
"42702", // ERRCODE_AMBIGUOUS_COLUMN
"42725", // ERRCODE_AMBIGUOUS_FUNCTION
"42P08", // ERRCODE_AMBIGUOUS_PARAMETER
"42P09", // ERRCODE_AMBIGUOUS_ALIAS
"42P10", // ERRCODE_INVALID_COLUMN_REFERENCE
"42611", // ERRCODE_INVALID_COLUMN_DEFINITION
"42P11", // ERRCODE_INVALID_CURSOR_DEFINITION
"42P12", // ERRCODE_INVALID_DATABASE_DEFINITION
"42P13", // ERRCODE_INVALID_FUNCTION_DEFINITION
"42P14", // ERRCODE_INVALID_PSTATEMENT_DEFINITION
"42P15", // ERRCODE_INVALID_SCHEMA_DEFINITION
"42P16", // ERRCODE_INVALID_TABLE_DEFINITION
"42P17", // ERRCODE_INVALID_OBJECT_DEFINITION
"44000", // ERRCODE_WITH_CHECK_OPTION_VIOLATION
"53000", // ERRCODE_INSUFFICIENT_RESOURCES
"53100", // ERRCODE_DISK_FULL
"53200", // ERRCODE_OUT_OF_MEMORY
"53300", // ERRCODE_TOO_MANY_CONNECTIONS
"53400", // ERRCODE_CONFIGURATION_LIMIT_EXCEEDED
"54000", // ERRCODE_PROGRAM_LIMIT_EXCEEDED
"54001", // ERRCODE_STATEMENT_TOO_COMPLEX
"54011", // ERRCODE_TOO_MANY_COLUMNS
"54023", // ERRCODE_TOO_MANY_ARGUMENTS
"55000", // ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
"55006", // ERRCODE_OBJECT_IN_USE
"55P02", // ERRCODE_CANT_CHANGE_RUNTIME_PARAM
"55P03", // ERRCODE_LOCK_NOT_AVAILABLE
"55P04", // ERRCODE_UNSAFE_NEW_ENUM_VALUE_USAGE
"57000", // ERRCODE_OPERATOR_INTERVENTION
"57014", // ERRCODE_QUERY_CANCELED
"57P01", // ERRCODE_ADMIN_SHUTDOWN
"57P02", // ERRCODE_CRASH_SHUTDOWN
"57P03", // ERRCODE_CANNOT_CONNECT_NOW
"57P04", // ERRCODE_DATABASE_DROPPED
"57P05", // ERRCODE_IDLE_SESSION_TIMEOUT
"58000", // ERRCODE_SYSTEM_ERROR
"58030", // ERRCODE_IO_ERROR
"58P01", // ERRCODE_UNDEFINED_FILE
"58P02", // ERRCODE_DUPLICATE_FILE
"F0000", // ERRCODE_CONFIG_FILE_ERROR
"F0001", // ERRCODE_LOCK_FILE_EXISTS
"HV000", // ERRCODE_FDW_ERROR
"HV005", // ERRCODE_FDW_COLUMN_NAME_NOT_FOUND
"HV002", // ERRCODE_FDW_DYNAMIC_PARAMETER_VALUE_NEEDED
"HV010", // ERRCODE_FDW_FUNCTION_SEQUENCE_ERROR
"HV021", // ERRCODE_FDW_INCONSISTENT_DESCRIPTOR_INFORMATION
"HV024", // ERRCODE_FDW_INVALID_ATTRIBUTE_VALUE
"HV007", // ERRCODE_FDW_INVALID_COLUMN_NAME
"HV008", // ERRCODE_FDW_INVALID_COLUMN_NUMBER
"HV004", // ERRCODE_FDW_INVALID_DATA_TYPE
"HV006", // ERRCODE_FDW_INVALID_DATA_TYPE_DESCRIPTORS
"HV091", // ERRCODE_FDW_INVALID_DESCRIPTOR_FIELD_IDENTIFIER
"HV00B", // ERRCODE_FDW_INVALID_HANDLE
"HV00C", // ERRCODE_FDW_INVALID_OPTION_INDEX
"HV00D", // ERRCODE_FDW_INVALID_OPTION_NAME
"HV090", // ERRCODE_FDW_INVALID_STRING_LENGTH_OR_BUFFER_LENGTH
"HV00A", // ERRCODE_FDW_INVALID_STRING_FORMAT
"HV009", // ERRCODE_FDW_INVALID_USE_OF_NULL_POINTER
"HV014", // ERRCODE_FDW_TOO_MANY_HANDLES
"HV001", // ERRCODE_FDW_OUT_OF_MEMORY
"HV00P", // ERRCODE_FDW_NO_SCHEMAS
"HV00J", // ERRCODE_FDW_OPTION_NAME_NOT_FOUND
"HV00K", // ERRCODE_FDW_REPLY_HANDLE
"HV00Q", // ERRCODE_FDW_SCHEMA_NOT_FOUND
"HV00R", // ERRCODE_FDW_TABLE_NOT_FOUND
"HV00L", // ERRCODE_FDW_UNABLE_TO_CREATE_EXECUTION
"HV00M", // ERRCODE_FDW_UNABLE_TO_CREATE_REPLY
"HV00N", // ERRCODE_FDW_UNABLE_TO_ESTABLISH_CONNECTION
"P0000", // ERRCODE_PLPGSQL_ERROR
"P0001", // ERRCODE_RAISE_EXCEPTION
"P0002", // ERRCODE_ASSERT_FAILURE
"XX000", // ERRCODE_INTERNAL_ERROR
"XX001", // ERRCODE_DATA_CORRUPTED
"XX002", // ERRCODE_INDEX_CORRUPTED
// Add more error codes here if needed. Make sure to update PGSQL_ERROR_CODES also.
"XXXXX", // ERRCODE_UNKNOWN
};
static_assert(static_cast<uint8_t>(PGSQL_ERROR_CODES::PGSQL_ERROR_CODES_COUNT) == sizeof(error_code_str) / sizeof(char*), "Mismatch between PGSQL_ERROR_CODES_COUNT and error_code_str array size");
static constexpr const char* severity_str[] = {
"UNKNOWN",
"FATAL",
"PANIC",
"ERROR",
"WARNING",
"NOTICE",
"DEBUG",
"INFO",
"LOG"
};
static_assert(static_cast<uint8_t>(PGSQL_ERROR_SEVERITY::PGSQL_ERROR_SEVERITY_COUNT) == sizeof(severity_str) / sizeof(char*), "Mismatch between PGSQL_ERROR_SEVERITY_COUNT and severity_str array size");
};
#define PGSQL_GET_ERROR_CODE_STR(ENUM_CODE) PgSQL_Error_Helper::get_error_code(PGSQL_ERROR_CODES::ENUM_CODE)
#endif /* __CLASS_PGSQL_ERROR_HELPER_H */

@ -1,5 +1,5 @@
#ifndef __CLASS_POSTGRESQL_HOSTGROUPS_MANAGER_H
#define __CLASS_POSTGRESQL_HOSTGROUPS_MANAGER_H
#ifndef __CLASS_PGSQL_HOSTGROUPS_MANAGER_H
#define __CLASS_PGSQL_HOSTGROUPS_MANAGER_H
#include "proxysql.h"
#include "cpp.h"
#include "proxysql_gtid.h"
@ -984,4 +984,4 @@ private:
};
#endif /* __CLASS_POSTGRESQL_HOSTGROUPS_MANAGER_H */
#endif /* __CLASS_PGSQL_HOSTGROUPS_MANAGER_H */

@ -1,5 +1,5 @@
#ifndef __CLASS_POSTGRESQL_LOGGER_H
#define __CLASS_POSTGRESQL_LOGGER_H
#ifndef __CLASS_PGSQL_LOGGER_H
#define __CLASS_PGSQL_LOGGER_H
#include "proxysql.h"
#include "cpp.h"
@ -96,5 +96,4 @@ class PgSQL_Logger {
void wrunlock();
};
#endif /* __CLASS_POSTGRESQL_LOGGER_H */
#endif /* __CLASS_PGSQL_LOGGER_H */

@ -182,24 +182,34 @@ private:
};
class PgSQL_Protocol;
#define PGSQL_QUERY_RESULT_NO_DATA 0x00
#define PGSQL_QUERY_RESULT_TUPLE 0x01
#define PGSQL_QUERY_RESULT_COMMAND 0x02
#define PGSQL_QUERY_RESULT_READY 0x04
#define PGSQL_QUERY_RESULT_ERROR 0x08
#define PGSQL_QUERY_RESULT_EMPTY 0x10
class PgSQL_Query_Result {
public:
PgSQL_Query_Result();
~PgSQL_Query_Result();
void init(PgSQL_Protocol* _proto, PGconn* _conn);
unsigned int add_row_description(PGresult* result);
unsigned int add_row(PGresult* result);
unsigned int add_command_completion(PGresult* result);
unsigned int add_error(PgSQL_Data_Stream* _myds, PGresult* result);
void init(PgSQL_Protocol* _proto, PgSQL_Data_Stream* _myds, PgSQL_Connection* _conn);
unsigned int add_row_description(const PGresult* result);
unsigned int add_row(const PGresult* result);
unsigned int add_command_completion(const PGresult* result);
unsigned int add_error(const PGresult* result);
unsigned int add_empty_query_response(const PGresult* result);
unsigned int add_ready_status(PGTransactionStatusType txn_status);
bool get_resultset(PtrSizeArray* PSarrayFinal);
unsigned long long current_size();
inline bool is_transfer_started() const { return transfer_started; }
inline bool is_resultset_completed() const { return resultset_completed; }
inline unsigned long long get_num_rows() const { return num_rows; }
inline unsigned int get_num_fields() const { return num_fields; }
inline unsigned long long get_resultset_size() const { return resultset_size; }
inline uint8_t get_result_packet_type() const { return result_packet_type; }
private:
void buffer_init();
@ -214,13 +224,12 @@ private:
unsigned long long pkt_count;
unsigned int num_fields;
unsigned int buffer_used;
//ExecStatusType result_status;
unsigned char* buffer;
PgSQL_Protocol* proto;
PGconn* pgsql_conn;
//PGresult* result;
PgSQL_Data_Stream* myds;
PgSQL_Connection* conn;
bool transfer_started;
bool resultset_completed;
uint8_t result_packet_type;
friend class PgSQL_Protocol;
};
@ -240,15 +249,18 @@ public:
EXECUTION_STATE process_handshake_response_packet(unsigned char* pkt, unsigned int len);
void welcome_client();
void generate_error_packet(bool send, bool ready, const char* msg, const char* code, bool fatal, PtrSize_t* _ptr = NULL);
void generate_error_packet(bool send, bool ready, const char* msg, PGSQL_ERROR_CODES code, bool fatal, bool track = false, PtrSize_t* _ptr = NULL);
bool generate_ok_packet(bool send, bool ready, const char* msg, int rows, const char* query, PtrSize_t* _ptr = NULL);
//bool generate_row_description(bool send, PgSQL_Query_Result* rs, const PG_Fields& fields, unsigned int size);
unsigned int copy_row_description_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, PGresult* result);
unsigned int copy_row_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, PGresult* result);
unsigned int copy_command_completion_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, PGresult* result);
unsigned int copy_error_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, PGresult* result);
unsigned int copy_row_description_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, const PGresult* result);
unsigned int copy_row_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, const PGresult* result);
unsigned int copy_command_completion_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, const PGresult* result);
unsigned int copy_error_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, const PGresult* result);
unsigned int copy_empty_query_response_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, const PGresult* result);
unsigned int copy_ready_status_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, PGTransactionStatusType txn_status);
private:
bool get_header(unsigned char* pkt, unsigned int len, pgsql_hdr* hdr);
void load_conn_parameters(pgsql_hdr* pkt, bool startup);

@ -1,5 +1,5 @@
#ifndef __CLASS_POSTGRESQL_SESSION_H
#define __CLASS_POSTGRESQL_SESSION_H
#ifndef __CLASS_PGSQL_SESSION_H
#define __CLASS_PGSQL_SESSION_H
#include <functional>
#include <vector>
@ -221,11 +221,11 @@ private:
void SetQueryTimeout();
bool handler_rc0_PROCESSING_STMT_PREPARE(enum session_status& st, PgSQL_Data_Stream* myds, bool& prepared_stmt_with_no_params);
void handler_rc0_PROCESSING_STMT_EXECUTE(PgSQL_Data_Stream* myds);
bool handler_minus1_ClientLibraryError(PgSQL_Data_Stream* myds, int myerr, char** errmsg);
void handler_minus1_LogErrorDuringQuery(PgSQL_Connection* myconn, int myerr, char* errmsg);
bool handler_minus1_HandleErrorCodes(PgSQL_Data_Stream* myds, int myerr, char** errmsg, int& handler_ret);
void handler_minus1_GenerateErrorMessage(PgSQL_Data_Stream* myds, PgSQL_Connection* myconn, bool& wrong_pass);
void handler_minus1_HandleBackendConnection(PgSQL_Data_Stream* myds, PgSQL_Connection* myconn);
bool handler_minus1_ClientLibraryError(PgSQL_Data_Stream* myds);
void handler_minus1_LogErrorDuringQuery(PgSQL_Connection* myconn);
bool handler_minus1_HandleErrorCodes(PgSQL_Data_Stream* myds, int& handler_ret);
void handler_minus1_GenerateErrorMessage(PgSQL_Data_Stream* myds, bool& wrong_pass);
void handler_minus1_HandleBackendConnection(PgSQL_Data_Stream* myds);
int RunQuery(PgSQL_Data_Stream* myds, PgSQL_Connection* myconn);
void handler___status_WAITING_CLIENT_DATA();
void handler_rc0_Process_GTID(PgSQL_Connection* myconn);
@ -367,7 +367,7 @@ public:
PgSQL_Backend* find_or_create_backend(int, PgSQL_Data_Stream* _myds = NULL);
void SQLite3_to_MySQL(SQLite3_result*, char*, int, MySQL_Protocol*, bool in_transaction = false, bool deprecate_eof_active = false);
void PgSQL_Result_to_PgSQL_wire(PGconn* pgsql_conn, PgSQL_Query_Result* query_result, unsigned int warning_count, PgSQL_Data_Stream* _myds = NULL);
void PgSQL_Result_to_PgSQL_wire(PgSQL_Connection* conn, PgSQL_Data_Stream* _myds = NULL);
void MySQL_Stmt_Result_to_MySQL_wire(MYSQL_STMT* stmt, PgSQL_Connection* myconn);
unsigned int NumActiveTransactions(bool check_savpoint = false);
bool HasOfflineBackends();
@ -410,9 +410,10 @@ public:
bool known_query_for_locked_on_hostgroup(uint64_t);
void unable_to_parse_set_statement(bool*);
bool has_any_backend();
void detected_broken_connection(const char* file, unsigned int line, const char* func, const char* action, PgSQL_Connection* myconn, int myerr, const char* message, bool verbose = false);
void detected_broken_connection(const char* file, unsigned int line, const char* func, const char* action, PgSQL_Connection* myconn, bool verbose = false);
void generate_status_one_hostgroup(int hid, std::string& s);
void reset_warning_hostgroup_flag_and_release_connection();
void set_previous_status_mode3(bool allow_execute = true);
};
#define PgSQL_KILL_QUERY 1
@ -441,4 +442,4 @@ private:
void* PgSQL_kill_query_thread(void* arg);
#endif /* __CLASS_POSTGRESQL_SESSION_H */
#endif /* __CLASS_PGSQL_SESSION_H */

@ -1,8 +1,8 @@
#ifndef __CLASS_POSTGRESQL_THREAD_H
#define __CLASS_POSTGRESQL_THREAD_H
#define ____CLASS_STANDARD_POSTGRESQL_THREAD_H
#include "prometheus/counter.h"
#include "prometheus/gauge.h"
#ifndef __CLASS_PGSQL_THREAD_H
#define __CLASS_PGSQL_THREAD_H
#define ____CLASS_STANDARD_PGSQL_THREAD_H
#include <prometheus/counter.h>
#include <prometheus/gauge.h>
#include "proxysql.h"
#include "Base_Thread.h"
@ -107,8 +107,8 @@ enum PgSQL_Thread_status_variable {
st_var_automatic_detected_sqli,
st_var_mysql_whitelisted_sqli_fingerprint,
st_var_client_host_error_killed_connections,
st_var_END*/
PG_st_var_END
*/
PG_st_var_END = 42 // to avoid ASAN complaining. TO FIX
};
class __attribute__((aligned(64))) PgSQL_Thread : public Base_Thread
@ -463,6 +463,7 @@ public:
char* default_schema;
char* interfaces;
char* keep_multiplexing_variables;
char* default_client_encoding;
//unsigned int default_charset; // removed in 2.0.13 . Obsoleted previously using PgSQL_Variables instead
int handle_unknown_charset;
bool servers_stats;
@ -702,4 +703,4 @@ public:
};
#endif /* __CLASS_POSTGRESQL_THREAD_H */
#endif /* __CLASS_PGSQL_THREAD_H */

@ -1,5 +1,5 @@
#ifndef POSTGRESQL_VARIABLES_H
#define POSTGRESQL_VARIABLES_H
#ifndef PGSQL_VARIABLES_H
#define PGSQL_VARIABLES_H
#include "proxysql.h"
#include "cpp.h"
@ -51,5 +51,5 @@ public:
bool parse_variable_number(PgSQL_Session*sess, int idx, std::string &value1, bool* lock_hostgroup);
};
#endif // POSTGRESQL_VARIABLES_H
#endif // PGSQL_VARIABLES_H

@ -807,6 +807,7 @@ PgSQL_HostGroups_Manager* PgHGM;
__thread int pgsql_thread___authentication_method;
__thread int pgsql_thread___show_processlist_extended;
__thread char *pgsql_thread___server_version;
__thread char *pgsql_thread___default_client_encoding;
__thread bool pgsql_thread___have_ssl;
__thread int pgsql_thread___max_connections;
__thread bool pgsql_thread___use_tcp_keepalive;
@ -1055,6 +1056,7 @@ extern PgSQL_HostGroups_Manager *PgHGM;
extern __thread int pgsql_thread___authentication_method;
extern __thread int pgsql_thread___show_processlist_extended;
extern __thread char *pgsql_thread___server_version;
extern __thread char* pgsql_thread___default_client_encoding;
extern __thread bool pgsql_thread___have_ssl;
extern __thread int pgsql_thread___max_connections;
extern __thread bool pgsql_thread___use_tcp_keepalive;

@ -143,7 +143,7 @@ _OBJ_CXX := ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo
MySQL_encode.oo MySQL_ResultSet.oo \
ProxySQL_Admin_Tests.oo ProxySQL_Admin_Tests2.oo ProxySQL_Admin_Scheduler.oo ProxySQL_Admin_Disk_Upgrade.oo ProxySQL_Admin_Stats.oo \
Base_Session.oo Base_Thread.oo \
proxysql_find_charset.oo ProxySQL_Poll.oo sha256crypt.oo PgSQL_Protocol.oo PgSQL_Thread.oo Client_Session.oo PgSQL_Data_Stream.oo PgSQL_Session.oo PgSQL_Variables.oo PgSQL_HostGroups_Manager.oo PgSQL_Connection.oo PgSQL_Backend.oo PgSQL_Logger.oo PgSQL_Authentication.oo
proxysql_find_charset.oo ProxySQL_Poll.oo sha256crypt.oo PgSQL_Protocol.oo PgSQL_Thread.oo Client_Session.oo PgSQL_Data_Stream.oo PgSQL_Session.oo PgSQL_Variables.oo PgSQL_HostGroups_Manager.oo PgSQL_Connection.oo PgSQL_Backend.oo PgSQL_Logger.oo PgSQL_Authentication.oo PgSQL_Error_Helper.oo
OBJ_CXX := $(patsubst %,$(ODIR)/%,$(_OBJ_CXX))
HEADERS := ../include/*.h ../include/*.hpp

@ -2206,7 +2206,7 @@ void MySQL_HostGroups_Manager::update_table_mysql_servers_for_monitor(bool lock)
int cols = 0;
int affected_rows = 0;
SQLite3_result* resultset = NULL;
char* query = const_cast<char*>("SELECT hostname, port, status, use_ssl FROM mysql_servers WHERE status != 3 GROUP BY hostname, port");
const char* query = "SELECT hostname, port, status, use_ssl FROM mysql_servers WHERE status != 3 GROUP BY hostname, port";
proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "%s\n", query);
mydb->execute_statement(query, &error , &cols , &affected_rows , &resultset);

@ -1,19 +1,18 @@
#include <fcntl.h>
#include <sstream>
#include <atomic>
#include "../deps/json/json.hpp"
using json = nlohmann::json;
#define PROXYJSON
#include "PgSQL_HostGroups_Manager.h"
#include "proxysql.h"
#include "cpp.h"
//#include "SpookyV2.h"
#include <fcntl.h>
#include <sstream>
#include "MySQL_PreparedStatement.h"
#include "PgSQL_Data_Stream.h"
#include "query_processor.h"
#include "MySQL_Variables.h"
#include <atomic>
// some of the code that follows is from mariadb client library memory allocator
typedef int myf; // Type of MyFlags in my_funcs
@ -3054,8 +3053,7 @@ handler_again:
NEXT_IMMEDIATE(ASYNC_CONNECT_TIMEOUT);
}
next_event(ASYNC_CONNECT_CONT);
}
else {
} else {
NEXT_IMMEDIATE(ASYNC_CONNECT_END);
}
break;
@ -3070,50 +3068,28 @@ handler_again:
}
if (is_error_present()) {
// always increase the counter
proxy_error("Failed to PQconnectStart() on %u:%s:%d , FD (Conn:%d , MyDS:%d) , %d: %s.\n", parent->myhgc->hid, parent->address, parent->port, PQsocket(pgsql_conn), myds->fd, PQstatus(pgsql_conn), get_error_message().c_str());
proxy_error("Failed to PQconnectStart() on %u:%s:%d , FD (Conn:%d , MyDS:%d) , %s.\n", parent->myhgc->hid, parent->address, parent->port, PQsocket(pgsql_conn), myds->fd, get_error_code_with_message().c_str());
NEXT_IMMEDIATE(ASYNC_CONNECT_FAILED);
}
else {
} else {
if (PQisnonblocking(pgsql_conn) == false) {
// Set non-blocking mode
if (PQsetnonblocking(pgsql_conn, 1) != 0) {
// WARNING: DO NOT RELEASE this PGresult
const PGresult* result = PQgetResultFromPGconn(pgsql_conn);
set_error_from_result(result);
proxy_error("Failed to set non-blocking mode: %s\n", get_error_code_with_message().c_str());
NEXT_IMMEDIATE(ASYNC_CONNECT_FAILED);
}
}
NEXT_IMMEDIATE(ASYNC_CONNECT_SUCCESSFUL);
}
break;
case ASYNC_CONNECT_SUCCESSFUL:
if (is_connected()) {
//if (pgsql->options.use_ssl == 1)
// if (myds)
// if (myds->sess != NULL)
// if (myds->sess->session_fast_forward == true) {
// assert(myds->ssl == NULL);
// if (myds->ssl == NULL) {
// // check the definition of P_MARIADB_TLS
// /* P_MARIADB_TLS* matls = (P_MARIADB_TLS*)pgsql->net.pvio->ctls;
// if (matls != NULL) {
// myds->encrypted = true;
// myds->ssl = (SSL *)matls->ssl;
// myds->rbio_ssl = BIO_new(BIO_s_mem());
// myds->wbio_ssl = BIO_new(BIO_s_mem());
// SSL_set_bio(myds->ssl, myds->rbio_ssl, myds->wbio_ssl);
// } else {
// // if pgsql->options.use_ssl == 1 but matls == NULL
// // it means that ProxySQL tried to use SSL to connect to the backend
// // but the backend didn't support SSL
// }
// */
// }
// }
}
if (!is_connected())
assert(0); // shouldn't ever reach here, we have messed up the state machine
__sync_fetch_and_add(&PgHGM->status.server_connections_connected, 1);
__sync_fetch_and_add(&parent->connect_OK, 1);
if (PQisnonblocking(pgsql_conn) == false) {
// Set non-blocking mode
if (PQsetnonblocking(pgsql_conn, 1) != 0) {
const std::string errmsg = PQerrorMessage(pgsql_conn);
set_error(PG_CONNECT_FAILED, errmsg);
proxy_error("Failed to set non-blocking mode: %s\n", errmsg.c_str());
NEXT_IMMEDIATE(ASYNC_CONNECT_FAILED);
}
}
//MySQL_Monitor::update_dns_cache_from_mysql_conn(pgsql);
break;
case ASYNC_CONNECT_FAILED:
@ -3144,11 +3120,10 @@ handler_again:
if (async_exit_status) {
next_event(ASYNC_QUERY_CONT);
} else {
#ifdef PROXYSQL_USE_RESULT
if (is_error_present()) {
NEXT_IMMEDIATE(ASYNC_QUERY_END);
}
NEXT_IMMEDIATE(ASYNC_USE_RESULT_START);
#else
NEXT_IMMEDIATE(ASYNC_STORE_RESULT_START);
#endif
}
break;
case ASYNC_QUERY_CONT:
@ -3158,54 +3133,47 @@ handler_again:
if (async_exit_status) {
next_event(ASYNC_QUERY_CONT);
} else {
#ifdef PROXYSQL_USE_RESULT
if (is_error_present()) {
NEXT_IMMEDIATE(ASYNC_QUERY_END);
}
NEXT_IMMEDIATE(ASYNC_USE_RESULT_START);
#else
NEXT_IMMEDIATE(ASYNC_STORE_RESULT_START);
#endif
}
break;
case ASYNC_USE_RESULT_START:
if (is_error_present()) {
NEXT_IMMEDIATE(ASYNC_QUERY_END);
}
fetch_result_start();
if (async_exit_status == PG_EVENT_NONE) {
if (is_error_present()) {
NEXT_IMMEDIATE(ASYNC_QUERY_END);
}
first_result = true;
if (myds->sess->mirror == false) {
if (query_result_reuse == NULL) {
query_result = new PgSQL_Query_Result();
query_result->init(&myds->sess->client_myds->myprot, pgsql_conn);
query_result->init(&myds->sess->client_myds->myprot, myds, this);
}
else {
query_result = query_result_reuse;
query_result_reuse = NULL;
query_result->init(&myds->sess->client_myds->myprot, pgsql_conn);
query_result->init(&myds->sess->client_myds->myprot, myds, this);
}
}
else {
} else {
if (query_result_reuse == NULL) {
query_result = new PgSQL_Query_Result();
query_result->init(NULL, pgsql_conn);
query_result->init(NULL, myds, this);
}
else {
query_result = query_result_reuse;
query_result_reuse = NULL;
query_result->init(NULL, pgsql_conn);
query_result->init(NULL, myds, this);
}
}
NEXT_IMMEDIATE(ASYNC_USE_RESULT_CONT);
}
else {
} else {
assert(0); // shouldn't ever reach here
}
break;
case ASYNC_USE_RESULT_CONT:
{
if (is_error_present()) {
NEXT_IMMEDIATE(ASYNC_QUERY_END);
}
if (myds->sess && myds->sess->client_myds && myds->sess->mirror == false &&
myds->sess->status != SHOW_WARNINGS) { // see issue#4072
unsigned int buffered_data = 0;
@ -3223,33 +3191,63 @@ handler_again:
break;
}
PGresult* result = get_last_result();
const PGresult* result = get_last_result();
if (result) {
switch (PQresultStatus(result)) {
case PGRES_COMMAND_OK:
query_result->add_command_completion(result);
NEXT_IMMEDIATE(ASYNC_USE_RESULT_CONT);
break;
case PGRES_TUPLES_OK:
case PGRES_SINGLE_TUPLE:
break;
default:
//const std::string errmsg = PQresultErrorMessage(result);
//set_error(PG_RESULT_FAILED, errmsg);
//proxy_error("Failed condition in PQresultStatus(). %s\n", errmsg.c_str());
//NEXT_IMMEDIATE(ASYNC_QUERY_END);
query_result->add_error(myds, result);
NEXT_IMMEDIATE(ASYNC_USE_RESULT_CONT);
auto state = PQresultStatus(result);
switch (state) {
case PGRES_COMMAND_OK:
query_result->add_command_completion(result);
NEXT_IMMEDIATE(ASYNC_USE_RESULT_CONT);
break;
case PGRES_EMPTY_QUERY:
query_result->add_empty_query_response(result);
NEXT_IMMEDIATE(ASYNC_USE_RESULT_CONT);
break;
case PGRES_TUPLES_OK:
case PGRES_SINGLE_TUPLE:
break;
case PGRES_COPY_OUT:
case PGRES_COPY_IN:
case PGRES_COPY_BOTH:
// NOT IMPLEMENTED
assert(0);
break;
case PGRES_BAD_RESPONSE:
case PGRES_NONFATAL_ERROR:
case PGRES_FATAL_ERROR:
default:
// we don't have a command completion, empty query responseor error packet in the result. This check is here to
// handle internal cleanup of libpq that might return residual protocol messages from the broken connection and
// may add multiple final packets.
if ((query_result->get_result_packet_type() & (PGSQL_QUERY_RESULT_COMMAND | PGSQL_QUERY_RESULT_EMPTY | PGSQL_QUERY_RESULT_ERROR)) == 0) {
set_error_from_result(result, PGSQL_ERROR_FIELD_ALL);
assert(is_error_present());
// we will not send FATAL error messages to the client
const PGSQL_ERROR_SEVERITY severity = get_error_severity();
if (severity == PGSQL_ERROR_SEVERITY::ERRSEVERITY_ERROR ||
severity == PGSQL_ERROR_SEVERITY::ERRSEVERITY_WARNING ||
severity == PGSQL_ERROR_SEVERITY::ERRSEVERITY_NOTICE) {
query_result->add_error(result);
}
const PGSQL_ERROR_CATEGORY error_category = get_error_category();
if (error_category != PGSQL_ERROR_CATEGORY::ERRCATEGORY_SYNTAX_ERROR &&
error_category != PGSQL_ERROR_CATEGORY::ERRCATEGORY_STATUS &&
error_category != PGSQL_ERROR_CATEGORY::ERRCATEGORY_DATA_ERROR) {
proxy_error("Error: %s\n", get_error_code_with_message().c_str());
}
}
NEXT_IMMEDIATE(ASYNC_USE_RESULT_CONT);
}
if (first_result == true) {
first_result = false;
query_result->add_row_description(result);
first_result = false;
}
if (PQntuples(result) > 0) {
unsigned int br = query_result->add_row(result);
__sync_fetch_and_add(&parent->bytes_recv, br);
myds->sess->thread->status_variables.stvar[st_var_queries_backends_bytes_recv] += br;
@ -3262,8 +3260,7 @@ handler_again:
(pgsql_thread___throttle_ratio_server_to_client && pgsql_thread___throttle_max_bytes_per_second_to_client && (processed_bytes > (unsigned long long)pgsql_thread___throttle_max_bytes_per_second_to_client / 10 * (unsigned long long)pgsql_thread___throttle_ratio_server_to_client))
) {
next_event(ASYNC_USE_RESULT_CONT); // we temporarily pause
}
else {
} else {
NEXT_IMMEDIATE(ASYNC_USE_RESULT_CONT); // we continue looping
}
} else {
@ -3272,11 +3269,17 @@ handler_again:
}
}
if (query_result->is_resultset_completed() == false) {
if (myds) {
query_result->add_error(myds, NULL);
}
if ((query_result->get_result_packet_type() & (PGSQL_QUERY_RESULT_COMMAND | PGSQL_QUERY_RESULT_EMPTY | PGSQL_QUERY_RESULT_ERROR)) == 0) {
// if we reach here we assume that error_info is already set in previous call
if (!is_error_present())
assert(0); // we might have missed setting error_info in previous call
query_result->add_error(NULL);
}
// finally add ready for query packet
query_result->add_ready_status(PQtransactionStatus(pgsql_conn));
NEXT_IMMEDIATE(ASYNC_QUERY_END);
}
break;
@ -3996,10 +3999,19 @@ void PgSQL_Connection::connect_start() {
const std::string& conninfo_str = conninfo.str();
pgsql_conn = PQconnectStart(conninfo_str.c_str());
//pgsql_conn = PQconnectdb(conninfo_str.c_str());
//PQsetErrorVerbosity(pgsql_conn, PQERRORS_VERBOSE);
//PQsetErrorContextVisibility(pgsql_conn, PQSHOW_CONTEXT_ERRORS);
if (pgsql_conn == NULL || PQstatus(pgsql_conn) == CONNECTION_BAD) {
const std::string errmsg = PQerrorMessage(pgsql_conn);
set_error(PG_CONNECT_FAILED, errmsg);
proxy_error("Connect failed. %s\n", errmsg.c_str());
if (pgsql_conn) {
// WARNING: DO NOT RELEASE this PGresult
const PGresult* result = PQgetResultFromPGconn(pgsql_conn);
set_error_from_result(result);
} else {
set_error(PGSQL_GET_ERROR_CODE_STR(ERRCODE_OUT_OF_MEMORY), "Out of memory", false);
}
proxy_error("Connect failed. %s\n", get_error_code_with_message().c_str());
return;
}
fd = PQsocket(pgsql_conn);
@ -4012,6 +4024,7 @@ void PgSQL_Connection::connect_cont(short event) {
reset_error();
async_exit_status = PG_EVENT_NONE;
// For troubleshooting connection issue
#if 0
const char* message = nullptr;
switch (PQstatus(pgsql_conn))
@ -4056,17 +4069,16 @@ void PgSQL_Connection::connect_cont(short event) {
case PGRES_POLLING_READING:
async_exit_status = PG_EVENT_READ;
break;
case PGRES_POLLING_FAILED:
case PGRES_POLLING_OK:
async_exit_status = PG_EVENT_NONE;
break;
//case PGRES_POLLING_FAILED:
default:
{
const std::string errmsg = PQerrorMessage(pgsql_conn);
set_error(PG_CONNECT_FAILED, errmsg);
proxy_error("Connect failed. %s\n", errmsg.c_str());
return;
}
// WARNING: DO NOT RELEASE this PGresult
const PGresult* result = PQgetResultFromPGconn(pgsql_conn);
set_error_from_result(result);
proxy_error("Connect failed. %s\n", get_error_code_with_message().c_str());
return;
}
}
@ -4075,9 +4087,10 @@ void PgSQL_Connection::query_start() {
reset_error();
async_exit_status = PG_EVENT_NONE;
if (PQsendQuery(pgsql_conn, query.ptr) == 0) {
const std::string errmsg = PQerrorMessage(pgsql_conn);
set_error(PG_QUERY_FAILED, errmsg);
proxy_error("Failed to send query. %s\n", errmsg.c_str());
// WARNING: DO NOT RELEASE this PGresult
const PGresult* result = PQgetResultFromPGconn(pgsql_conn);
set_error_from_result(result);
proxy_error("Failed to send query. %s\n", get_error_code_with_message().c_str());
return;
}
flush();
@ -4098,23 +4111,30 @@ void PgSQL_Connection::fetch_result_start() {
reset_error();
async_exit_status = PG_EVENT_NONE;
if (PQsetSingleRowMode(pgsql_conn) == 0) {
const std::string errmsg = PQerrorMessage(pgsql_conn);
set_error(PG_RESULT_FAILED, errmsg);
proxy_error("Failed to set single row mode. %s\n", errmsg.c_str());
// WARNING: DO NOT RELEASE this PGresult
const PGresult* result = PQgetResultFromPGconn(pgsql_conn);
set_error_from_result(result);
proxy_error("Failed to set single row mode. %s\n", get_error_code_with_message().c_str());
return;
}
}
void PgSQL_Connection::fetch_result_cont(short event) {
PROXY_TRACE();
reset_error();
reset_last_result();
async_exit_status = PG_EVENT_NONE;
if (PQconsumeInput(pgsql_conn) == 0) {
const std::string errmsg = PQerrorMessage(pgsql_conn);
set_error(PG_RESULT_FAILED, errmsg);
proxy_error("Failed to consume input. %s\n", errmsg.c_str());
return;
// WARNING: DO NOT RELEASE this PGresult
const PGresult* result = PQgetResultFromPGconn(pgsql_conn);
/* We will only set the error if the result is not NULL or we didn't capture error in last call. If the result is NULL,
* it indicates that an error was already captured during a previous PQconsumeInput call,
* and we do not want to overwrite that information.
*/
if (result || is_error_present() == false) {
set_error_from_result(result);
proxy_error("Failed to consume input. %s\n", get_error_code_with_message().c_str());
}
}
if (PQisBusy(pgsql_conn)) {
@ -4136,9 +4156,10 @@ void PgSQL_Connection::flush() {
async_exit_status = PG_EVENT_READ;
}
else {
const std::string errmsg = PQerrorMessage(pgsql_conn);
set_error(PG_QUERY_FAILED, errmsg);
proxy_error("Failed to flush data backend. %s\n", errmsg.c_str());
// WARNING: DO NOT RELEASE this PGresult
const PGresult* result = PQgetResultFromPGconn(pgsql_conn);
set_error_from_result(result);
proxy_error("Failed to flush data to backend. %s\n", get_error_code_with_message().c_str());
async_exit_status = PG_EVENT_NONE;
}
}
@ -4188,38 +4209,31 @@ bool PgSQL_Connection::is_connected() const {
return true;
}
std::string PgSQL_Connection::get_error_code_from_result() const {
assert(pgsql_conn);
std::string error_code{};
if (last_result != nullptr) {
ExecStatusType status = PQresultStatus(last_result);
if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
error_code = PQresultErrorField(last_result, PG_DIAG_SQLSTATE);
}
}
return error_code;
}
void PgSQL_Connection::compute_unknown_transaction_status() {
if (is_connected()) {
const std::string& errocode = get_error_code_from_result();
if (errocode.empty()) {
unknown_transaction_status = false; // no error
return;
}
if (errocode[0] == 'C') { // client error
// do not change it
if (pgsql_conn) {
// make sure we have not missed even a single error
if (is_error_present() == false) {
unknown_transaction_status = false;
return;
}
if (errocode[0] == 'P') { // server error
/*if (is_connected() == false) {
unknown_transaction_status = true;
return;
}*/
switch (PQtransactionStatus(pgsql_conn)) {
case PQTRANS_INTRANS:
case PQTRANS_INERROR:
case PQTRANS_ACTIVE:
unknown_transaction_status = true;
break;
case PQTRANS_UNKNOWN:
default:
//unknown_transaction_status = false;
break;
}
// all other cases, server error
unknown_transaction_status = true;
}
}
@ -4257,8 +4271,8 @@ void PgSQL_Connection::async_free_result() {
// }
// query.stmt = NULL;
//}
reset_last_result();
}
reset_last_result();
compute_unknown_transaction_status();
async_state_machine = ASYNC_IDLE;
if (query_result) {
@ -4268,6 +4282,7 @@ void PgSQL_Connection::async_free_result() {
query_result_reuse = query_result;
query_result = NULL;
}
first_result = false;
}
int PgSQL_Connection::async_set_autocommit(short event, bool ac) {
@ -4483,3 +4498,71 @@ int PgSQL_Connection::async_ping(short event) {
return 1;
}
bool PgSQL_Connection::IsKnownActiveTransaction() {
bool in_txn = false;
if (pgsql_conn) {
// Get the transaction status
PGTransactionStatusType status = PQtransactionStatus(pgsql_conn);
if (status == PQTRANS_INTRANS || status == PQTRANS_INERROR) {
in_txn = true;
}
}
return in_txn;
}
bool PgSQL_Connection::IsActiveTransaction() {
bool in_txn = false;
if (pgsql_conn) {
// Get the transaction status
PGTransactionStatusType status = PQtransactionStatus(pgsql_conn);
switch (status) {
case PQTRANS_INTRANS:
case PQTRANS_INERROR:
in_txn = true;
break;
case PQTRANS_UNKNOWN:
case PQTRANS_IDLE:
case PQTRANS_ACTIVE:
default:
in_txn = false;
}
if (in_txn == false && is_error_present() && unknown_transaction_status == true) {
in_txn = true;
}
/*if (ret == false) {
//bool r = ( mysql_thread___autocommit_false_is_transaction || mysql_thread___forward_autocommit ); // deprecated , see #3253
bool r = (mysql_thread___autocommit_false_is_transaction);
if (r && (IsAutoCommit() == false)) {
ret = true;
}
}*/
}
return in_txn;
}
bool PgSQL_Connection::IsServerOffline() {
bool ret = false;
if (parent == NULL)
return ret;
server_status = parent->status; // we copy it here to avoid race condition. The caller will see this
if (
(server_status == MYSQL_SERVER_STATUS_OFFLINE_HARD) // the server is OFFLINE as specific by the user
||
(server_status == MYSQL_SERVER_STATUS_SHUNNED && parent->shunned_automatic == true && parent->shunned_and_kill_all_connections == true) // the server is SHUNNED due to a serious issue
||
(server_status == MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG) // slave is lagging! see #774
) {
ret = true;
}
return ret;
}
bool PgSQL_Connection::is_connection_in_reusable_state() const {
const PGTransactionStatusType txn_status = PQtransactionStatus(pgsql_conn);
const bool conn_usable = !(txn_status == PQTRANS_UNKNOWN || txn_status == PQTRANS_ACTIVE);
assert(!(conn_usable == false && is_error_present() == false));
return conn_usable;
}

@ -167,14 +167,14 @@ static enum pgsql_sslstatus get_sslstatus(SSL* ssl, int n)
ERR_clear_error();
switch (err) {
case SSL_ERROR_NONE:
return POSTGRESQL_SSLSTATUS_OK;
return PGSQL_SSLSTATUS_OK;
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_READ:
return POSTGRESQL_SSLSTATUS_WANT_IO;
return PGSQL_SSLSTATUS_WANT_IO;
case SSL_ERROR_ZERO_RETURN:
case SSL_ERROR_SYSCALL:
default:
return POSTGRESQL_SSLSTATUS_FAIL;
return PGSQL_SSLSTATUS_FAIL;
}
}
@ -232,14 +232,14 @@ enum pgsql_sslstatus PgSQL_Data_Stream::do_ssl_handshake() {
long rc = SSL_get_verify_result(ssl);
if (rc != X509_V_OK) {
proxy_error("Disconnecting %s:%d: X509 client SSL certificate verify error: (%ld:%s)\n", addr.addr, addr.port, rc, X509_verify_cert_error_string(rc));
return POSTGRESQL_SSLSTATUS_FAIL;
return PGSQL_SSLSTATUS_FAIL;
}
}
}
status = get_sslstatus(ssl, n);
//proxy_info("SSL status = %d\n", status);
/* Did SSL request to write bytes? */
if (status == POSTGRESQL_SSLSTATUS_WANT_IO) {
if (status == PGSQL_SSLSTATUS_WANT_IO) {
//proxy_info("SSL status is WANT_IO %d\n", status);
do {
n = BIO_read(wbio_ssl, buf, sizeof(buf));
@ -250,7 +250,7 @@ enum pgsql_sslstatus PgSQL_Data_Stream::do_ssl_handshake() {
}
else if (!BIO_should_retry(wbio_ssl)) {
//proxy_info("BIO_should_retry failed\n");
return POSTGRESQL_SSLSTATUS_FAIL;
return PGSQL_SSLSTATUS_FAIL;
}
} while (n > 0);
}
@ -623,7 +623,7 @@ int PgSQL_Data_Stream::read_from_net() {
len -= n2;
if (!SSL_is_init_finished(ssl)) {
//proxy_info("SSL_is_init_finished NOT completed\n");
if (do_ssl_handshake() == POSTGRESQL_SSLSTATUS_FAIL) {
if (do_ssl_handshake() == PGSQL_SSLSTATUS_FAIL) {
//proxy_info("SSL_is_init_finished failed!!\n");
shut_soft();
return -1;
@ -653,7 +653,7 @@ int PgSQL_Data_Stream::read_from_net() {
*/
status = get_sslstatus(ssl, n2);
//proxy_info("SSL status = %d\n", status);
if (status == POSTGRESQL_SSLSTATUS_WANT_IO) {
if (status == PGSQL_SSLSTATUS_WANT_IO) {
do {
n2 = BIO_read(wbio_ssl, buf2, sizeof(buf2));
//proxy_info("BIO_read with %d bytes\n", n2);
@ -666,7 +666,7 @@ int PgSQL_Data_Stream::read_from_net() {
}
} while (n2 > 0);
}
if (status == POSTGRESQL_SSLSTATUS_FAIL) {
if (status == PGSQL_SSLSTATUS_FAIL) {
shut_soft();
return -1;
}
@ -867,7 +867,7 @@ void PgSQL_Data_Stream::set_pollout() {
else {
if (!SSL_is_init_finished(ssl)) {
//proxy_info("SSL_is_init_finished NOT completed\n");
if (do_ssl_handshake() == POSTGRESQL_SSLSTATUS_FAIL) {
if (do_ssl_handshake() == PGSQL_SSLSTATUS_FAIL) {
//proxy_info("SSL_is_init_finished failed!!\n");
shut_soft();
return;
@ -905,7 +905,7 @@ int PgSQL_Data_Stream::write_to_net_poll() {
if (encrypted) {
if (!SSL_is_init_finished(ssl)) {
//proxy_info("SSL_is_init_finished completed: NO!\n");
if (do_ssl_handshake() == POSTGRESQL_SSLSTATUS_FAIL) {
if (do_ssl_handshake() == PGSQL_SSLSTATUS_FAIL) {
//proxy_info("SSL_is_init_finished failed!!\n");
shut_soft();
return -1;

@ -0,0 +1,453 @@
#include "../deps/json/json.hpp"
using json = nlohmann::json;
#define PROXYJSON
#include "PgSQL_Error_Helper.h"
#include "proxysql.h"
#include "cpp.h"
#include "libpq-fe.h"
constexpr const char* PgSQL_Error_Helper::error_code_str[];
PGSQL_ERROR_CODES PgSQL_Error_Helper::identify_error_code(const char* errorCode) {
if (strlen(errorCode) != 5)
return PGSQL_ERROR_CODES::ERRCODE_UNKNOWN;
for (uint8_t i = 0; i < static_cast<uint8_t>(PGSQL_ERROR_CODES::PGSQL_ERROR_CODES_COUNT); i++) {
if (strncmp(errorCode, error_code_str[i], 5) == 0) {
return static_cast<PGSQL_ERROR_CODES>(i);
}
}
return PGSQL_ERROR_CODES::ERRCODE_UNKNOWN;
}
PGSQL_ERROR_CLASS PgSQL_Error_Helper::identify_error_class(const char* errorCode) {
if (strncmp(errorCode, "00", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_SUCCESS;
}
else if (strncmp(errorCode, "01", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_WARNING;
}
else if (strncmp(errorCode, "02", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_NO_DATA;
}
else if (strncmp(errorCode, "03", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_SQL_STATEMENT_NOT_YET_COMPLETE;
}
else if (strncmp(errorCode, "08", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_CONNECTION_EXCEPTION;
}
else if (strncmp(errorCode, "09", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_TRIGGERED_ACTION_EXCEPTION;
}
else if (strncmp(errorCode, "0A", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_FEATURE_NOT_SUPPORTED;
}
else if (strncmp(errorCode, "0B", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_INVALID_TRANSACTION_INITIATION;
}
else if (strncmp(errorCode, "0F", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_LOCATOR_EXCEPTION;
}
else if (strncmp(errorCode, "0L", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_INVALID_GRANTOR;
}
else if (strncmp(errorCode, "0P", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_INVALID_ROLE_SPECIFICATION;
}
else if (strncmp(errorCode, "0Z", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_DIAGNOSTICS_EXCEPTION;
}
else if (strncmp(errorCode, "20", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_CASE_NOT_FOUND;
}
else if (strncmp(errorCode, "21", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_CARDINALITY_VIOLATION;
}
else if (strncmp(errorCode, "22", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_DATA_EXCEPTION;
}
else if (strncmp(errorCode, "23", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_INTEGRITY_CONSTRAINT_VIOLATION;
}
else if (strncmp(errorCode, "24", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_INVALID_CURSOR_STATE;
}
else if (strncmp(errorCode, "25", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_INVALID_TRANSACTION_STATE;
}
else if (strncmp(errorCode, "26", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_INVALID_SQL_STATEMENT_NAME;
}
else if (strncmp(errorCode, "27", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_TRIGGERED_DATA_CHANGE_VIOLATION;
}
else if (strncmp(errorCode, "28", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_INVALID_AUTHORIZATION_SPECIFICATION;
}
else if (strncmp(errorCode, "2B", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST;
}
else if (strncmp(errorCode, "2D", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_INVALID_TRANSACTION_TERMINATION;
}
else if (strncmp(errorCode, "2F", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_SQL_ROUTINE_EXCEPTION;
}
else if (strncmp(errorCode, "34", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_INVALID_CURSOR_NAME;
}
else if (strncmp(errorCode, "38", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_EXTERNAL_ROUTINE_EXCEPTION;
}
else if (strncmp(errorCode, "39", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION;
}
else if (strncmp(errorCode, "3B", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_SAVEPOINT_EXCEPTION;
}
else if (strncmp(errorCode, "3D", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_INVALID_CATALOG_NAME;
}
else if (strncmp(errorCode, "3F", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_INVALID_SCHEMA_NAME;
}
else if (strncmp(errorCode, "40", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_TRANSACTION_ROLLBACK;
}
else if (strncmp(errorCode, "42", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION;
}
else if (strncmp(errorCode, "44", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_WITH_CHECK_OPTION_VIOLATION;
}
else if (strncmp(errorCode, "53", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_INSUFFICIENT_RESOURCES;
}
else if (strncmp(errorCode, "54", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_PROGRAM_LIMIT_EXCEEDED;
}
else if (strncmp(errorCode, "55", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_OBJECT_NOT_IN_PREREQUISITE_STATE;
}
else if (strncmp(errorCode, "57", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_OPERATOR_INTERVENTION;
}
else if (strncmp(errorCode, "58", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_SYSTEM_ERROR_UNSPECIFIED;
}
else if (strncmp(errorCode, "72", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_CRASH_SHUTDOWN;
}
else if (strncmp(errorCode, "F0", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_CONFIG_FILE_ERROR;
}
else if (strncmp(errorCode, "HV", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_FOREIGN_DATA_WRAPPER_ERROR;
}
else if (strncmp(errorCode, "P0", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_PLPGSQL_ERROR;
}
else if (strncmp(errorCode, "XX", 2) == 0) {
return PGSQL_ERROR_CLASS::ERRCLASS_INTERNAL_ERROR;
}
else {
return PGSQL_ERROR_CLASS::ERRCLASS_UNKNOWN_ERROR;
}
}
PGSQL_ERROR_CATEGORY PgSQL_Error_Helper::categorize_error_class(PGSQL_ERROR_CLASS err_class) {
switch (err_class) {
case PGSQL_ERROR_CLASS::ERRCLASS_SUCCESS:
case PGSQL_ERROR_CLASS::ERRCLASS_WARNING:
case PGSQL_ERROR_CLASS::ERRCLASS_NO_DATA:
case PGSQL_ERROR_CLASS::ERRCLASS_SQL_STATEMENT_NOT_YET_COMPLETE:
return PGSQL_ERROR_CATEGORY::ERRCATEGORY_STATUS;
case PGSQL_ERROR_CLASS::ERRCLASS_CONNECTION_EXCEPTION:
return PGSQL_ERROR_CATEGORY::ERRCATEGORY_CONNECTION_ERROR;
case PGSQL_ERROR_CLASS::ERRCLASS_INVALID_AUTHORIZATION_SPECIFICATION:
case PGSQL_ERROR_CLASS::ERRCLASS_INVALID_GRANTOR:
case PGSQL_ERROR_CLASS::ERRCLASS_INVALID_ROLE_SPECIFICATION:
return PGSQL_ERROR_CATEGORY::ERRCATEGORY_AUTHORIZATION_ERROR;
case PGSQL_ERROR_CLASS::ERRCLASS_INSUFFICIENT_RESOURCES:
return PGSQL_ERROR_CATEGORY::ERRCATEGORY_RESOURCE_ERROR;
case PGSQL_ERROR_CLASS::ERRCLASS_CONFIG_FILE_ERROR:
return PGSQL_ERROR_CATEGORY::ERRCATEGORY_CONFIGURATION_ERROR;
case PGSQL_ERROR_CLASS::ERRCLASS_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION:
return PGSQL_ERROR_CATEGORY::ERRCATEGORY_SYNTAX_ERROR;
case PGSQL_ERROR_CLASS::ERRCLASS_FEATURE_NOT_SUPPORTED:
return PGSQL_ERROR_CATEGORY::ERRCATEGORY_FEATURE_NOT_SUPPORTED;
case PGSQL_ERROR_CLASS::ERRCLASS_TRIGGERED_ACTION_EXCEPTION:
case PGSQL_ERROR_CLASS::ERRCLASS_INVALID_TRANSACTION_INITIATION:
case PGSQL_ERROR_CLASS::ERRCLASS_INVALID_TRANSACTION_STATE:
case PGSQL_ERROR_CLASS::ERRCLASS_INVALID_TRANSACTION_TERMINATION:
case PGSQL_ERROR_CLASS::ERRCLASS_TRANSACTION_ROLLBACK:
case PGSQL_ERROR_CLASS::ERRCLASS_SAVEPOINT_EXCEPTION:
return PGSQL_ERROR_CATEGORY::ERRCATEGORY_TRANSACTION_ERROR;
case PGSQL_ERROR_CLASS::ERRCLASS_CASE_NOT_FOUND:
case PGSQL_ERROR_CLASS::ERRCLASS_CARDINALITY_VIOLATION:
case PGSQL_ERROR_CLASS::ERRCLASS_DATA_EXCEPTION:
case PGSQL_ERROR_CLASS::ERRCLASS_INTEGRITY_CONSTRAINT_VIOLATION:
case PGSQL_ERROR_CLASS::ERRCLASS_WITH_CHECK_OPTION_VIOLATION:
return PGSQL_ERROR_CATEGORY::ERRCATEGORY_DATA_ERROR;
case PGSQL_ERROR_CLASS::ERRCLASS_SQL_ROUTINE_EXCEPTION:
case PGSQL_ERROR_CLASS::ERRCLASS_TRIGGERED_DATA_CHANGE_VIOLATION:
return PGSQL_ERROR_CATEGORY::ERRCATEGORY_ROUTINE_ERROR;
case PGSQL_ERROR_CLASS::ERRCLASS_INVALID_CURSOR_STATE:
case PGSQL_ERROR_CLASS::ERRCLASS_INVALID_CURSOR_NAME:
return PGSQL_ERROR_CATEGORY::ERRCATEGORY_CURSOR_ERROR;
case PGSQL_ERROR_CLASS::ERRCLASS_EXTERNAL_ROUTINE_EXCEPTION:
case PGSQL_ERROR_CLASS::ERRCLASS_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION:
return PGSQL_ERROR_CATEGORY::ERRCATEGORY_EXTERNAL_ROUTINE_ERROR;
case PGSQL_ERROR_CLASS::ERRCLASS_PROGRAM_LIMIT_EXCEEDED:
return PGSQL_ERROR_CATEGORY::ERRCATEGORY_RESOURCE_LIMIT_ERROR;
case PGSQL_ERROR_CLASS::ERRCLASS_OBJECT_NOT_IN_PREREQUISITE_STATE:
return PGSQL_ERROR_CATEGORY::ERRCATEGORY_OBJECT_STATE_ERROR;
case PGSQL_ERROR_CLASS::ERRCLASS_OPERATOR_INTERVENTION:
return PGSQL_ERROR_CATEGORY::ERRCATEGORY_OPERATOR_INTERVENTION_ERROR;
case PGSQL_ERROR_CLASS::ERRCLASS_FOREIGN_DATA_WRAPPER_ERROR:
return PGSQL_ERROR_CATEGORY::ERRCATEGORY_FDW_ERROR;
case PGSQL_ERROR_CLASS::ERRCLASS_PLPGSQL_ERROR:
return PGSQL_ERROR_CATEGORY::ERRCATEGORY_PLPGSQL_ERROR;
case PGSQL_ERROR_CLASS::ERRCLASS_INTERNAL_ERROR:
return PGSQL_ERROR_CATEGORY::ERRCATEGORY_INTERNAL_ERROR_CATEGORY;
default:
return PGSQL_ERROR_CATEGORY::ERRCATEGORY_UNKNOWN_CATEGORY;
}
}
PGSQL_ERROR_SEVERITY PgSQL_Error_Helper::identify_error_severity(const char* severity) {
PGSQL_ERROR_SEVERITY ret = PGSQL_ERROR_SEVERITY::ERRSEVERITY_UNKNOWN_SEVERITY;
if (strcasecmp(severity, "PANIC") == 0) {
ret = PGSQL_ERROR_SEVERITY::ERRSEVERITY_PANIC;
} else if (strcasecmp(severity, "FATAL") == 0) {
ret = PGSQL_ERROR_SEVERITY::ERRSEVERITY_FATAL;
} else if (strcasecmp(severity, "ERROR") == 0) {
ret = PGSQL_ERROR_SEVERITY::ERRSEVERITY_ERROR;
} else if (strcasecmp(severity, "WARNING") == 0) {
ret = PGSQL_ERROR_SEVERITY::ERRSEVERITY_WARNING;
} else if (strcasecmp(severity, "NOTICE") == 0) {
ret = PGSQL_ERROR_SEVERITY::ERRSEVERITY_NOTICE;
} else if (strcasecmp(severity, "DEBUG") == 0) {
ret = PGSQL_ERROR_SEVERITY::ERRSEVERITY_DEBUG;
} else if (strcasecmp(severity, "LOG") == 0) {
ret = PGSQL_ERROR_SEVERITY::ERRSEVERITY_LOG;
} else if (strcasecmp(severity, "INFO") == 0) {
ret = PGSQL_ERROR_SEVERITY::ERRSEVERITY_INFO;
} else {
ret = PGSQL_ERROR_SEVERITY::ERRSEVERITY_UNKNOWN_SEVERITY;
}
return ret;
}
void PgSQL_ErrorInfo_Ext::reset() {
text = PGSQL_ERROR_SEVERITY::ERRSEVERITY_UNKNOWN_SEVERITY;
detail.clear();
hint.clear();
position.clear();
internal_position.clear();
internal_query.clear();
context.clear();
schema_name.clear();
table_name.clear();
column_name.clear();
datatype_name.clear();
constraint_name.clear();
source_file.clear();
source_line.clear();
source_function.clear();
}
void PgSQL_Error_Helper::fill_error_info(PgSQL_ErrorInfo& err_info, const char* code, const char* msg, const char* severity) {
strncpy(err_info.sqlstate, code, 5);
err_info.sqlstate[5] = '\0';
err_info.severity = PgSQL_Error_Helper::identify_error_severity(severity);
err_info.code = PgSQL_Error_Helper::identify_error_code(code);
err_info.type = PgSQL_Error_Helper::identify_error_class(code);
err_info.category = PgSQL_Error_Helper::categorize_error_class(err_info.type);
err_info.message = msg;
}
void PgSQL_Error_Helper::fill_error_info(PgSQL_ErrorInfo& err_info, PGSQL_ERROR_CODES code, const char* msg, PGSQL_ERROR_SEVERITY severity) {
fill_error_info(err_info, get_error_code(code), msg, PgSQL_Error_Helper::get_severity(severity));
}
/*
void PgSQL_Error_Helper::fill_error_info_from_error_message(PgSQL_ErrorInfo& err_info, const char* error_msg) {
std::string errorMsgStr(error_msg);
std::string sqlState;
std::string primaryErrorMsg;
std::string severity;
// Initialize positions
size_t startPos = 0;
size_t endPos = 0;
// Extract severity (assume it's the first word in the primary error message)
size_t severityEndPos = errorMsgStr.find(": ");
if (severityEndPos != std::string::npos) {
severity = errorMsgStr.substr(0, severityEndPos);
startPos = severityEndPos + 2; // Skip the ": "
} else {
severity = get_severity(PGSQL_ERROR_SEVERITY::ERRSEVERITY_UNKNOWN_SEVERITY);
}
// Extract SQL state in the format [XXXXX]
startPos = errorMsgStr.find('[', startPos);
endPos = (startPos != std::string::npos) ? errorMsgStr.find(']', startPos) : std::string::npos;
if (startPos != std::string::npos && endPos != std::string::npos && endPos == startPos + 6) {
sqlState = errorMsgStr.substr(startPos + 1, 5); // Extract the SQL state
primaryErrorMsg = errorMsgStr.substr(severityEndPos + 2, startPos - (severityEndPos + 2)); // Extract the primary error message up to the SQL state
} else {
sqlState = get_error_code(PGSQL_ERROR_CODES::ERRCODE_RAISE_EXCEPTION); // Default SQL state
primaryErrorMsg = errorMsgStr.substr(severityEndPos + 2); // No SQL state found, remainder is the error message
}
fill_error_info(err_info, sqlState.c_str(), primaryErrorMsg.c_str(), severity.c_str());
}
*/
void reset_error_info(PgSQL_ErrorInfo& err_info, bool release_extented) {
err_info.sqlstate[0] = '\0';
err_info.code = PGSQL_ERROR_CODES::ERRCODE_SUCCESSFUL_COMPLETION;
err_info.severity = PGSQL_ERROR_SEVERITY::ERRSEVERITY_UNKNOWN_SEVERITY;
err_info.type = PGSQL_ERROR_CLASS::ERRCLASS_UNKNOWN_ERROR;
err_info.category = PGSQL_ERROR_CATEGORY::ERRCATEGORY_UNKNOWN_CATEGORY;
err_info.message.clear();
if (err_info.ext_info) {
if (release_extented) {
delete err_info.ext_info;
err_info.ext_info = NULL;
} else {
err_info.ext_info->reset();
}
}
}
void PgSQL_Error_Helper::fill_extended_error_info(PgSQL_ErrorInfo& err_info, const PGresult* result, uint16_t ext_fields) {
if (ext_fields == 0) {
if (err_info.ext_info != NULL) {
delete err_info.ext_info;
err_info.ext_info = NULL;
}
return;
}
char* val = NULL;
if (err_info.ext_info == NULL) {
err_info.ext_info = new PgSQL_ErrorInfo_Ext();
} else {
err_info.ext_info->reset();
}
if (ext_fields & PGSQL_ERROR_FIELD_TEXT) {
val = PQresultErrorField(result, PG_DIAG_SEVERITY_NONLOCALIZED);
err_info.ext_info->text = identify_error_severity(val ? val : "");
}
if (ext_fields & PGSQL_ERROR_FIELD_DETAIL) {
val = PQresultErrorField(result, PG_DIAG_MESSAGE_DETAIL);
err_info.ext_info->detail = val ? val : "";
}
if (ext_fields & PGSQL_ERROR_FIELD_HINT) {
val = PQresultErrorField(result, PG_DIAG_MESSAGE_HINT);
err_info.ext_info->hint = val ? val : "";
}
if (ext_fields & PGSQL_ERROR_FIELD_POSITION) {
val = PQresultErrorField(result, PG_DIAG_STATEMENT_POSITION);
err_info.ext_info->position = val ? val : "";
}
if (ext_fields & PGSQL_ERROR_FIELD_INTERNAL_POSITION) {
val = PQresultErrorField(result, PG_DIAG_INTERNAL_POSITION);
err_info.ext_info->internal_position = val ? val : "";
}
if (ext_fields & PGSQL_ERROR_FIELD_INTERNAL_QUERY) {
val = PQresultErrorField(result, PG_DIAG_INTERNAL_QUERY);
err_info.ext_info->internal_query = val ? val : "";
}
if (ext_fields & PGSQL_ERROR_FIELD_CONTEXT) {
val = PQresultErrorField(result, PG_DIAG_CONTEXT);
err_info.ext_info->context = val ? val : "";
}
if (ext_fields & PGSQL_ERROR_FIELD_SCHEMA_NAME) {
val = PQresultErrorField(result, PG_DIAG_SCHEMA_NAME);
err_info.ext_info->schema_name = val ? val : "";
}
if (ext_fields & PGSQL_ERROR_FIELD_TABLE_NAME) {
val = PQresultErrorField(result, PG_DIAG_TABLE_NAME);
err_info.ext_info->table_name = val ? val : "";
}
if (ext_fields & PGSQL_ERROR_FIELD_COLUMN_NAME) {
val = PQresultErrorField(result, PG_DIAG_COLUMN_NAME);
err_info.ext_info->column_name = val ? val : "";
}
if (ext_fields & PGSQL_ERROR_FIELD_DATA_TYPE_NAME) {
val = PQresultErrorField(result, PG_DIAG_DATATYPE_NAME);
err_info.ext_info->datatype_name = val ? val : "";
}
if (ext_fields & PGSQL_ERROR_FIELD_CONSTRAINT_NAME) {
val = PQresultErrorField(result, PG_DIAG_CONSTRAINT_NAME);
err_info.ext_info->constraint_name = val ? val : "";
}
if (ext_fields & PGSQL_ERROR_FIELD_FILE) {
val = PQresultErrorField(result, PG_DIAG_SOURCE_FILE);
err_info.ext_info->source_file = val ? val : "";
}
if (ext_fields & PGSQL_ERROR_FIELD_LINE) {
val = PQresultErrorField(result, PG_DIAG_SOURCE_LINE);
err_info.ext_info->source_line = val ? val : "";
}
if (ext_fields & PGSQL_ERROR_FIELD_ROUTINE) {
val = PQresultErrorField(result, PG_DIAG_SOURCE_FUNCTION);
err_info.ext_info->source_function = val ? val : "";
}
}
void PgSQL_Error_Helper::fill_error_info(PgSQL_ErrorInfo& err_info, const PGresult* result, uint16_t ext_fields) {
if (result == nullptr) {
return;
}
const char* sqlstate = PQresultErrorField(result, PG_DIAG_SQLSTATE);
const char* message = PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY);
const char* severity = PQresultErrorField(result, PG_DIAG_SEVERITY);
fill_error_info(err_info, sqlstate ? sqlstate : "00000", message ? message : "", severity ? severity : "");
fill_extended_error_info(err_info, result, ext_fields);
}

@ -3358,10 +3358,9 @@ void PgSQL_HostGroups_Manager::destroy_MyConn_from_pool(PgSQL_Connection *c, boo
if (mysrvc->status==MYSQL_SERVER_STATUS_ONLINE && c->send_quit && queue.size() < __sync_fetch_and_add(&GloMTH->variables.connpoll_reset_queue_length,0)) {
if (c->async_state_machine==ASYNC_IDLE) {
// overall, the backend seems healthy and so it is the connection. Try to reset it
int myerr=mysql_errno(c->pgsql);
if (myerr >= 2000 && myerr < 3000) {
if (c->is_connection_in_reusable_state() == false) {
// client library error . We must not try to save the connection
proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Not trying to reset PgSQL_Connection %p, server %s:%d . Error code %d\n", c, mysrvc->address, mysrvc->port, myerr);
proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Not trying to reset PgSQL_Connection %p, server %s:%d . Error %s\n", c, mysrvc->address, mysrvc->port, c->get_error_code_with_message().c_str());
} else {
proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Trying to reset PgSQL_Connection %p, server %s:%d\n", c, mysrvc->address, mysrvc->port);
to_del=false;

@ -684,7 +684,7 @@ bool PgSQL_Protocol::process_startup_packet(unsigned char* pkt, unsigned int len
if (!user || *user == '\0') {
proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p. no username supplied.\n", (*myds), (*myds)->sess);
generate_error_packet(true, false, "no username supplied", NULL, true);
generate_error_packet(true, false, "no username supplied", PGSQL_ERROR_CODES::ERRCODE_PROTOCOL_VIOLATION, true);
return false;
}
@ -731,7 +731,7 @@ EXECUTION_STATE PgSQL_Protocol::process_handshake_response_packet(unsigned char*
if (!user || *user == '\0') {
proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s'. Client password pkt before startup packet.\n", (*myds), (*myds)->sess, user);
generate_error_packet(true, false, "client password pkt before startup packet", NULL, true);
generate_error_packet(true, false, "client password pkt before startup packet", PGSQL_ERROR_CODES::ERRCODE_PROTOCOL_VIOLATION, true);
goto __exit_process_pkt_handshake_response;
}
@ -802,7 +802,7 @@ EXECUTION_STATE PgSQL_Protocol::process_handshake_response_packet(unsigned char*
if (!pass || *pass == '\0') {
proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s'. Empty password returned by client.\n", (*myds), (*myds)->sess, user);
generate_error_packet(true, false, "empty password returned by client", NULL, true);
generate_error_packet(true, false, "empty password returned by client", PGSQL_ERROR_CODES::ERRCODE_PROTOCOL_VIOLATION, true);
break;
}
@ -841,7 +841,8 @@ EXECUTION_STATE PgSQL_Protocol::process_handshake_response_packet(unsigned char*
proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s'. Selected SASL mechanism: %s.\n", (*myds), (*myds)->sess, user, mech);
if (strcmp(mech, "SCRAM-SHA-256") != 0) {
proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s'. Client selected an invalid SASL authentication mechanism: %s.\n", (*myds), (*myds)->sess, user, mech);
generate_error_packet(true, false, "client selected an invalid SASL authentication mechanism", NULL, true);
generate_error_packet(true, false, "client selected an invalid SASL authentication mechanism",
PGSQL_ERROR_CODES::ERRCODE_PROTOCOL_VIOLATION, true);
break;
}
@ -861,7 +862,7 @@ EXECUTION_STATE PgSQL_Protocol::process_handshake_response_packet(unsigned char*
if (!scram_handle_client_first((*myds)->scram_state, &stored_user_info, ((const unsigned char*)hdr.data.ptr) + read_pos, length)) {
proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s'. SASL authentication failed\n", (*myds), (*myds)->sess, user);
generate_error_packet(true,false, "SASL authentication failed", NULL, true);
generate_error_packet(true, false, "SASL authentication failed", PGSQL_ERROR_CODES::ERRCODE_PROTOCOL_VIOLATION, true);
break;
}
@ -908,7 +909,7 @@ EXECUTION_STATE PgSQL_Protocol::process_handshake_response_packet(unsigned char*
}
} else {
proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s'. User not found in the database.\n", (*myds), (*myds)->sess, user);
generate_error_packet(true,false, "User not found", NULL, true);
generate_error_packet(true, false, "User not found", PGSQL_ERROR_CODES::ERRCODE_PROTOCOL_VIOLATION, true);
}
// set the default session charset
//(*myds)->sess->default_charset = charset;
@ -987,20 +988,26 @@ void PgSQL_Protocol::welcome_client() {
pgpkt.set_multi_pkt_mode(true);
pgpkt.write_AuthenticationOk();
pgpkt.write_ParameterStatus("server_encoding", "UTF-8");
pgpkt.write_ParameterStatus("is_superuser", "on"); // only for admin
const char* client_encoding = (*myds)->myconn->conn_params.get_value(PG_CLIENT_ENCODING);
if (client_encoding)
pgpkt.write_ParameterStatus("client_encoding", client_encoding);
const char* application_name = (*myds)->myconn->conn_params.get_value(PG_APPLICATION_NAME);
if (application_name)
pgpkt.write_ParameterStatus("application_name", application_name);
const char* client_encoding = (*myds)->myconn->conn_params.get_value(PG_CLIENT_ENCODING);
if (client_encoding)
pgpkt.write_ParameterStatus("client_encoding", client_encoding);
// if client does not provide client_encoding, PostgreSQL uses the default client encoding.
// We need to save the default client encoding to send it to the client in case client doesn't provide one.
else if (pgsql_thread___default_client_encoding)
pgpkt.write_ParameterStatus("client_encoding", pgsql_thread___default_client_encoding);
if (pgsql_thread___server_version)
pgpkt.write_ParameterStatus("server_version", pgsql_thread___server_version);
pgpkt.write_ParameterStatus("server_encoding", "UTF8");
pgpkt.write_ReadyForQuery();
pgpkt.set_multi_pkt_mode(false);
@ -1010,10 +1017,15 @@ void PgSQL_Protocol::welcome_client() {
//(*myds)->sess->status = WAITING_CLIENT_DATA;
}
void PgSQL_Protocol::generate_error_packet(bool send, bool ready, const char* msg, const char* code, bool fatal, PtrSize_t* _ptr) {
void PgSQL_Protocol::generate_error_packet(bool send, bool ready, const char* msg, PGSQL_ERROR_CODES code, bool fatal, bool track, PtrSize_t* _ptr) {
// to avoid memory leak
assert(send == true || _ptr);
if (send) {
// in case of fatal error we dont generate ready packets
ready = !fatal;
}
PG_pkt pgpkt{};
if (ready)
@ -1022,7 +1034,7 @@ void PgSQL_Protocol::generate_error_packet(bool send, bool ready, const char* ms
pgpkt.write_generic('E', "cscscscsc",
'S', fatal ? "FATAL" : "ERROR",
'V', fatal ? "FATAL" : "ERROR",
'C', code ? code : "08P01", 'M', msg, 0);
'C', PgSQL_Error_Helper::get_error_code(code), 'M', msg, 0);
if (ready) {
pgpkt.write_ReadyForQuery();
@ -1058,6 +1070,12 @@ void PgSQL_Protocol::generate_error_packet(bool send, bool ready, const char* ms
_ptr->ptr = buff.first;
_ptr->size = buff.second;
}
if (track) {
if (*myds && (*myds)->sess && (*myds)->sess->thread) {
(*myds)->sess->thread->status_variables.stvar[st_var_generated_pkt_err]++;
}
}
}
bool PgSQL_Protocol::scram_handle_client_first(ScramState* scram_state, PgCredentials* user, const unsigned char* data, uint32_t datalen)
@ -1310,7 +1328,7 @@ bool PgSQL_Protocol::generate_ok_packet(bool send, bool ready, const char* msg,
//}
unsigned int PgSQL_Protocol::copy_row_description_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, PGresult* result) {
unsigned int PgSQL_Protocol::copy_row_description_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, const PGresult* result) {
assert(pg_query_result);
assert(result);
@ -1367,7 +1385,7 @@ unsigned int PgSQL_Protocol::copy_row_description_to_PgSQL_Query_Result(bool sen
return size;
}
unsigned int PgSQL_Protocol::copy_row_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, PGresult* result) {
unsigned int PgSQL_Protocol::copy_row_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, const PGresult* result) {
assert(pg_query_result);
assert(result);
assert(pg_query_result->num_fields);
@ -1428,18 +1446,16 @@ unsigned int PgSQL_Protocol::copy_row_to_PgSQL_Query_Result(bool send, PgSQL_Que
return total_size;
}
unsigned int PgSQL_Protocol::copy_command_completion_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, PGresult* result) {
unsigned int PgSQL_Protocol::copy_command_completion_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, const PGresult* result) {
assert(pg_query_result);
assert(result);
const char* tag = PQcmdStatus(result);
const char* tag = PQcmdStatus((PGresult*)result);
if (!tag) assert(0); // for testing it should not be null
const unsigned int tag_len = strlen(tag) + 1;
unsigned int size = 1 + 4 + tag_len + 1 + 4 + 1; // 'C', length, tag, Z, length, I
const unsigned int size = strlen(tag) + 1 + 1 + 4; // tag length, null byte, 'C', length, tag
bool alloced_new_buffer = false;
unsigned char* _ptr = pg_query_result->buffer_reserve_space(size);
// buffer is not enough to store the new row. Remember we have already pushed data to PSarrayOUT
@ -1451,11 +1467,8 @@ unsigned int PgSQL_Protocol::copy_command_completion_to_PgSQL_Query_Result(bool
PG_pkt pgpkt(_ptr, size);
pgpkt.put_char('C');
pgpkt.put_uint32(tag_len + 4);
pgpkt.put_uint32(size - 1);
pgpkt.put_string(tag);
pgpkt.put_char('Z');
pgpkt.put_uint32(4 + 1);
pgpkt.put_char('I');
if (send == true) {
// not supported
@ -1469,11 +1482,11 @@ unsigned int PgSQL_Protocol::copy_command_completion_to_PgSQL_Query_Result(bool
//pg_query_result->buffer_to_PSarrayOut();
pg_query_result->PSarrayOUT.add(_ptr, size);
}
pg_query_result->pkt_count+=2;
pg_query_result->pkt_count++;
return size;
}
unsigned int PgSQL_Protocol::copy_error_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, PGresult* result) {
unsigned int PgSQL_Protocol::copy_error_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, const PGresult* result) {
assert(pg_query_result);
assert(result);
@ -1496,7 +1509,7 @@ unsigned int PgSQL_Protocol::copy_error_to_PgSQL_Query_Result(bool send, PgSQL_Q
const char* source_line = PQresultErrorField(result, PG_DIAG_SOURCE_LINE);
const char* source_function = PQresultErrorField(result, PG_DIAG_SOURCE_FUNCTION);
unsigned int size = 1 + 4 + 1 + 1 + 4 + 1; // 'E', length, null byte , Z, length, I
unsigned int size = 1 + 4 + 1; // 'E', length, null byte
if (severity) size += strlen(severity) + 1 + 1;
if (text) size += strlen(text) + 1 + 1;
@ -1529,7 +1542,7 @@ unsigned int PgSQL_Protocol::copy_error_to_PgSQL_Query_Result(bool send, PgSQL_Q
PG_pkt pgpkt(_ptr, size);
pgpkt.put_char('E');
pgpkt.put_uint32(size - (6 + 1)); // subtracting ready packet size
pgpkt.put_uint32(size - 1);
if (severity) {
pgpkt.put_char('S');
pgpkt.put_string(severity);
@ -1603,9 +1616,6 @@ unsigned int PgSQL_Protocol::copy_error_to_PgSQL_Query_Result(bool send, PgSQL_Q
pgpkt.put_string(source_function);
}
pgpkt.put_char('\0');
pgpkt.put_char('Z');
pgpkt.put_uint32(4 + 1);
pgpkt.put_char('I');
if (send == true) {
// not supported
@ -1619,18 +1629,97 @@ unsigned int PgSQL_Protocol::copy_error_to_PgSQL_Query_Result(bool send, PgSQL_Q
//pg_query_result->buffer_to_PSarrayOut();
pg_query_result->PSarrayOUT.add(_ptr, size);
}
pg_query_result->pkt_count += 2;
pg_query_result->pkt_count++;
return size;
}
unsigned int PgSQL_Protocol::copy_empty_query_response_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, const PGresult* result) {
assert(pg_query_result);
// we are currently not using result. It is just for future use
const unsigned int size = 1 + 4; // I, length
bool alloced_new_buffer = false;
unsigned char* _ptr = pg_query_result->buffer_reserve_space(size);
// buffer is not enough to store the new row. Remember we have already pushed data to PSarrayOUT
if (_ptr == NULL) {
_ptr = (unsigned char*)l_alloc(size);
alloced_new_buffer = true;
}
PG_pkt pgpkt(_ptr, size);
pgpkt.put_char('I');
pgpkt.put_uint32(size - 1);
if (send == true) {
// not supported
//(*myds)->PSarrayOUT->add((void*)_ptr, size);
}
pg_query_result->resultset_size += size;
if (alloced_new_buffer) {
// we created new buffer
//pg_query_result->buffer_to_PSarrayOut();
pg_query_result->PSarrayOUT.add(_ptr, size);
}
pg_query_result->pkt_count++;
return size;
}
unsigned int PgSQL_Protocol::copy_ready_status_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, PGTransactionStatusType txn_status) {
assert(pg_query_result);
char txn_state = 'I';
if (txn_status == PQTRANS_INTRANS)
txn_state = 'T';
else if (txn_status == PQTRANS_INERROR)
txn_state = 'E';
const unsigned int size = 1 + 4 + 1; // Z, length, I/T/E
bool alloced_new_buffer = false;
unsigned char* _ptr = pg_query_result->buffer_reserve_space(size);
// buffer is not enough to store the new row. Remember we have already pushed data to PSarrayOUT
if (_ptr == NULL) {
_ptr = (unsigned char*)l_alloc(size);
alloced_new_buffer = true;
}
PG_pkt pgpkt(_ptr, size);
pgpkt.put_char('Z');
pgpkt.put_uint32(size - 1);
pgpkt.put_char(txn_state);
if (send == true) {
// not supported
//(*myds)->PSarrayOUT->add((void*)_ptr, size);
}
pg_query_result->resultset_size += size;
if (alloced_new_buffer) {
// we created new buffer
//pg_query_result->buffer_to_PSarrayOut();
pg_query_result->PSarrayOUT.add(_ptr, size);
}
pg_query_result->pkt_count++;
return size;
}
PgSQL_Query_Result::PgSQL_Query_Result() {
buffer = NULL;
transfer_started = false;
resultset_completed = false;
buffer_used = 0;
resultset_size = 0;
num_fields = 0;
num_rows = 0;
pkt_count = 0;
result_packet_type = PGSQL_QUERY_RESULT_NO_DATA;
}
PgSQL_Query_Result::~PgSQL_Query_Result() {
@ -1653,16 +1742,12 @@ void PgSQL_Query_Result::buffer_init() {
buffer_used = 0;
}
void PgSQL_Query_Result::init(PgSQL_Protocol* _proto, PGconn* _conn) {
void PgSQL_Query_Result::init(PgSQL_Protocol* _proto, PgSQL_Data_Stream* _myds, PgSQL_Connection* _conn) {
PROXY_TRACE2();
transfer_started = false;
resultset_completed = false;
//resultset_size = 0;
//sid = 0;
num_rows = 0;
proto = _proto;
//result = NULL;
pgsql_conn = _conn;
conn = _conn;
myds = _myds;
buffer_init();
if (proto == NULL) {
@ -1670,57 +1755,77 @@ void PgSQL_Query_Result::init(PgSQL_Protocol* _proto, PGconn* _conn) {
}
}
unsigned int PgSQL_Query_Result::add_row_description(PGresult* result) {
return proto->copy_row_description_to_PgSQL_Query_Result(false, this, result);
unsigned int PgSQL_Query_Result::add_row_description(const PGresult* result) {
const unsigned int res = proto->copy_row_description_to_PgSQL_Query_Result(false, this, result);
result_packet_type |= PGSQL_QUERY_RESULT_TUPLE;
return res;
}
unsigned int PgSQL_Query_Result::add_row(PGresult* result) {
unsigned int PgSQL_Query_Result::add_row(const PGresult* result) {
//result_type |= PGSQL_QUERY_RESULT_TUPLE;
return proto->copy_row_to_PgSQL_Query_Result(false,this, result);
}
unsigned int PgSQL_Query_Result::add_error(PgSQL_Data_Stream* _myds, PGresult* result) {
unsigned int PgSQL_Query_Result::add_error(const PGresult* result) {
unsigned int size = 0;
if (_myds && _myds->killed_at) { // see case #750
PtrSize_t pkt;
if (_myds->kill_type == 0) {
proto->generate_error_packet(false, true, (char*)"Query execution was interrupted, query_timeout exceeded", "57014", false, &pkt);
PgHGM->p_update_pgsql_error_counter(p_pgsql_error_type::proxysql, _myds->myconn->parent->myhgc->hid, _myds->myconn->parent->address, _myds->myconn->parent->port, 1907);
}
else {
proto->generate_error_packet(false, true, (char*)"Query execution was interrupted", "57014", false, &pkt);
PgHGM->p_update_pgsql_error_counter(p_pgsql_error_type::proxysql, _myds->myconn->parent->myhgc->hid, _myds->myconn->parent->address, _myds->myconn->parent->port, 1317);
if (result) {
size = proto->copy_error_to_PgSQL_Query_Result(false, this, result);
PgHGM->p_update_pgsql_error_counter(p_pgsql_error_type::proxysql, conn->parent->myhgc->hid, conn->parent->address, conn->parent->port, 1907);
}
else {
PtrSize_t pkt;
if (myds && myds->killed_at) { // see case #750
if (myds->kill_type == 0) {
proto->generate_error_packet(false, false, (char*)"Query execution was interrupted, query_timeout exceeded",
PGSQL_ERROR_CODES::ERRCODE_QUERY_CANCELED, false, false, &pkt);
PgHGM->p_update_pgsql_error_counter(p_pgsql_error_type::proxysql, conn->parent->myhgc->hid, conn->parent->address, conn->parent->port, 1907);
} else {
proto->generate_error_packet(false, false, (char*)"Query execution was interrupted",
PGSQL_ERROR_CODES::ERRCODE_QUERY_CANCELED, false, false, &pkt);
PgHGM->p_update_pgsql_error_counter(p_pgsql_error_type::proxysql, conn->parent->myhgc->hid, conn->parent->address, conn->parent->port, 1317);
}
} else if (conn->is_error_present()) {
proto->generate_error_packet(false, false, conn->get_error_message().c_str(), conn->get_error_code(), false, false, &pkt);
PgHGM->p_update_pgsql_error_counter(p_pgsql_error_type::proxysql, conn->parent->myhgc->hid, conn->parent->address, conn->parent->port, 1907);
} else {
assert(0); // should never reach here
}
PSarrayOUT.add(pkt.ptr, pkt.size);
resultset_size += pkt.size;
size = pkt.size;
} else if (result) {
size = proto->copy_error_to_PgSQL_Query_Result(false, this, result);
// get error from connection
//proto->generate_error_packet(false, true, "Unknown error", sqlstate, false, &pkt);
// TODO: Check this is a mysql error
PgHGM->p_update_pgsql_error_counter(p_pgsql_error_type::proxysql, _myds->myconn->parent->myhgc->hid, _myds->myconn->parent->address, _myds->myconn->parent->port, 1907);
} else {
}
buffer_to_PSarrayOut();
resultset_completed = true;
result_packet_type |= PGSQL_QUERY_RESULT_ERROR;
return size;
}
unsigned int PgSQL_Query_Result::add_empty_query_response(const PGresult* result) {
const unsigned int bytes = proto->copy_empty_query_response_to_PgSQL_Query_Result(false, this, result);
result_packet_type |= PGSQL_QUERY_RESULT_EMPTY;
return bytes;
}
unsigned int PgSQL_Query_Result::add_ready_status(PGTransactionStatusType txn_status) {
const unsigned int bytes = proto->copy_ready_status_to_PgSQL_Query_Result(false, this, txn_status);
buffer_to_PSarrayOut();
result_packet_type |= PGSQL_QUERY_RESULT_READY;
return bytes;
}
bool PgSQL_Query_Result::get_resultset(PtrSizeArray* PSarrayFinal) {
assert(buffer_used == 0); // we still have data in the buffer
transfer_started = true;
// Ready packet confirms that the result is complete
bool result_complete = (result_packet_type & PGSQL_QUERY_RESULT_READY);
if (proto) {
PSarrayFinal->copy_add(&PSarrayOUT, 0, PSarrayOUT.len);
while (PSarrayOUT.len)
PSarrayOUT.remove_index(PSarrayOUT.len - 1, NULL);
}
reset();
return resultset_completed;
return result_complete;
}
void PgSQL_Query_Result::buffer_to_PSarrayOut() {
@ -1755,10 +1860,9 @@ unsigned long long PgSQL_Query_Result::current_size() {
return intsize;
}
unsigned int PgSQL_Query_Result::add_command_completion(PGresult* result) {
unsigned int PgSQL_Query_Result::add_command_completion(const PGresult* result) {
const unsigned int bytes = proto->copy_command_completion_to_PgSQL_Query_Result(false, this, result);
buffer_to_PSarrayOut();
resultset_completed = true;
result_packet_type |= PGSQL_QUERY_RESULT_COMMAND;
return bytes;
}
@ -1787,4 +1891,5 @@ void PgSQL_Query_Result::reset() {
num_fields = 0;
num_rows = 0;
pkt_count = 0;
result_packet_type = PGSQL_QUERY_RESULT_NO_DATA;
}

File diff suppressed because it is too large Load Diff

@ -272,7 +272,7 @@ void PgSQL_Listeners_Manager::del(unsigned int idx) {
delete ifi;
}
static char* mysql_thread_variables_names[] = {
static char* pgsql_thread_variables_names[] = {
(char*)"authentication_method",
(char*)"shun_on_failures",
(char*)"shun_recovery_time_sec",
@ -397,6 +397,7 @@ static char* mysql_thread_variables_names[] = {
(char*)"poll_timeout_on_failure",
(char*)"server_capabilities",
(char*)"server_version",
(char*)"default_client_encoding",
(char*)"keep_multiplexing_variables",
(char*)"kill_backend_connection_when_disconnect",
(char*)"client_session_track_gtid",
@ -906,7 +907,7 @@ PgSQL_Threads_Handler::PgSQL_Threads_Handler() {
variables.authentication_method = (int)AUTHENTICATION_METHOD::SASL_SCRAM_SHA_256; //SCRAM Authentication method
variables.server_version = strdup((char*)"16.1");
variables.default_client_encoding = strdup((char*)"UTF8");
variables.shun_on_failures = 5;
variables.shun_recovery_time_sec = 10;
variables.unshun_algorithm = 0;
@ -1285,6 +1286,7 @@ char* PgSQL_Threads_Handler::get_variable_string(char* name) {
if (!strcmp(name, "default_schema")) return strdup(variables.default_schema);
}
if (!strcmp(name, "server_version")) return strdup(variables.server_version);
if (!strcmp(name, "default_client_encoding")) return strdup(variables.default_client_encoding);
if (!strcmp(name, "eventslog_filename")) return strdup(variables.eventslog_filename);
if (!strcmp(name, "auditlog_filename")) return strdup(variables.auditlog_filename);
if (!strcmp(name, "interfaces")) return strdup(variables.interfaces);
@ -1417,6 +1419,7 @@ char* PgSQL_Threads_Handler::get_variable(char* name) { // this is the public fu
}
if (!strcasecmp(name, "firewall_whitelist_errormsg")) return strdup(variables.firewall_whitelist_errormsg);
if (!strcasecmp(name, "server_version")) return strdup(variables.server_version);
if (!strcasecmp(name, "default_client_encoding")) return strdup(variables.default_client_encoding);
if (!strcasecmp(name, "auditlog_filename")) return strdup(variables.auditlog_filename);
if (!strcasecmp(name, "eventslog_filename")) return strdup(variables.eventslog_filename);
if (!strcasecmp(name, "default_schema")) return strdup(variables.default_schema);
@ -1701,7 +1704,16 @@ bool PgSQL_Threads_Handler::set_variable(char* name, const char* value) { // thi
return false;
}
}
if (!strcasecmp(name, "default_client_encoding")) {
if (vallen) {
free(variables.default_client_encoding);
variables.default_client_encoding = strdup(value);
return true;
}
else {
return false;
}
}
if (!strcasecmp(name, "init_connect")) {
if (variables.init_connect) free(variables.init_connect);
variables.init_connect = NULL;
@ -1991,7 +2003,7 @@ bool PgSQL_Threads_Handler::set_variable(char* name, const char* value) { // thi
}
// return variables from both mysql_thread_variables_names AND mysql_tracked_variables
// return variables from both pgsql_thread_variables_names AND mysql_tracked_variables
char** PgSQL_Threads_Handler::get_variables_list() {
@ -2176,14 +2188,14 @@ char** PgSQL_Threads_Handler::get_variables_list() {
}
const size_t l = sizeof(mysql_thread_variables_names) / sizeof(char*);
const size_t l = sizeof(pgsql_thread_variables_names) / sizeof(char*);
unsigned int i;
size_t ltv = 0;
for (i = 0; i < SQL_NAME_LAST_LOW_WM; i++) {
if (mysql_tracked_variables[i].is_global_variable)
ltv++;
}
char** ret = (char**)malloc(sizeof(char*) * (l + ltv)); // not adding + 1 because mysql_thread_variables_names is already NULL terminated
char** ret = (char**)malloc(sizeof(char*) * (l + ltv)); // not adding + 1 because pgsql_thread_variables_names is already NULL terminated
size_t fv = 0;
for (i = 0; i < SQL_NAME_LAST_LOW_WM; i++) {
if (mysql_tracked_variables[i].is_global_variable) {
@ -2196,14 +2208,14 @@ char** PgSQL_Threads_Handler::get_variables_list() {
// this is an extra check.
assert(fv == ltv);
for (i = ltv; i < l + ltv - 1; i++) {
ret[i] = (strdup(mysql_thread_variables_names[i - ltv]));
ret[i] = (strdup(pgsql_thread_variables_names[i - ltv]));
}
ret[l + ltv - 1] = NULL; // last value
return ret;
}
// Returns true if the given name is the name of an existing mysql variable
// scan both mysql_thread_variables_names AND mysql_tracked_variables
// scan both pgsql_thread_variables_names AND mysql_tracked_variables
bool PgSQL_Threads_Handler::has_variable(const char* name) {
if (strlen(name) > 8) {
if (strncmp(name, "default_", 8) == 0) {
@ -2219,10 +2231,10 @@ bool PgSQL_Threads_Handler::has_variable(const char* name) {
}
}
}
size_t no_vars = sizeof(mysql_thread_variables_names) / sizeof(char*);
size_t no_vars = sizeof(pgsql_thread_variables_names) / sizeof(char*);
for (unsigned int i = 0; i < no_vars - 1; ++i) {
size_t var_len = strlen(mysql_thread_variables_names[i]);
if (strlen(name) == var_len && !strncmp(name, mysql_thread_variables_names[i], var_len)) {
size_t var_len = strlen(pgsql_thread_variables_names[i]);
if (strlen(name) == var_len && !strncmp(name, pgsql_thread_variables_names[i], var_len)) {
return true;
}
}
@ -2327,8 +2339,7 @@ void PgSQL_Threads_Handler::start_listeners() {
char* _tmp = NULL;
_tmp = GloPTH->get_variable((char*)"interfaces");
if (strlen(_tmp) == 0) {
//GloPTH->set_variable((char *)"interfaces", (char *)"0.0.0.0:6033;/tmp/proxysql.sock"); // set default
GloPTH->set_variable((char*)"interfaces", (char*)"0.0.0.0:6035"); // changed. See isseu #1104
GloPTH->set_variable((char*)"interfaces", (char*)"0.0.0.0:6133");
}
free(_tmp);
tokenizer_t tok;
@ -2569,6 +2580,7 @@ PgSQL_Threads_Handler::~PgSQL_Threads_Handler() {
if (variables.default_schema) free(variables.default_schema);
if (variables.interfaces) free(variables.interfaces);
if (variables.server_version) free(variables.server_version);
if (variables.default_client_encoding) free(variables.default_client_encoding);
if (variables.keep_multiplexing_variables) free(variables.keep_multiplexing_variables);
if (variables.firewall_whitelist_errormsg) free(variables.firewall_whitelist_errormsg);
if (variables.init_connect) free(variables.init_connect);
@ -2706,6 +2718,7 @@ PgSQL_Thread::~PgSQL_Thread() {
if (mysql_thread___default_session_track_gtids) { free(mysql_thread___default_session_track_gtids); mysql_thread___default_session_track_gtids = NULL; }
if (pgsql_thread___server_version) { free(pgsql_thread___server_version); pgsql_thread___server_version = NULL; }
if (pgsql_thread___default_client_encoding) { free(pgsql_thread___default_client_encoding); pgsql_thread___default_client_encoding = NULL; }
for (int i = 0; i < SQL_NAME_LAST_LOW_WM; i++) {
if (mysql_thread___default_variables[i]) {
@ -2724,7 +2737,6 @@ PgSQL_Thread::~PgSQL_Thread() {
if (pgsql_thread___ssl_p2s_crl) { free(pgsql_thread___ssl_p2s_crl); pgsql_thread___ssl_p2s_crl = NULL; }
if (pgsql_thread___ssl_p2s_crlpath) { free(pgsql_thread___ssl_p2s_crlpath); pgsql_thread___ssl_p2s_crlpath = NULL; }
if (match_regexes) {
Session_Regex* sr = NULL;
sr = match_regexes[0];
@ -3856,6 +3868,9 @@ void PgSQL_Thread::refresh_variables() {
if (pgsql_thread___server_version) free(pgsql_thread___server_version);
pgsql_thread___server_version = GloPTH->get_variable_string((char*)"server_version");
if (pgsql_thread___default_client_encoding) free(pgsql_thread___default_client_encoding);
pgsql_thread___default_client_encoding = GloPTH->get_variable_string((char*)"default_client_encoding");
pgsql_thread___have_ssl = (bool)GloPTH->get_variable_int((char*)"have_ssl");
if (mysql_thread___eventslog_filename) free(mysql_thread___eventslog_filename);
@ -3874,6 +3889,7 @@ void PgSQL_Thread::refresh_variables() {
pgsql_thread___default_schema = GloPTH->get_variable_string((char*)"default_schema");
if (pgsql_thread___keep_multiplexing_variables) free(pgsql_thread___keep_multiplexing_variables);
pgsql_thread___keep_multiplexing_variables = GloPTH->get_variable_string((char*)"keep_multiplexing_variables");
/*
mysql_thread___server_capabilities = GloPTH->get_variable_uint16((char*)"server_capabilities");
mysql_thread___handle_unknown_charset = GloPTH->get_variable_int((char*)"handle_unknown_charset");
@ -3942,6 +3958,7 @@ PgSQL_Thread::PgSQL_Thread() {
last_processing_idles = 0;
__thread_PgSQL_Thread_Variables_version = 0;
pgsql_thread___server_version = NULL;
pgsql_thread___default_client_encoding = NULL;
pgsql_thread___have_ssl = true;
pgsql_thread___default_schema = NULL;
pgsql_thread___init_connect = NULL;

@ -1704,7 +1704,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_
bool is_valid_global_variable(const char *var_name) {
if (strlen(var_name) > 6 && !strncmp(var_name, "mysql-", 6) && GloMTH->has_variable(var_name + 6)) {
return true;
} else if (strlen(var_name) > 6 && !strncmp(var_name, "pgsql-", 11) && GloPTH->has_variable(var_name + 6)) {
} else if (strlen(var_name) > 6 && !strncmp(var_name, "pgsql-", 6) && GloPTH->has_variable(var_name + 6)) {
return true;
} else if (strlen(var_name) > 6 && !strncmp(var_name, "admin-", 6) && SPA->has_variable(var_name + 6)) {
return true;
@ -4082,7 +4082,7 @@ void admin_session_handler(Client_Session<T> sess, void *_pa, PtrSize_t *pkt) {
} else if constexpr (std::is_same<T, PgSQL_Session*>::value) {
backup_shun_on_failures = pgsql_thread___shun_on_failures;
pgsql_thread___shun_on_failures = 1;
// Set the error twice to surpass 'mysql_thread___shun_on_failures' value.
// Set the error twice to surpass 'pgsql_thread___shun_on_failures' value.
mysrvc->connect_error(i_errcode, false);
mysrvc->connect_error(i_errcode, false);
pgsql_thread___shun_on_failures = backup_shun_on_failures;
@ -5954,7 +5954,7 @@ ProxySQL_Admin::ProxySQL_Admin() :
} else {
variables.mysql_ifaces=strdup("0.0.0.0:6032"); // changed. See isseu #1103
}
variables.pgsql_ifaces= strdup("0.0.0.0:6034");
variables.pgsql_ifaces= strdup("0.0.0.0:6132");
variables.telnet_admin_ifaces=NULL;
variables.telnet_stats_ifaces=NULL;
variables.refresh_interval=2000;
@ -10835,7 +10835,7 @@ void ProxySQL_Admin::send_error_msg_to_client(Client_Session<T>& sess, const cha
PgSQL_Data_Stream* myds = sess->client_myds;
char* new_msg = (char*)malloc(strlen(msg) + sizeof(prefix_msg));
sprintf(new_msg, "%s%s", prefix_msg, msg);
myds->myprot.generate_error_packet(true,true, new_msg, NULL, false);
myds->myprot.generate_error_packet(true, true, new_msg, PGSQL_ERROR_CODES::ERRCODE_RAISE_EXCEPTION, false);
free(new_msg);
myds->DSS = STATE_SLEEP;
}

@ -47,6 +47,27 @@ CommandLine::~CommandLine() {
if (mysql_password)
free(mysql_password);
if (pgsql_admin_host)
free(pgsql_admin_host);
if (pgsql_server_host)
free(pgsql_server_host);
if (pgsql_server_username)
free(pgsql_server_username);
if (pgsql_server_password)
free(pgsql_server_password);
if (pgsql_host)
free(pgsql_host);
if (pgsql_username)
free(pgsql_username);
if (pgsql_password)
free(pgsql_password);
if (pgsql_root_host)
free(pgsql_root_host);
if (pgsql_root_username)
free(pgsql_root_username);
if (pgsql_root_password)
free(pgsql_root_password);
if (workdir)
free(workdir);
}
@ -237,6 +258,82 @@ int CommandLine::getEnv() {
replace_str_field(&this->mysql_password, value);
}
{
// unprivileged test connection
value = getenv("TAP_PGSQL_HOST");
if (value)
replace_str_field(&this->pgsql_host, value);
value = getenv("TAP_PGSQL_PORT");
if (value) {
env_port = strtol(value, NULL, 10);
if (env_port > 0 && env_port < 65536)
pgsql_port = env_port;
}
value = getenv("TAP_PGSQL_USERNAME");
if (value)
replace_str_field(&this->pgsql_username, value);
value = getenv("TAP_PGSQL_PASSWORD");
if (value)
replace_str_field(&this->pgsql_password, value);
// privileged test connection
value = getenv("TAP_PGSQLROOT_HOST");
if (value)
replace_str_field(&this->pgsql_root_host, value);
value = getenv("TAP_PGSQLROOT_PORT");
if (value) {
env_port = strtol(value, NULL, 10);
if (env_port > 0 && env_port < 65536)
pgsql_root_port = env_port;
}
value = getenv("TAP_PGSQLROOT_USERNAME");
if (value)
replace_str_field(&this->pgsql_root_username, value);
value = getenv("TAP_PGSQLROOT_PASSWORD");
if (value)
replace_str_field(&this->pgsql_root_password, value);
// pgsql proxysql admin connection
value = getenv("TAP_PGSQLADMIN_HOST");
if (value)
replace_str_field(&this->pgsql_admin_host, value);
value = getenv("TAP_PGSQLADMIN_PORT");
if (value) {
env_port = strtol(value, NULL, 10);
if (env_port > 0 && env_port < 65536)
pgsql_admin_port = env_port;
}
// admin username and password are identical for both MySQL and PostgreSQL.
// pgsql server connection
value = getenv("TAP_PGSQLSERVER_HOST");
if (value)
replace_str_field(&this->pgsql_server_host, value);
value = getenv("TAP_PGSQLSERVER_PORT");
if (value) {
env_port = strtol(value, NULL, 10);
if (env_port > 0 && env_port < 65536)
pgsql_server_port = env_port;
}
value = getenv("TAP_PGSQLSERVER_USERNAME");
if (value)
replace_str_field(&this->pgsql_server_username, value);
value = getenv("TAP_PGSQLSERVER_PASSWORD");
if (value)
replace_str_field(&this->pgsql_server_password, value);
}
value = getenv("TAP_WORKDIR");
if (value)

@ -38,6 +38,28 @@ class CommandLine {
char* mysql_username = strdup("root");
char* mysql_password = strdup("root");
// proxysql postgresql admin connection
char* pgsql_admin_host = strdup("127.0.0.1");
int pgsql_admin_port = 6132;
// pgsql server connection
char* pgsql_server_host = strdup("127.0.0.1");
int pgsql_server_port = 5432;
char* pgsql_server_username = strdup("postgres");
char* pgsql_server_password = strdup("postgres");
// unpriviliged test connection
char* pgsql_host = strdup("127.0.0.1");
int pgsql_port = 6133;
char* pgsql_username = strdup("testuser");
char* pgsql_password = strdup("testuser");
// priviliged test connection
char* pgsql_root_host = strdup("127.0.0.1");
int pgsql_root_port = 6133;
char* pgsql_root_username = strdup("postgres");
char* pgsql_root_password = strdup("postgres");
char* workdir = strdup("./");
uint64_t client_flags = 0;

@ -138,8 +138,8 @@ MYLIBS_DYNAMIC_PART := -Wl,--export-dynamic -Wl,-Bdynamic -lgnutls -ltap -lcpp_d
MYLIBS_STATIC_PART := -Wl,-Bstatic -lconfig -lproxysql -ldaemon -lconfig++ -lre2 -lpcrecpp -lpcre -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lev -lprometheus-cpp-pull -lprometheus-cpp-core
MYLIBS_PG_PART := -Wl,-Bstatic -lpq -lpgcommon -lpgport
MYLIBS_LAST_PART := -Wl,-Bdynamic -lpthread -lm -lz -lrt -ldl $(EXTRALINK)
MYLIBS := $(MYLIBS_DYNAMIC_PART) $(MYLIBS_STATIC_PART) $(MYLIBS_LAST_PART)
MYLIBS_PG := $(MYLIBS_DYNAMIC_PART) $(MYLIBS_STATIC_PART) $(MYLIBS_PG_PART) $(MYLIBS_LAST_PART)
MYLIBS := $(MYLIBS_DYNAMIC_PART) $(MYLIBS_STATIC_PART) $(MYLIBS_PG_PART) $(MYLIBS_LAST_PART)
#MYLIBS_PG := $(MYLIBS_DYNAMIC_PART) $(MYLIBS_STATIC_PART) $(MYLIBS_PG_PART) $(MYLIBS_LAST_PART)
#MYLIBS := -Wl,--export-dynamic -Wl,-Bdynamic -lssl -lcrypto -lgnutls -ltap -lcpp_dotenv -Wl,-Bstatic -lconfig -lproxysql -ldaemon -lconfig++ -lre2 -lpcrecpp -lpcre -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lev -lprometheus-cpp-pull -lprometheus-cpp-core -luuid -Wl,-Bdynamic -lpthread -lm -lz -lrt -ldl $(EXTRALINK)
MYLIBSJEMALLOC := -Wl,-Bstatic -ljemalloc
@ -329,9 +329,6 @@ prepare_statement_err3024_async-t: prepare_statement_err3024-t.cpp $(TAP_LDIR)/l
test_wexecvp_syscall_failures-t: test_wexecvp_syscall_failures-t.cpp $(TAP_LDIR)/libtap.so
$(CXX) $< $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) -Wl,--wrap=pipe,--wrap=fcntl,--wrap=read,--wrap=poll $(STATIC_LIBS) -o $@
libpq-t: libpq-t.cpp $(TAP_LDIR)/libtap.so
$(CXX) $< $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS_PG) $(STATIC_LIBS) -o $@
### clean targets

@ -1,41 +0,0 @@
/*
* Usage:
* Prepare PostgreSQL server:
* sudo -u postgres createuser --pwprompt testuser
* sudo -u postgres createdb testuser --owner testuser
* prepend to pg_hba.conf: "host all testuser 127.0.0.1/32 scram-sha-256"
* systemctl restart postgresql
* Run the test:
* TAP_PORT=5432 ./libpq-t
*/
#include "command_line.h"
#include "tap.h"
#include <stdlib.h>
#include <libpq-fe.h>
int main() {
plan(1);
CommandLine cl;
if(cl.getEnv()) {
return exit_status();
}
PGconn *conn;
PGresult *res;
char *conninfo;
int r;
// assumes dbname=username
// optionally, for traffic analysis: sslmode=disable
r = asprintf(&conninfo, "host=%s port=%u user=%s password=%s", cl.host, cl.port, cl.username, cl.password);
// Establish connection
conn = PQconnectdb(conninfo);
// Check if connection succeeded
ok(PQstatus(conn) == CONNECTION_OK, "Connection to database should succeed");
PQfinish(conn);
return exit_status();
}

@ -0,0 +1,320 @@
/**
* @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; \
})
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");
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");
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");
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 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);
teardown_database(conn);
test_invalid_connection(with_ssl);
PQfinish(conn);
}
int main(int argc, char** argv) {
plan(88); // Total number of tests planned
if (cl.getEnv())
return exit_status();
execute_tests(false); // without SSL
execute_tests(true); // with SSL
return exit_status();
}
Loading…
Cancel
Save