Generic error handling, transaction detection and handling, bug fixes

* Implemented generic error handling to cover all the scenarios.
* Extraction of results from PGconn in case of connection errors. Introduced a patch to facilitate this.
* Added transaction handling.
* Ready packet now includes transaction status.
* Split command completion and ready packet to increase flexibility.
* Resolved multiple bugs to enhance stability.
v2.x_pg_PrepStmtBase_240714
Rahim Kanji 2 years ago
parent 6aa8a50d3d
commit 65fc633cf5

1
deps/Makefile vendored

@ -303,6 +303,7 @@ 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 && patch -p0 < ../get_result_from_pgconn.patch
cd postgresql/postgresql && ./configure --with-ssl=openssl --with-includes=$(shell pwd)/libssl/openssl/include/openssl --with-libraries=$(shell pwd)/libssl/openssl/lib --enable-static --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

@ -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,69 @@ public:
void compute_unknown_transaction_status();
void async_free_result();
void flush();
bool IsActiveTransaction();
bool IsKnownActiveTransaction();
bool IsServerOffline();
std::string get_error_code_from_result() const;
int get_server_version() {
return PQserverVersion(pgsql_conn);
}
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;
}
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;
return error_info.message;
}
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(PG_ERROR_TYPE _err_type, const std::string& _err_msg) {
err_type = _err_type;
err_msg = _err_msg;
void set_error_from_result(const PGresult* result, uint16_t ext_fields = 0) {
PgSQL_Error_Helper::fill_error_info(error_info, result, ext_fields);
}
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 +589,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,653 @@
#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 err_code) {
return error_code_str[static_cast<uint8_t>(err_code)];
}
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);
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");
};
#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"
@ -983,4 +983,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,33 @@ private:
};
class PgSQL_Protocol;
#define PGSQL_QUERY_RESULT_EMPTY 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_WARNING 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_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 +223,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 +248,16 @@ 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_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>
@ -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();
@ -441,4 +441,4 @@ private:
void* PgSQL_kill_query_thread(void* arg);
#endif /* __CLASS_POSTGRESQL_SESSION_H */
#endif /* __CLASS_PGSQL_SESSION_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

@ -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

@ -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,8 +3191,7 @@ handler_again:
break;
}
PGresult* result = get_last_result();
const PGresult* result = get_last_result();
if (result) {
switch (PQresultStatus(result)) {
case PGRES_COMMAND_OK:
@ -3235,21 +3202,25 @@ handler_again:
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);
{
set_error_from_result(result, PGSQL_ERROR_FIELD_ALL);
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 +3233,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 +3242,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_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 +3972,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_ALWAYS);
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 +3997,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 +4042,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 +4060,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 +4084,26 @@ 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);
set_error_from_result(result);
// this is not actually an error, a hint to the user
error_info.severity = PGSQL_ERROR_SEVERITY::ERRSEVERITY_INFO;
proxy_warning("Failed to consume input. %s\n", get_error_code_with_message().c_str());
}
if (PQisBusy(pgsql_conn)) {
@ -4136,9 +4125,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 +4178,30 @@ 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
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] == 'C') { // client error
// do not change it
if (is_connected() == false) {
unknown_transaction_status = true;
return;
}
if (errocode[0] == 'P') { // server error
switch (PQtransactionStatus(pgsql_conn)) {
case PQTRANS_INTRANS:
case PQTRANS_INERROR:
case PQTRANS_UNKNOWN:
case PQTRANS_ACTIVE:
unknown_transaction_status = true;
return;
break;
default:
unknown_transaction_status = false;
}
// all other cases, server error
unknown_transaction_status = true;
}
}
@ -4257,8 +4239,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 +4250,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 +4466,63 @@ int PgSQL_Connection::async_ping(short event) {
return 1;
}
bool PgSQL_Connection::IsKnownActiveTransaction() {
bool in_txn = false;
if (pgsql_conn) {
// Get the transaction status
if (PQtransactionStatus(pgsql_conn) == PQTRANS_INTRANS) {
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:
case PQTRANS_UNKNOWN:
in_txn = true;
break;
default:
//case PQTRANS_IDLE:
//case PQTRANS_ACTIVE:
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;
}

@ -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);
}
@ -621,7 +621,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;
@ -651,7 +651,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);
@ -664,7 +664,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;
}
@ -865,7 +865,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;
@ -903,7 +903,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,409 @@
#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;
}
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 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);
}

@ -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;
@ -1010,10 +1011,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 +1028,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 +1064,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 +1322,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 +1379,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 +1440,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 +1461,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 +1476,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 +1503,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 +1536,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 +1610,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 +1623,61 @@ 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_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_EMPTY;
}
PgSQL_Query_Result::~PgSQL_Query_Result() {
@ -1653,16 +1700,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 +1713,71 @@ 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_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 +1812,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 +1843,5 @@ void PgSQL_Query_Result::reset() {
num_fields = 0;
num_rows = 0;
pkt_count = 0;
result_packet_type = PGSQL_QUERY_RESULT_EMPTY;
}

@ -1327,7 +1327,7 @@ void PgSQL_Session::return_proxysql_internal(PtrSize_t* pkt) {
}
// default
client_myds->DSS = STATE_QUERY_SENT_NET;
client_myds->myprot.generate_pkt_ERR(true, NULL, NULL, 1, 1064, (char*)"42000", (char*)"Unknown PROXYSQL INTERNAL command", true);
client_myds->myprot.generate_error_packet(true, true, "Unknown PROXYSQL INTERNAL command", PGSQL_ERROR_CODES::ERRCODE_SYNTAX_ERROR, false, true);
if (mirror == false) {
RequestEnd(NULL);
}
@ -1686,7 +1686,8 @@ bool PgSQL_Session::handler_special_queries(PtrSize_t* pkt) {
if ((pkt->size >= 22 + 5) && (strncasecmp((char*)"LOAD DATA LOCAL INFILE", (char*)pkt->ptr + 5, 22) == 0)) {
if (mysql_thread___enable_load_data_local_infile == false) {
client_myds->DSS = STATE_QUERY_SENT_NET;
client_myds->myprot.generate_pkt_ERR(true, NULL, NULL, 1, 1047, (char*)"HY000", (char*)"Unsupported 'LOAD DATA LOCAL INFILE' command", true);
client_myds->myprot.generate_error_packet(true, true, "Unsupported 'LOAD DATA LOCAL INFILE' command",
PGSQL_ERROR_CODES::ERRCODE_FEATURE_NOT_SUPPORTED, false, true);
if (mirror == false) {
RequestEnd(NULL);
}
@ -3061,7 +3062,8 @@ bool PgSQL_Session::handler_again___status_CONNECTING_SERVER(int* _rc) {
if (thread) {
thread->status_variables.stvar[st_var_max_connect_timeout_err]++;
}
client_myds->myprot.generate_error_packet(true, true, errmsg.c_str(), "57P03", false); // not sure if this is the right error code
client_myds->myprot.generate_error_packet(true, true, errmsg.c_str(), PGSQL_ERROR_CODES::ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION,
false, true);
RequestEnd(mybe->server_myds);
string hg_status{};
@ -3196,16 +3198,16 @@ bool PgSQL_Session::handler_again___status_CONNECTING_SERVER(int* _rc) {
}
else {
__exit_handler_again___status_CONNECTING_SERVER_with_err:
int myerr = mysql_errno(myconn->pgsql);
if (myerr) {
char sqlstate[10];
sprintf(sqlstate, "%s", mysql_sqlstate(myconn->pgsql));
client_myds->myprot.generate_pkt_ERR(true, NULL, NULL, 1, mysql_errno(myconn->pgsql), sqlstate, mysql_error(myconn->pgsql), true);
bool is_error_present = myconn->is_error_present();
if (is_error_present) {
client_myds->myprot.generate_error_packet(true, true, myconn->error_info.message.c_str(),
myconn->error_info.code, false, true);
}
else {
char buf[256];
sprintf(buf, "Max connect failure while reaching hostgroup %d", current_hostgroup);
client_myds->myprot.generate_error_packet(true,true,buf, "57P03", false); // not sure if this is the right error code
client_myds->myprot.generate_error_packet(true, true, buf, PGSQL_ERROR_CODES::ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION,
false, true);
if (thread) {
thread->status_variables.stvar[st_var_max_connect_timeout_err]++;
}
@ -3221,7 +3223,7 @@ bool PgSQL_Session::handler_again___status_CONNECTING_SERVER(int* _rc) {
if (mirror) {
PROXY_TRACE();
}
myds->destroy_MySQL_Connection_From_Pool(myerr ? true : false);
myds->destroy_MySQL_Connection_From_Pool(is_error_present);
myds->max_connect_time = 0;
NEXT_IMMEDIATE_NEW(WAITING_CLIENT_DATA);
}
@ -3481,7 +3483,8 @@ void PgSQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C
char* err_msg = (char*)"Session trying to reach HG %d while locked on HG %d . Rejecting query: %s";
char* buf = (char*)malloc(strlen(err_msg) + strlen(nqn.c_str()) + strlen(end) + 64);
sprintf(buf, err_msg, current_hostgroup, locked_on_hostgroup, nqn.c_str(), end);
client_myds->myprot.generate_pkt_ERR(true, NULL, NULL, client_myds->pkt_sid + 1, 9005, (char*)"HY000", buf, true);
client_myds->myprot.generate_error_packet(true, true, buf, PGSQL_ERROR_CODES::ERRCODE_RAISE_EXCEPTION,
false, true);
thread->status_variables.stvar[st_var_hostgroup_locked_queries]++;
RequestEnd(NULL);
free(buf);
@ -3652,7 +3655,8 @@ void PgSQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C
char* err_msg = (char*)"Session trying to reach HG %d while locked on HG %d . Rejecting query: %s";
char* buf = (char*)malloc(strlen(err_msg) + strlen(nqn.c_str()) + strlen(end) + 64);
sprintf(buf, err_msg, current_hostgroup, locked_on_hostgroup, nqn.c_str(), end);
client_myds->myprot.generate_pkt_ERR(true, NULL, NULL, client_myds->pkt_sid + 1, 9005, (char*)"HY000", buf, true);
client_myds->myprot.generate_error_packet(true, true, buf, PGSQL_ERROR_CODES::ERRCODE_RAISE_EXCEPTION,
false, true);
thread->status_variables.stvar[st_var_hostgroup_locked_queries]++;
RequestEnd(NULL);
free(buf);
@ -4188,7 +4192,8 @@ __get_pkts_from_client:
char* err_msg = (char*)"Session trying to reach HG %d while locked on HG %d . Rejecting query: %s";
char* buf = (char*)malloc(strlen(err_msg) + strlen(nqn.c_str()) + strlen(end) + 64);
sprintf(buf, err_msg, current_hostgroup, locked_on_hostgroup, nqn.c_str(), end);
client_myds->myprot.generate_pkt_ERR(true, NULL, NULL, client_myds->pkt_sid + 1, 9005, (char*)"HY000", buf, true);
client_myds->myprot.generate_error_packet(true, true, buf, PGSQL_ERROR_CODES::ERRCODE_RAISE_EXCEPTION,
false, true);
thread->status_variables.stvar[st_var_hostgroup_locked_queries]++;
RequestEnd(NULL);
free(buf);
@ -4405,7 +4410,8 @@ __get_pkts_from_client:
char* err_msg = (char*)"Session trying to reach HG %d while locked on HG %d . Rejecting query: %s";
char* buf = (char*)malloc(strlen(err_msg) + strlen(nqn.c_str()) + strlen(end) + 64);
sprintf(buf, err_msg, current_hostgroup, locked_on_hostgroup, nqn.c_str(), end);
client_myds->myprot.generate_pkt_ERR(true, NULL, NULL, client_myds->pkt_sid + 1, 9005, (char*)"HY000", buf, true);
client_myds->myprot.generate_error_packet(true, true, buf, PGSQL_ERROR_CODES::ERRCODE_RAISE_EXCEPTION,
false, true);
thread->status_variables.stvar[st_var_hostgroup_locked_queries]++;
RequestEnd(NULL);
free(buf);
@ -4909,10 +4915,10 @@ bool PgSQL_Session::handler_minus1_ClientLibraryError(PgSQL_Data_Stream* myds, i
// this function was inline
void PgSQL_Session::handler_minus1_LogErrorDuringQuery(PgSQL_Connection* myconn, int myerr, char* errmsg) {
if (pgsql_thread___verbose_query_error) {
proxy_warning("Error during query on (%d,%s,%d,%lu) , user \"%s@%s\" , schema \"%s\" , %d, %s . digest_text = \"%s\"\n", myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, myconn->get_mysql_thread_id(), client_myds->myconn->userinfo->username, (client_myds->addr.addr ? client_myds->addr.addr : (char*)"unknown"), client_myds->myconn->userinfo->schemaname, myerr, (errmsg ? errmsg : mysql_error(myconn->pgsql)), CurrentQuery.QueryParserArgs.digest_text);
proxy_warning("Error during query on (%d,%s,%d,%lu) , user \"%s@%s\" , schema \"%s\" , %s . digest_text = \"%s\"\n", myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, myconn->get_mysql_thread_id(), client_myds->myconn->userinfo->username, (client_myds->addr.addr ? client_myds->addr.addr : (char*)"unknown"), client_myds->myconn->userinfo->schemaname, myconn->get_error_code_with_message().c_str(), CurrentQuery.QueryParserArgs.digest_text);
}
else {
proxy_warning("Error during query on (%d,%s,%d,%lu): %d, %s\n", myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, myconn->get_mysql_thread_id(), myerr, (errmsg ? errmsg : mysql_error(myconn->pgsql)));
proxy_warning("Error during query on (%d,%s,%d,%lu): %s\n", myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, myconn->get_mysql_thread_id(), myconn->get_error_code_with_message().c_str());
}
PgHGM->add_pgsql_errors(myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, client_myds->myconn->userinfo->username, (client_myds->addr.addr ? client_myds->addr.addr : (char*)"unknown"), client_myds->myconn->userinfo->schemaname, myerr, (char*)(errmsg ? errmsg : mysql_error(myconn->pgsql)));
}
@ -5005,10 +5011,10 @@ void PgSQL_Session::handler_minus1_GenerateErrorMessage(PgSQL_Data_Stream* myds,
switch (status) {
case PROCESSING_QUERY:
if (myconn) {
PgSQL_Result_to_PgSQL_wire(myconn->pgsql_conn, myconn->query_result, myconn->warning_count, myds);
PgSQL_Result_to_PgSQL_wire(myconn, myds);
}
else {
PgSQL_Result_to_PgSQL_wire(NULL, NULL, 0, myds);
PgSQL_Result_to_PgSQL_wire(NULL, myds);
}
break;
case PROCESSING_STMT_PREPARE:
@ -5497,7 +5503,7 @@ handler_again:
switch (status) {
case PROCESSING_QUERY:
PgSQL_Result_to_PgSQL_wire(myconn->pgsql_conn, myconn->query_result, myconn->warning_count, myconn->myds);
PgSQL_Result_to_PgSQL_wire(myconn, myconn->myds);
break;
case PROCESSING_STMT_PREPARE:
{
@ -5591,8 +5597,8 @@ handler_again:
break;
// rc==2 : a multi-resultset (or multi statement) was detected, and the current statement is completed
case 2:
PgSQL_Result_to_PgSQL_wire(myconn->pgsql_conn, myconn->query_result, myconn->warning_count, myconn->myds);
if (myconn->query_result) { // we also need to clear query_result, so that the next staement will recreate it if needed
PgSQL_Result_to_PgSQL_wire(myconn, myconn->myds);
if (myconn->query_result) { // we also need to clear query_result, so that the next statement will recreate it if needed
if (myconn->query_result_reuse) {
delete myconn->query_result_reuse;
}
@ -5998,7 +6004,8 @@ void PgSQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(
if (client_myds->switching_auth_stage) _pid += 2;
if (max_connections_reached == true) {
proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session=%p , DS=%p , Too many connections\n", this, client_myds);
client_myds->myprot.generate_pkt_ERR(true, NULL, NULL, _pid, 1040, (char*)"08004", (char*)"Too many connections", true);
client_myds->myprot.generate_error_packet(true, false, "Too many connections", PGSQL_ERROR_CODES::ERRCODE_TOO_MANY_CONNECTIONS,
true, true);
proxy_warning("pgsql-max_connections reached. Returning 'Too many connections'\n");
GloPgSQL_Logger->log_audit_entry(PROXYSQL_MYSQL_AUTH_ERR, this, NULL, (char*)"pgsql-max_connections reached");
__sync_fetch_and_add(&PgHGM->status.access_denied_max_connections, 1);
@ -6010,7 +6017,8 @@ void PgSQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(
char* b = (char*)malloc(strlen(a) + strlen(client_myds->myconn->userinfo->username) + 16);
sprintf(b, a, client_myds->myconn->userinfo->username, used_users);
GloPgSQL_Logger->log_audit_entry(PROXYSQL_MYSQL_AUTH_ERR, this, NULL, b);
client_myds->myprot.generate_pkt_ERR(true, NULL, NULL, 2, 1226, (char*)"42000", b, true);
client_myds->myprot.generate_error_packet(true, false, b, PGSQL_ERROR_CODES::ERRCODE_TOO_MANY_CONNECTIONS,
true, true);
proxy_warning("User '%s' has exceeded the 'max_user_connections' resource (current value: %d)\n", client_myds->myconn->userinfo->username, used_users);
free(b);
}
@ -6080,7 +6088,8 @@ void PgSQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(
char* b = (char*)malloc(strlen(a) + strlen(client_myds->myconn->userinfo->username));
sprintf(b, a, client_myds->myconn->userinfo->username);
GloPgSQL_Logger->log_audit_entry(PROXYSQL_MYSQL_AUTH_ERR, this, NULL, b);
client_myds->myprot.generate_pkt_ERR(true, NULL, NULL, _pid, 1040, (char*)"42000", b, true);
client_myds->myprot.generate_error_packet(true, false, b, PGSQL_ERROR_CODES::ERRCODE_SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION,
true, true);
free(b);
}
free(addr);
@ -6106,7 +6115,8 @@ void PgSQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(
char* _a = (char*)"ProxySQL Error: Access denied for user '%s' (using password: %s). SSL is required";
char* _s = (char*)malloc(strlen(_a) + strlen(client_myds->myconn->userinfo->username) + 32);
sprintf(_s, _a, client_myds->myconn->userinfo->username, (client_myds->myconn->userinfo->password ? "YES" : "NO"));
client_myds->myprot.generate_pkt_ERR(true, NULL, NULL, _pid, 1045, (char*)"28000", _s, true);
client_myds->myprot.generate_error_packet(true, false, _s, PGSQL_ERROR_CODES::ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION,
true, true);
proxy_error("ProxySQL Error: Access denied for user '%s' (using password: %s). SSL is required\n", client_myds->myconn->userinfo->username, (client_myds->myconn->userinfo->password ? "YES" : "NO"));
proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 8, "Session=%p , DS=%p . Access denied for user '%s' (using password: %s). SSL is required\n", this, client_myds, client_myds->myconn->userinfo->username, (client_myds->myconn->userinfo->password ? "YES" : "NO"));
__sync_add_and_fetch(&PgHGM->status.client_connections_aborted, 1);
@ -6181,7 +6191,7 @@ void PgSQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(
}
#endif // DEBUG
sprintf(_s, "ProxySQL Error: Access denied for user '%s'@'%s' (using password: %s)", client_myds->myconn->userinfo->username, client_addr, (client_myds->myconn->userinfo->password ? "YES" : "NO"));
client_myds->myprot.generate_error_packet(true,false, _s, "28P01", true);
client_myds->myprot.generate_error_packet(true, false, _s, PGSQL_ERROR_CODES::ERRCODE_INVALID_PASSWORD, true, true);
proxy_error("%s\n", _s);
free(_s);
__sync_fetch_and_add(&PgHGM->status.access_denied_wrong_password, 1);
@ -6246,13 +6256,15 @@ void PgSQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C
/* FIXME: temporary */
l_free(pkt->size, pkt->ptr);
client_myds->setDSS_STATE_QUERY_SENT_NET();
client_myds->myprot.generate_pkt_ERR(true, NULL, NULL, 1, 1045, (char*)"28000", (char*)"Command not supported", true);
client_myds->myprot.generate_error_packet(true, true, "Command not supported", PGSQL_ERROR_CODES::ERRCODE_FEATURE_NOT_SUPPORTED,
false, true);
client_myds->DSS = STATE_SLEEP;
}
else {
l_free(pkt->size, pkt->ptr);
client_myds->setDSS_STATE_QUERY_SENT_NET();
client_myds->myprot.generate_pkt_ERR(true, NULL, NULL, 1, 1045, (char*)"28000", (char*)"Command not supported", true);
client_myds->myprot.generate_error_packet(true, true, "Command not supported", PGSQL_ERROR_CODES::ERRCODE_FEATURE_NOT_SUPPORTED,
false, true);
client_myds->DSS = STATE_SLEEP;
}
}
@ -6260,7 +6272,7 @@ void PgSQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C
void PgSQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_PROCESS_KILL(PtrSize_t* pkt) {
l_free(pkt->size, pkt->ptr);
client_myds->setDSS_STATE_QUERY_SENT_NET();
client_myds->myprot.generate_pkt_ERR(true, NULL, NULL, 1, 9003, (char*)"28000", (char*)"Command not supported");
client_myds->myprot.generate_error_packet(true, true, "Command not supported", PGSQL_ERROR_CODES::ERRCODE_FEATURE_NOT_SUPPORTED, false);
client_myds->DSS = STATE_SLEEP;
}
@ -7040,7 +7052,8 @@ bool PgSQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C
sprintf(errmsg, m, value1.c_str());
}
client_myds->DSS = STATE_QUERY_SENT_NET;
client_myds->myprot.generate_pkt_ERR(true, NULL, NULL, 1, 1115, (char*)"42000", errmsg, true);
client_myds->myprot.generate_error_packet(true, true, errmsg,
PGSQL_ERROR_CODES::ERRCODE_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION, false, true);
client_myds->DSS = STATE_SLEEP;
status = WAITING_CLIENT_DATA;
free(errmsg);
@ -7299,7 +7312,8 @@ bool PgSQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C
errmsg = (char*)malloc(charset.length() + strlen(m));
sprintf(errmsg, m, charset.c_str());
client_myds->DSS = STATE_QUERY_SENT_NET;
client_myds->myprot.generate_pkt_ERR(true, NULL, NULL, 1, 1115, (char*)"42000", errmsg, true);
client_myds->myprot.generate_error_packet(true, true, errmsg,
PGSQL_ERROR_CODES::ERRCODE_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION, false, true);
client_myds->DSS = STATE_SLEEP;
status = WAITING_CLIENT_DATA;
free(errmsg);
@ -7519,7 +7533,8 @@ __exit_set_destination_hostgroup:
client_myds->DSS = STATE_QUERY_SENT_NET;
char buf[140];
sprintf(buf, "ProxySQL Error: connection is locked to hostgroup %d but trying to reach hostgroup %d", locked_on_hostgroup, current_hostgroup);
client_myds->myprot.generate_pkt_ERR(true, NULL, NULL, client_myds->pkt_sid + 1, 9006, (char*)"Y0000", buf);
client_myds->myprot.generate_error_packet(true, true, buf,
PGSQL_ERROR_CODES::ERRCODE_RAISE_EXCEPTION, false);
thread->status_variables.stvar[st_var_hostgroup_locked_queries]++;
RequestEnd(NULL);
l_free(pkt->size, pkt->ptr);
@ -7897,58 +7912,81 @@ void PgSQL_Session::MySQL_Stmt_Result_to_MySQL_wire(MYSQL_STMT* stmt, PgSQL_Conn
}
}
void PgSQL_Session::PgSQL_Result_to_PgSQL_wire(PGconn* pgsql, PgSQL_Query_Result* query_result, unsigned int warning_count, PgSQL_Data_Stream* _myds) {
if (pgsql == NULL) {
void PgSQL_Session::PgSQL_Result_to_PgSQL_wire(PgSQL_Connection* _conn, PgSQL_Data_Stream* _myds) {
if (_conn == NULL) {
// error
client_myds->myprot.generate_pkt_ERR(true, NULL, NULL, client_myds->pkt_sid + 1, 2013, (char*)"HY000", (char*)"Lost connection to MySQL server during query");
client_myds->myprot.generate_error_packet(true, true, "Lost connection to PostgreSQL server during query",
PGSQL_ERROR_CODES::ERRCODE_CONNECTION_FAILURE, false);
return;
}
assert(query_result);
PgSQL_Query_Result* query_result = _conn->query_result;
bool transfer_started = query_result->is_transfer_started();
bool resultset_completed = query_result->get_resultset(client_myds->PSarrayOUT);
CurrentQuery.rows_sent = query_result->get_num_rows();
bool com_field_list = client_myds->com_field_list;
assert(resultset_completed); // the resultset should always be completed if PgSQL_Result_to_PgSQL_wire is called
if (transfer_started == false) { // we have all the resultset when PgSQL_Result_to_PgSQL_wire was called
/*if (qpo && qpo->cache_ttl > 0 && com_field_list == false) { // the resultset should be cached
if (mysql_errno(pgsql) == 0 &&
(mysql_warning_count(pgsql) == 0 ||
mysql_thread___query_cache_handle_warnings == 1)) { // no errors
if (
(qpo->cache_empty_result == 1)
|| (
(qpo->cache_empty_result == -1)
&&
(thread->variables.query_cache_stores_empty_result || query_result->num_rows)
)
) {
client_myds->resultset->copy_add(client_myds->PSarrayOUT, 0, client_myds->PSarrayOUT->len);
client_myds->resultset_length = query_result->resultset_size;
unsigned char* aa = client_myds->resultset2buffer(false);
while (client_myds->resultset->len) client_myds->resultset->remove_index(client_myds->resultset->len - 1, NULL);
bool deprecate_eof_active = client_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF;
GloQC->set(
client_myds->myconn->userinfo->hash,
(const unsigned char*)CurrentQuery.QueryPointer,
CurrentQuery.QueryLength,
aa,
client_myds->resultset_length,
thread->curtime / 1000,
thread->curtime / 1000,
thread->curtime / 1000 + qpo->cache_ttl,
deprecate_eof_active
);
l_free(client_myds->resultset_length, aa);
client_myds->resultset_length = 0;
}
if (query_result && query_result->get_result_packet_type() != PGSQL_QUERY_RESULT_EMPTY) {
bool transfer_started = query_result->is_transfer_started();
// if there is an error, it will be false so results are not cached
bool is_tuple = query_result->get_result_packet_type() == (PGSQL_QUERY_RESULT_TUPLE | PGSQL_QUERY_RESULT_COMMAND | PGSQL_QUERY_RESULT_READY);
bool resultset_completed = query_result->get_resultset(client_myds->PSarrayOUT);
CurrentQuery.rows_sent = query_result->get_num_rows();
assert(resultset_completed); // the resultset should always be completed if PgSQL_Result_to_PgSQL_wire is called
if (transfer_started == false) { // we have all the resultset when PgSQL_Result_to_PgSQL_wire was called
if (qpo && qpo->cache_ttl > 0 && is_tuple == true) { // the resultset should be cached
/*if (mysql_errno(pgsql) == 0 &&
(mysql_warning_count(pgsql) == 0 ||
mysql_thread___query_cache_handle_warnings == 1)) { // no errors
if (
(qpo->cache_empty_result == 1)
|| (
(qpo->cache_empty_result == -1)
&&
(thread->variables.query_cache_stores_empty_result || query_result->num_rows)
)
) {
client_myds->resultset->copy_add(client_myds->PSarrayOUT, 0, client_myds->PSarrayOUT->len);
client_myds->resultset_length = query_result->resultset_size;
unsigned char* aa = client_myds->resultset2buffer(false);
while (client_myds->resultset->len) client_myds->resultset->remove_index(client_myds->resultset->len - 1, NULL);
bool deprecate_eof_active = client_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF;
GloQC->set(
client_myds->myconn->userinfo->hash,
(const unsigned char*)CurrentQuery.QueryPointer,
CurrentQuery.QueryLength,
aa,
client_myds->resultset_length,
thread->curtime / 1000,
thread->curtime / 1000,
thread->curtime / 1000 + qpo->cache_ttl,
deprecate_eof_active
);
l_free(client_myds->resultset_length, aa);
client_myds->resultset_length = 0;
}
}*/
}
}*/
}
/*else { // no result set
int myerrno = mysql_errno(pgsql);
}
} else { // if query result is empty, means there was an error before query result was generated
if (!_conn->is_error_present())
assert(0); // if query result is empty, there should be an error present in connection.
if (_myds && _myds->killed_at) {
if (_myds->kill_type == 0) {
client_myds->myprot.generate_error_packet(true, true, (char*)"Query execution was interrupted, query_timeout exceeded",
PGSQL_ERROR_CODES::ERRCODE_QUERY_CANCELED, false);
//PgHGM->p_update_pgsql_error_counter(p_pgsql_error_type::proxysql, _conn->parent->myhgc->hid, _conn->parent->address, _conn->parent->port, 1907);
}
else {
client_myds->myprot.generate_error_packet(true, true, (char*)"Query execution was interrupted",
PGSQL_ERROR_CODES::ERRCODE_QUERY_CANCELED, false);
//PgHGM->p_update_pgsql_error_counter(p_pgsql_error_type::proxysql, _conn->parent->myhgc->hid, _conn->parent->address, _conn->parent->port, 1317);
}
}
else {
client_myds->myprot.generate_error_packet(true, true, _conn->get_error_message().c_str(), _conn->get_error_code(), false);
//PgHGM->p_update_pgsql_error_counter(p_pgsql_error_type::proxysql, _conn->parent->myhgc->hid, _conn->parent->address, _conn->parent->port, 1907);
}
/*int myerrno = mysql_errno(pgsql);
if (myerrno == 0) {
unsigned int num_rows = mysql_affected_rows(pgsql);
uint16_t setStatus = (active_transactions ? SERVER_STATUS_IN_TRANS : 0);
@ -7976,8 +8014,9 @@ void PgSQL_Session::PgSQL_Result_to_PgSQL_wire(PGconn* pgsql, PgSQL_Query_Result
client_myds->myprot.generate_pkt_ERR(true, NULL, NULL, client_myds->pkt_sid + 1, mysql_errno(pgsql), sqlstate, mysql_error(pgsql));
}
//client_myds->pkt_sid++;
}*/
//}
}
*/
}
}
void PgSQL_Session::SQLite3_to_MySQL(SQLite3_result* result, char* error, int affected_rows, MySQL_Protocol* myprot, bool in_transaction, bool deprecate_eof_active) {
@ -8050,10 +8089,12 @@ void PgSQL_Session::SQLite3_to_MySQL(SQLite3_result* result, char* error, int af
if (error) {
// there was an error
if (strcmp(error, (char*)"database is locked") == 0) {
myprot->generate_pkt_ERR(true, NULL, NULL, sid, 1205, (char*)"HY000", error);
client_myds->myprot.generate_error_packet(true, true, error,
PGSQL_ERROR_CODES::ERRCODE_T_R_DEADLOCK_DETECTED, false);
}
else {
myprot->generate_pkt_ERR(true, NULL, NULL, sid, 1045, (char*)"28000", error);
client_myds->myprot.generate_error_packet(true, true, error,
PGSQL_ERROR_CODES::ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION, false);
}
}
else {

@ -10830,7 +10830,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;
}

Loading…
Cancel
Save