You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
proxysql/include/PgSQL_Protocol.h

1216 lines
48 KiB

#ifndef __POSTGRES_PROTOCOL_H
#define __POSTGRES_PROTOCOL_H
#include "proxysql.h"
#include "gen_utils.h"
#include "MySQL_Protocol.h"
/* no-auth modes */
#define PG_PKT_AUTH_ANY -1 /* same as trust but without username check */
#define PG_PKT_AUTH_TRUST AUTH_OK
/* protocol codes in Authentication* 'R' messages from server */
#define PG_PKT_AUTH_OK 0
#define PG_PKT_AUTH_KRB4 1 /* not supported */
#define PG_PKT_AUTH_KRB5 2 /* not supported */
#define PG_PKT_AUTH_PLAIN 3
#define PG_PKT_AUTH_CRYPT 4 /* not supported */
#define PG_PKT_AUTH_MD5 5
#define PG_PKT_AUTH_SCM_CREDS 6 /* not supported */
#define PG_PKT_AUTH_GSS 7 /* not supported */
#define PG_PKT_AUTH_GSS_CONT 8 /* not supported */
#define PG_PKT_AUTH_SSPI 9 /* not supported */
#define PG_PKT_AUTH_SASL 10
#define PG_PKT_AUTH_SASL_CONT 11
#define PG_PKT_AUTH_SASL_FIN 12
/* internal codes */
#define AUTH_CERT 107
#define AUTH_PEER 108
#define AUTH_HBA 109
#define AUTH_REJECT 110
#define AUTH_PAM 111
#define AUTH_SCRAM_SHA_256 112
#define PG_PKT_STARTUP_V2 0x20000
#define PG_PKT_STARTUP 0x30000
#define PG_PKT_CANCEL 80877102
#define PG_PKT_SSLREQ 80877103
#define PG_PKT_GSSENCREQ 80877104
#define PG_PKT_DEFAULT_SIZE 64
/* old style V2 header: len:4b code:4b */
#define OLD_HEADER_LEN 8
/* new style V3 packet header len - type:1b, len:4b */
#define NEW_HEADER_LEN 5
#define PGSQL_RESULTSET_BUFLEN (16 * 1024)
class ProxySQL_Admin;
struct PgCredentials;
struct ScramState;
enum class EXECUTION_STATE {
FAILED = 0,
SUCCESSFUL,
PENDING
};
struct pgsql_hdr {
uint32_t type;
uint32_t len;
PtrSize_t data;
};
class PG_pkt
{
public:
PG_pkt(unsigned c = PG_PKT_DEFAULT_SIZE) {
ownership = true;
capacity = l_near_pow_2(c);
size = 0;
ptr = (char*)malloc(capacity);
multiple_pkt_mode = false;
}
PG_pkt(void* _ptr, unsigned int _capacity) {
ownership = false;
ptr = (char*)_ptr;
capacity = _capacity;
size = 0;
}
~PG_pkt() {
reset();
}
void reset() {
if (ptr) {
if (ownership == true)
free(ptr);
else
assert(size == capacity); // just to check if we are not passing buffer boundaries
}
ptr = nullptr;
size = 0;
capacity = 0;
multiple_pkt_mode = false;
pkt_offset.clear();
}
std::pair<char*, unsigned int> detach() {
std::pair<char*, unsigned int> result(ptr, size);
ptr = nullptr;
size = 0;
capacity = 0;
multiple_pkt_mode = false;
pkt_offset.clear();
return result;
}
PtrSize_t* get_PtrSize(unsigned c = PG_PKT_DEFAULT_SIZE);
/**
* @brief Moves the current packet data to a PtrSizeArray.
*
* This function adds the current `ptr` and `size` to the provided
* `PtrSizeArray` (`psa`). It then resets the internal buffer (`ptr` and
* `size`) to a new buffer with a capacity of `c` if `c` is not zero.
*
* @param psa The PtrSizeArray where the current packet data will be added.
* @param c The desired capacity of the new internal buffer.
*/
void to_PtrSizeArray(PtrSizeArray* psa, unsigned c = PG_PKT_DEFAULT_SIZE);
void set_multi_pkt_mode(bool mode) {
multiple_pkt_mode = mode;
if (mode == false)
pkt_offset.clear();
}
/**
* @brief Resizes the internal buffer if needed to accommodate additional data.
*
* If the current size of the internal buffer (`size`) plus the requested length
* (`len`) exceeds the buffer's capacity (`capacity`), this function reallocates
* the buffer to a new size that's the nearest power of 2 greater than or equal
* to `size + len`.
*
* If the buffer already has enough space, this function does nothing.
*
* @param len The number of bytes of additional space required.
*
* @note This function only resizes the buffer if the `ownership` flag is true,
* indicating that the buffer is owned by the `PG_pkt` object.
*/
void make_space(unsigned int len);
/**
* @brief Appends a single character to the internal buffer.
*
* This function ensures there's enough space in the buffer and then appends
* the given character (`val`) to the end of the buffer.
*
* @param val The character to append.
*/
void put_char(char val);
/**
* @brief Appends a 16-bit unsigned integer to the internal buffer.
*
* This function ensures there's enough space in the buffer and then appends
* the given 16-bit unsigned integer (`val`) in big-endian byte order.
*
* @param val The 16-bit unsigned integer to append.
*/
void put_uint16(uint16_t val);
/**
* @brief Appends a 32-bit unsigned integer to the internal buffer.
*
* This function ensures there's enough space in the buffer and then appends
* the given 32-bit unsigned integer (`val`) in big-endian byte order.
*
* @param val The 32-bit unsigned integer to append.
*/
void put_uint32(uint32_t val);
/**
* @brief Appends a 64-bit unsigned integer to the internal buffer.
*
* This function appends the given 64-bit unsigned integer (`val`) to the
* internal buffer in big-endian byte order.
*
* @param val The 64-bit unsigned integer to append.
*/
void put_uint64(uint64_t val);
/**
* @brief Appends a block of bytes to the internal buffer.
*
* This function ensures there's enough space in the buffer and then copies
* `len` bytes from the provided data pointer (`data`) to the end of the buffer.
*
* @param data A pointer to the beginning of the data to append.
* @param len The number of bytes to append.
*/
void put_bytes(const void* data, int len);
/**
* @brief Appends a null-terminated string to the internal buffer.
*
* This function appends the given null-terminated string (`str`) to the
* internal buffer, including the null terminator.
*
* @param str The null-terminated string to append.
*/
void put_string(const char* str);
void write_generic(int type, const char* pktdesc, ...);
void write_ParameterStatus(const char* key, const char* val) {
write_generic('S', "ss", key, val);
}
void write_AuthenticationOk() {
write_generic('R', "i", 0);
}
void write_AuthenticationRequest(uint32_t auth_type, const uint8_t* data, int len) {
write_generic('R', "ib", auth_type, data, len);
}
void write_ReadyForQuery(char txn_state = 'I') {
write_generic('Z', "c", txn_state);
}
void write_CommandComplete(const char* desc) {
write_generic('C', "s", desc);
}
void write_BackendKeyData(const uint8_t* key) {
write_generic('K', "b", key, 8);
}
void write_StartupMessage(const char* user, const char* parms, int parms_len) {
write_generic(PG_PKT_STARTUP, "bsss", parms, parms_len, "user", user, "");
}
void write_PasswordMessage(const char* psw) {
write_generic('p', "s", psw);
}
void write_ParseCompletion() {
//put_char('1');
//put_uint32(4);
write_generic('1', "");
}
void write_BindCompletion() {
//put_char('2');
//put_uint32(4);
write_generic('2', "");
}
void write_CloseCompletion() {
//put_char('3');
//put_uint32(4);
write_generic('3', "");
}
void write_RowDescription(const char* tupdesc, ...);
void write_DataRow(const char* tupdesc, ...);
private:
/**
* @brief Initializes a new packet with a specified type.
*
* This function sets the first byte of the packet to the given `type` and
* reserves space for the packet length (which will be filled in later).
*
* @param type The type of the packet (must be a value between 0 and 255).
*/
void start_packet(int type);
/**
* @brief Completes a packet by filling in the length field.
*
* This function calculates the length of the packet (excluding the type
* byte) and writes it to the appropriate position in the packet buffer.
*
* @note If the `multiple_pkt_mode` flag is set to true, the length is
* calculated and written based on the last recorded packet offset.
*/
void finish_packet();
char* ptr;
unsigned int size;
unsigned int capacity;
// currently for debug only. will replace this with a single variable that will contain last pkt offset
std::vector<unsigned int> pkt_offset;
bool multiple_pkt_mode = false;
bool ownership = true;
friend void SQLite3_to_Postgres(PtrSizeArray* psa, SQLite3_result* result, char* error, int affected_rows, const char* query_type, bool send_ready_for_query, char txn_state);
};
class PgSQL_Protocol;
struct ColumnMetadata {
char* name; // Column name
uint32_t table_oid; // Table OID
uint16_t column_index; // Column index in table
uint32_t type_oid; // Data type OID
int16_t length; // Column length (-1 for variable length)
int32_t type_modifier; // Type modifier (-1 if none)
uint16_t format; // 0 = text, 1 = binary
};
/* Not Used anymore. To be removed in next iteration
class PgSQL_Describe_Prepared_Info {
public:
uint32_t* parameter_types; // Array of parameter type OIDs
size_t parameter_types_count; // Number of parameters
ColumnMetadata* columns; // Array of column metadata
size_t columns_count; // Number of columns
PgSQL_Describe_Prepared_Info();
~PgSQL_Describe_Prepared_Info();
// Populate metadata from PGresult
void populate(const PGresult* result);
void clear();
private:
void extract_parameters(const PGresult* result);
void extract_columns(const PGresult* result);
};
*/
#define PGSQL_QUERY_RESULT_NO_DATA 0x00
#define PGSQL_QUERY_RESULT_TUPLE 0x01
#define PGSQL_QUERY_RESULT_COMMAND 0x02
#define PGSQL_QUERY_RESULT_READY 0x04
#define PGSQL_QUERY_RESULT_ERROR 0x08
#define PGSQL_QUERY_RESULT_EMPTY 0x10
#define PGSQL_QUERY_RESULT_COPY_OUT 0x20
#define PGSQL_QUERY_RESULT_NOTICE 0x40
class PgSQL_Query_Result {
public:
PgSQL_Query_Result();
~PgSQL_Query_Result();
/**
* @brief Initializes the PgSQL_Query_Result object.
*
* This method initializes the `PgSQL_Query_Result` object with the
* provided `PgSQL_Protocol`, `PgSQL_Data_Stream`, and `PgSQL_Connection`
* objects. It also initializes the internal buffer using the
* `buffer_init` method and resets any internal state.
*
* @param _proto A pointer to the `PgSQL_Protocol` object associated with
* this query result.
* @param _myds A pointer to the `PgSQL_Data_Stream` object associated with
* this query result.
* @param _conn A pointer to the `PgSQL_Connection` object associated with
* this query result.
*
* @note This method is typically called when a new query is executed.
*/
void init(PgSQL_Protocol* _proto, PgSQL_Data_Stream* _myds, PgSQL_Connection* _conn);
/**
* @brief Adds a row description to the query result.
*
* This method adds a row description (from a `PGresult` object) to the
* query result. It copies the row description data to the internal buffer
* or to the `PSarrayOUT` if the buffer is full.
*
* @param result A pointer to a `PGresult` object containing the row
* description to add.
*
* @return The number of bytes added to the query result.
*
* @note This method is used to prepare the client for receiving rows
* with the corresponding data types and column names.
*/
unsigned int add_row_description(const PGresult* result);
/**
* @brief Adds a row of data to the query result.
*
* This method adds a row of data (from a `PGresult` object) to the query
* result. It copies the row data to the internal buffer or to the
* `PSarrayOUT` if the buffer is full.
*
* @param result A pointer to a `PGresult` object containing the row data
* to add.
*
* @return The number of bytes added to the query result.
*/
unsigned int add_row(const PGresult* result);
/**
* @brief Adds a row of data to the query result from a PSresult.
*
* This method adds a row of data (from a `PSresult` object) to the query
* result. It copies the row data to the internal buffer or to the
* `PSarrayOUT` if the buffer is full.
*
* @param result A pointer to a `PSresult` object containing the row data
* to add.
*
* @return The number of bytes added to the query result.
*/
unsigned int add_row(const PSresult* result);
/**
* @brief Adds a command completion message to the query result.
*
* This method adds a command completion message (from a `PGresult`
* object) to the query result. It extracts the command tag and affected
* rows count (if requested) and adds them to the internal buffer or the
* `PSarrayOUT` if the buffer is full.
*
* @param result A pointer to a `PGresult` object containing the command
* completion message.
* @param extract_affected_rows A boolean flag indicating whether to
* extract the affected rows count from the
* `PGresult` object.
*
* @return The number of bytes added to the query result.
*
* @note This method is used to signal the completion of a command
* (e.g., INSERT, UPDATE, DELETE) and to send the appropriate
* response to the client.
*/
unsigned int add_command_completion(const PGresult* result, bool extract_affected_rows = true);
/**
* @brief Adds an error message to the query result.
*
* This method adds an error message (from a `PGresult` object) to the
* query result. It copies the error data to the internal buffer or to the
* `PSarrayOUT` if the buffer is full.
*
* @param result A pointer to a `PGresult` object containing the error
* message to add.
*
* @return The number of bytes added to the query result.
*
* @note This method is used to handle errors that occur during query
* execution and to send the error information to the client.
*/
unsigned int add_error(const PGresult* result);
/**
* @brief Adds an empty query response to the query result.
*
* This method adds an empty query response (for example from query
* returning no rows) to the query result. It copies the empty query
* response data to the internal buffer or to the `PSarrayOUT` if the
* buffer is full.
*
* @param result A pointer to a `PGresult` object representing the empty
* response.
*
* @return The number of bytes added to the query result.
*
* @note This method is used to handle cases where a query does not
* return any rows or data, and to send the appropriate response
* to the client.
*/
unsigned int add_empty_query_response(const PGresult* result);
/**
* @brief Adds a ready status message to the query result.
*
* This method adds a ready status message to the query result, indicating
* that the server is ready for a new query. The status reflects the
* transaction state.
*
* @param txn_status The transaction status type, indicating whether a
* transaction is in progress or not.
*
* @return The number of bytes added to the query result.
*
* @note This method is used to signal to the client that the server is
* ready for a new query and that any previous query has completed.
*/
unsigned int add_ready_status(PGTransactionStatusType txn_status);
/**
* @brief Adds the start of a COPY OUT response to the packet.
*
* This function adds the initial part of a COPY OUT response to the packet.
* It uses the provided PGresult object to determine the necessary information
* to include in the response.
*
* @param result A pointer to the PGresult object containing the response data.
* @return The number of bytes added to the packet.
*/
unsigned int add_copy_out_response_start(const PGresult* result);
/**
* @brief Adds a row of data to the COPY OUT response.
*
* This function adds a row of data to the ongoing COPY OUT response. The data
* is provided as a pointer to the row data and its length.
*
* @param data A pointer to the row data to be added.
* @param len The length of the row data in bytes.
* @return The number of bytes added to the packet.
*/
unsigned int add_copy_out_row(const void* data, unsigned int len);
/**
* @brief Adds the end of a COPY OUT response to the packet.
*
* This function adds the final part of a COPY OUT response to the packet,
* indicating the end of the response.
*
* @return The number of bytes added to the packet.
*/
unsigned int add_copy_out_response_end();
/**
* @brief Adds a notice message to the query result.
*
* This method adds a notice message, to the query result.
*
* The notice is copied to the internal buffer or to the `PSarrayOUT` if the buffer is full.
*
* @param result A pointer to a `PGresult` object containing the notice message to add.
* @return The number of bytes added to the query result.
*
*/
unsigned int add_notice(const PGresult* result);
/**
* @brief Adds a "No Data" message to the query result.
*
* This method adds a "No Data" message to the query result, indicating that
* the executed statement does not return any rows.
*
* @return The number of bytes added to the query result.
*
*/
unsigned int add_no_data();
/**
* @brief Adds a prepare completion message to the query result.
*
* This method adds a prepare completion message to the query result, indicating
* that a prepared statement has been successfully created.
*
* @return The number of bytes added to the query result.
*
*/
unsigned int add_parse_completion();
/**
* @brief Adds a describe completion message to the query result.
*
* This method adds a describe completion message (from a `PGresult` object) to the query result.
* The describe completion message provides metadata about a prepared statement or portal,
* such as the statement type and associated fields.
*
* @param result A pointer to a `PGresult` object containing the describe completion data.
* @param stmt_type The type of statement being described (e.g., prepared statement or portal).
*
* @return The number of bytes added to the query result.
*
*/
unsigned int add_describe_completion(const PGresult* result, uint8_t stmt_type);
/**
* @brief Retrieves the query result set and copies it to a PtrSizeArray.
*
* This method retrieves the accumulated query result, including row
* descriptions, rows, errors, etc., and copies it to the provided
* `PtrSizeArray`. It also resets the internal state of the
* `PgSQL_Query_Result` object after the result set is copied.
*
* @param PSarrayFinal The `PtrSizeArray` where the query result will be
* copied.
*
* @return `true` if the result set is complete (i.e., a ready status
* packet has been added), `false` otherwise.
*
* @note This method is typically called when all query results have been
* accumulated and are ready to be sent to the client.
*/
bool get_resultset(PtrSizeArray* PSarrayFinal); // this also calls reset
/**
* @brief Calculates the current size of the PgSQL_Query_Result object.
*
* This method calculates the total size of the `PgSQL_Query_Result`
* object in bytes, including the size of the object itself, the internal
* buffer, and any packets stored in the `PSarrayOUT`.
*
* @return The current size of the `PgSQL_Query_Result` object in bytes.
*/
unsigned long long current_size();
/**
* @brief Clears the contents of the PgSQL_Query_Result object.
*
* This method resets the internal state of the PgSQL_Query_Result object, freeing any allocated buffers
* and removing all packets from the output array.
*
* The method performs the following actions:
* - Removes and frees all packets from the PSarrayOUT array.
* - Initializes the internal buffer for result data.
* - Resets all counters and state variables to their default values.
*
*/
void clear();
inline bool is_transfer_started() const { return transfer_started; }
inline unsigned long long get_num_rows() const { return num_rows; }
inline unsigned long long get_affected_rows() const { return affected_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:
/**
* @brief Initializes the internal buffer for storing query results.
*
* If the `buffer` pointer is null, this function allocates a new buffer
* of size `PGSQL_RESULTSET_BUFLEN` and assigns it to the `buffer` pointer.
* It also resets the `buffer_used` counter to 0, indicating that the
* buffer is currently empty.
*
* @note This method is called by the `init` method to ensure that the
* buffer is properly initialized before any query results are added.
*/
void buffer_init();
inline unsigned int buffer_available_capacity() const { return (PGSQL_RESULTSET_BUFLEN - buffer_used); }
/**
* @brief Reserves space in the internal buffer and returns a pointer.
*
* This method checks if there is enough space in the internal `buffer`
* to store the requested `size` of data. If there is space, it returns
* a pointer to the available location and updates `buffer_used`.
* Otherwise, it flushes the buffer to `PSarrayOUT`, allocates a new
* buffer, and returns a pointer to the available location.
*
* @param size The number of bytes of space to reserve.
*
* @return A pointer to the reserved space in the buffer, or `NULL` if
* there is not enough space.
*
* @note This method is used to efficiently manage the internal buffer
* and avoid unnecessary memory allocations.
*/
unsigned char* buffer_reserve_space(unsigned int size);
/**
* @brief Flushes the internal buffer to the PSarrayOUT.
*
* This method moves the data currently stored in the internal `buffer`
* to the `PSarrayOUT` (a `PtrSizeArray`). It then resizes the
* `buffer` to the default size `PGSQL_RESULTSET_BUFLEN` and resets
* `buffer_used` to 0.
*
* @note This method is used when the internal `buffer` is full and
* needs to be flushed to release the memory and continue adding
* more data.
*/
void buffer_to_PSarrayOut();
/**
* @brief Resets the internal state of the PgSQL_Query_Result object.
*
* This method resets the internal state of the `PgSQL_Query_Result`
* object to its initial state, including clearing the result set data,
* resetting counters, and preparing for a new query result.
*
* @note This method is typically called after the query result has been
* sent to the client and the object is ready to handle a new query.
*/
void reset();
PtrSizeArray PSarrayOUT;
unsigned long long resultset_size;
unsigned long long num_rows;
unsigned long long pkt_count;
unsigned long long affected_rows;
unsigned int num_fields;
unsigned int buffer_used;
unsigned char* buffer;
PgSQL_Protocol* proto;
PgSQL_Data_Stream* myds;
PgSQL_Connection* conn;
bool transfer_started;
uint8_t result_packet_type;
friend class PgSQL_Protocol;
friend class PgSQL_Connection;
};
class PgSQL_Protocol : public MySQL_Protocol {
public:
void init(PgSQL_Data_Stream** __myds, PgSQL_Connection_userinfo* __userinfo, PgSQL_Session* __sess) {
myds = __myds;
userinfo = __userinfo;
sess = __sess;
current_PreStmt = NULL;
}
PgSQL_Data_Stream* get_myds() { return *myds; }
/**
* @brief Generates the initial handshake packet for the PostgreSQL protocol.
*
* This function generates the initial handshake packet that is sent to the
* PostgreSQL client. It includes an authentication request based on the
* configured authentication method (`pgsql_thread___authentication_method`).
*
* @param send A boolean flag indicating whether to send the packet immediately
* or just generate it.
* @param _ptr A pointer to a pointer where the generated packet data will be
* stored (if `send` is false).
* @param len A pointer to an unsigned integer where the length of the
* generated packet will be stored (if `send` is false).
* @param _thread_id A pointer to a 32-bit unsigned integer where the thread ID
* will be stored.
* @param deprecate_eof_active A boolean flag to control deprecation of EOF
* active behavior.
*
* @return `true` if the packet was successfully generated, `false` otherwise.
*
* @note This function updates the authentication method and next packet type
* in the `PgSQL_Data_Stream` object. If `send` is true, it also adds
* the generated packet to the output buffer and updates the data stream
* state.
*/
bool generate_pkt_initial_handshake(bool send, void** ptr, unsigned int* len, uint32_t* thread_id, bool deprecate_eof_active) override;
/**
* @brief Processes a PostgreSQL startup packet.
*
* This function processes a PostgreSQL startup packet received from the
* client. It extracts the connection parameters, checks for SSL requests,
* and validates the user name.
*
* @param pkt A pointer to the beginning of the packet buffer.
* @param len The length of the packet buffer in bytes.
* @param ssl_request A boolean variable that is set to `true` if the client
* requests an SSL connection.
*
* @return `true` if the startup packet was successfully processed, `false`
* otherwise.
*
* @note This function updates the data stream state to `STATE_SERVER_HANDSHAKE`
* after successfully processing the startup packet. It also handles
* SSL requests and generates an error packet if the user name is
* missing.
*/
bool process_startup_packet(unsigned char* pkt, unsigned int len, bool& ssl_request);
/**
* @brief Processes a PostgreSQL handshake response packet.
*
* This function processes a handshake response packet received from the
* PostgreSQL client. It handles authentication based on the selected
* authentication method (e.g., clear text password, SCRAM-SHA-256) and
* updates the session state.
*
* @param pkt A pointer to the beginning of the packet buffer.
* @param len The length of the packet buffer in bytes.
*
* @return The execution state after processing the handshake response
* packet.
*
* @note This function validates the packet type, retrieves user credentials
* from the database, performs authentication, and updates the session
* state. It also handles errors related to authentication and invalid
* packets.
*/
EXECUTION_STATE process_handshake_response_packet(unsigned char* pkt, unsigned int len);
/**
* @brief Sends a welcome message to the PostgreSQL client.
*
* This function sends a welcome message to the PostgreSQL client after a
* successful authentication. The welcome message includes parameter status
* messages and a ready-for-query message.
*
* @note This function updates the output buffer with the welcome message
* data. It also sets the session state to `STATE_CLIENT_AUTH_OK`.
*/
void welcome_client();
/**
* @brief Generates an error packet for the PostgreSQL protocol.
*
* This function generates an error packet that is sent to the PostgreSQL
* client in case of an error. It includes the error severity, code, and
* message.
*
* @param send A boolean flag indicating whether to send the packet
* immediately or just generate it.
* @param ready A boolean flag indicating whether to generate a ready-for-query
* packet after the error.
* @param msg The error message to be included in the packet.
* @param code The error code.
* @param fatal A boolean flag indicating whether the error is fatal.
* @param track A boolean flag to control whether to track the error count.
* @param _ptr A pointer to a `PtrSize_t` structure (if `send` is false)
* where the generated packet data will be stored.
*
* @note This function updates the output buffer with the generated error
* packet. It also updates the data stream state to `STATE_ERR` if
* necessary.
*/
void generate_error_packet(bool send, bool ready, const char* msg, PGSQL_ERROR_CODES code, bool fatal, bool track = false, PtrSize_t* _ptr = NULL);
/**
* @brief Generates an "OK" packet for the PostgreSQL protocol.
*
* This function generates an "OK" packet, which is sent to the PostgreSQL
* client after a successful command execution (e.g., INSERT, UPDATE, DELETE,
* SELECT). It includes a command tag (e.g., "INSERT 0 10" for an INSERT
* command that affected 10 rows) and a ready-for-query message if `ready`
* is true.
*
* @param send A boolean flag indicating whether to send the packet
* immediately or just generate it.
* @param ready A boolean flag indicating whether to generate a ready-for-query
* packet after the "OK" packet.
* @param msg An optional message to be included in the "OK" packet.
* @param rows The number of rows affected by the command (used for
* INSERT, UPDATE, DELETE, and SELECT).
* @param query The original query string that was executed.
* @param _ptr A pointer to a `PtrSize_t` structure (if `send` is false)
* where the generated packet data will be stored.
*
* @return `true` if the packet was successfully generated, `false` otherwise.
*
* @note This function extracts the appropriate command tag based on the
* `query` string and constructs the "OK" packet accordingly. It also
* updates the output buffer with the generated packet. If `ready` is
* true, it also generates and sends a ready-for-query packet.
*/
bool generate_ok_packet(bool send, bool ready, const char* msg, int rows, const char* query, char trx_state = 'I', PtrSize_t* _ptr = NULL,
const std::vector<std::pair<std::string,std::string>>& param_status = std::vector<std::pair<std::string, std::string>>());
bool generate_parse_completion_packet(bool send, bool ready, char trx_state, PtrSize_t* _ptr = NULL);
bool generate_ready_for_query_packet(bool send, char trx_state, PtrSize_t* _ptr = NULL);
// Not Used anymore. To be removed in next iteration
//bool generate_describe_completion_packet(bool send, bool ready, const PgSQL_Describe_Prepared_Info* desc, uint8_t stmt_type, char trx_state, PtrSize_t* _ptr = NULL);
bool generate_close_completion_packet(bool send, bool ready, char trx_state, PtrSize_t* _ptr = NULL);
bool generate_bind_completion_packet(bool send, bool ready, char trx_state, PtrSize_t* _ptr = NULL);
bool generate_no_data_packet(bool send, PtrSize_t* _ptr = NULL);
// temporary overriding generate_pkt_OK to avoid crash. FIXME remove this
bool generate_pkt_OK(bool send, void** ptr, unsigned int* len, uint8_t sequence_id, unsigned int affected_rows,
uint64_t last_insert_id, uint16_t status, uint16_t warnings, char* msg, bool eof_identifier = false) {
char txn_state = 'I';
if (status & SERVER_STATUS_IN_TRANS) {
txn_state = 'T';
}
return generate_ok_packet(send, true, msg, affected_rows, "OK 1", txn_state);
}
// temporary overriding generate_pkt_EOF to avoid crash. FIXME remove this
bool generate_pkt_EOF(bool send, void** ptr, unsigned int* len, uint8_t sequence_id, uint16_t warnings,
uint16_t status, MySQL_ResultSet* myrs = NULL) {
char txn_state = 'I';
if (status & SERVER_STATUS_IN_TRANS) {
txn_state = 'T';
}
return generate_ok_packet(send, true, NULL, 0, "OK 1", txn_state);
}
// temporary overriding generate_pkt_ERR to avoid crash. FIXME remove this
bool generate_pkt_ERR(bool send, void** ptr, unsigned int* len, uint8_t sequence_id, uint16_t error_code,
char* sql_state, const char* sql_message, bool track = false) {
generate_error_packet(send, true, sql_message, PGSQL_ERROR_CODES::ERRCODE_RAISE_EXCEPTION, false, track);
return true;
}
//bool generate_row_description(bool send, PgSQL_Query_Result* rs, const PG_Fields& fields, unsigned int size);
/**
* @brief Copies a row description from a PGresult to a PgSQL_Query_Result.
*
* This function copies the row description from a `PGresult` object (typically
* obtained from libpq) to a `PgSQL_Query_Result` object. The row description
* contains information about the columns returned by a query, such as column
* names, data types, and other metadata.
*
* @param send A boolean flag indicating whether to send the generated packet
* immediately or just generate it. (Currently not supported).
* @param pg_query_result A pointer to the `PgSQL_Query_Result` object where the
* row description will be copied.
* @param result A pointer to the `PGresult` object containing the row
* description to be copied.
*
* @return The number of bytes copied to the `PgSQL_Query_Result` object.
*
* @note This function is used to prepare the client for receiving rows
* with the corresponding data types and column names.
*/
unsigned int copy_row_description_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, const PGresult* result);
/**
* @brief Copies a row of data from a PGresult to a PgSQL_Query_Result.
*
* This function copies a row of data from a `PGresult` object (typically
* obtained from libpq) to a `PgSQL_Query_Result` object. The row data
* represents a single row from the result set of a query.
*
* @param send A boolean flag indicating whether to send the generated packet
* immediately or just generate it. (Currently not supported).
* @param pg_query_result A pointer to the `PgSQL_Query_Result` object where the
* row data will be copied.
* @param result A pointer to the `PGresult` object containing the row data
* to be copied.
*
* @return The number of bytes copied to the `PgSQL_Query_Result` object.
*/
unsigned int copy_row_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, const PGresult* result);
/**
* @brief Copies a command completion message from a PGresult to a
* PgSQL_Query_Result.
*
* This function copies a command completion message from a `PGresult` object
* (typically obtained from libpq) to a `PgSQL_Query_Result` object. The
* command completion message indicates that a command (e.g., INSERT, UPDATE,
* DELETE) has finished executing.
*
* @param send A boolean flag indicating whether to send the generated packet
* immediately or just generate it. (Currently not supported).
* @param pg_query_result A pointer to the `PgSQL_Query_Result` object where the
* command completion message will be copied.
* @param result A pointer to the `PGresult` object containing the command
* completion message to be copied.
* @param extract_affected_rows A boolean flag indicating whether to extract
* the affected rows count from the `PGresult`
* object.
*
* @return The number of bytes copied to the `PgSQL_Query_Result` object.
*
* @note This function extracts the command tag and affected rows count (if
* requested) and copies them to the `PgSQL_Query_Result` object.
*/
unsigned int copy_command_completion_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, const PGresult* result, bool extract_affected_rows);
/**
* @brief Copies an error/notice message from a PGresult to a PgSQL_Query_Result.
*
* This function copies an error/notice message from a `PGresult` object (typically
* obtained from libpq) to a `PgSQL_Query_Result` object. The message
* contains information about an error/notice that occurred during query execution.
*
* @param send A boolean flag indicating whether to send the generated packet
* immediately or just generate it. (Currently not supported).
* @param pg_query_result A pointer to the `PgSQL_Query_Result` object where the
* error message will be copied.
* @param result A pointer to the `PGresult` object containing the error
* message to be copied.
* @param is_error A boolean flag indicating whether the message is an error or a notice.
*
* @return The number of bytes copied to the `PgSQL_Query_Result` object.
*
* @note This function extracts the various error fields (severity, code,
* message, detail, etc.) from the `PGresult` object and copies them
* to the `PgSQL_Query_Result` object.
*/
unsigned int copy_error_notice_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, const PGresult* result, bool is_error);
/**
* @brief Copies an empty query response from a PGresult to a
* PgSQL_Query_Result.
*
* This function copies an empty query response from a `PGresult` object
* (typically obtained from libpq) to a `PgSQL_Query_Result` object. The
* empty query response indicates that a query did not return any rows.
*
* @param send A boolean flag indicating whether to send the generated packet
* immediately or just generate it. (Currently not supported).
* @param pg_query_result A pointer to the `PgSQL_Query_Result` object where the
* empty query response will be copied.
* @param result A pointer to the `PGresult` object containing the empty query
* response to be copied.
*
* @return The number of bytes copied to the `PgSQL_Query_Result` object.
*/
unsigned int copy_empty_query_response_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, const PGresult* result);
/**
* @brief Copies a ready status message from a PGresult to a
* PgSQL_Query_Result.
*
* This function copies a ready status message from a `PGresult` object
* (typically obtained from libpq) to a `PgSQL_Query_Result` object. The
* ready status indicates that the server is ready for a new query.
*
* @param send A boolean flag indicating whether to send the generated packet
* immediately or just generate it. (Currently not supported).
* @param pg_query_result A pointer to the `PgSQL_Query_Result` object where the
* ready status message will be copied.
* @param txn_status The transaction status type, indicating whether a
* transaction is in progress or not.
*
* @return The number of bytes copied to the `PgSQL_Query_Result` object.
*/
unsigned int copy_ready_status_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, PGTransactionStatusType txn_status);
/**
* @brief Copies a buffer from a PSresult to a PgSQL_Query_Result.
*
* This function copies a buffer of data from a `PSresult` object to a
* `PgSQL_Query_Result` object. The buffer can contain various types of
* data, including row data or other results.
*
* @param send A boolean flag indicating whether to send the generated packet
* immediately or just generate it. (Currently not supported).
* @param pg_query_result A pointer to the `PgSQL_Query_Result` object where the
* buffer will be copied.
* @param result A pointer to the `PSresult` object containing the buffer to
* be copied.
*
* @return The number of bytes copied to the `PgSQL_Query_Result` object.
*/
unsigned int copy_buffer_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, const PSresult* result);
/**
* @brief Copies the start of a response to a PgSQL_Query_Result.
*
* This function copies the initial part of a response to the provided
* PgSQL_Query_Result object. It can optionally send the response.
*
* @param send Whether to send the response.
* @param pg_query_result The PgSQL_Query_Result object to copy the response to.
* @param result The PGresult object containing the response data.
* @return The number of bytes copied.
*/
unsigned int copy_out_response_start_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, const PGresult* result);
/**
* @brief Copies a row to a PgSQL_Query_Result.
*
* This function copies a single row of data to the provided PgSQL_Query_Result
* object. It can optionally send the row data.
*
* @param send Whether to send the row data.
* @param pg_query_result The PgSQL_Query_Result object to copy the row to.
* @param data The row data to copy.
* @param len The length of the row data.
* @return The number of bytes copied.
*/
unsigned int copy_out_row_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result, const unsigned char* data, unsigned int len);
/**
* @brief Copies the end of a response to a PgSQL_Query_Result.
*
* This function copies the final part of a response to the provided
* PgSQL_Query_Result object. It can optionally send the response.
*
* @param send Whether to send the response.
* @param pg_query_result The PgSQL_Query_Result object to copy the response to.
* @return The number of bytes copied.
*/
unsigned int copy_out_response_end_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result);
/**
* @brief Adds a "No Data" message to the query result.
*
* This method adds a "No Data" message to the query result, indicating that
* the executed statement does not return any rows.
*
* @param send A boolean flag indicating whether to send the generated packet
* immediately or just generate it. (Currently not supported).
* @param pg_query_result A pointer to the `PgSQL_Query_Result` object where the
* "No Data" message will be added.
*
* @return The number of bytes added to the query result.
*
* @note This method is typically used in response to DESCRIBE commands that do not return data rows.
*/
unsigned int copy_no_data_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result);
/**
* @brief Copies a parse completion message to PgSQL_Query_Result.
*
* This function copies a parse completion message to the provided
* PgSQL_Query_Result object.
*
* @param send A boolean flag indicating whether to send the generated packet
* immediately or just generate it. (Currently not supported).
* @param pg_query_result A pointer to the PgSQL_Query_Result object where the
* parse completion message will be copied.
*
* @return The number of bytes copied to the PgSQL_Query_Result object.
*
* @note This function adds a '1' (ParseComplete) packet to the query result.
*/
unsigned int copy_parse_completion_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result);
/**
* @brief Copies a describe completion message to PgSQL_Query_Result.
*
* This function copies a describe completion message (from a `PGresult` object) to the provided
* `PgSQL_Query_Result` object. The describe completion message provides metadata about a prepared
* statement or portal, such as the statement type and associated fields.
*
* If the statement type is 'S', a parameter description packet is generated, including the number
* and types of parameters. If there are result columns, a row description packet is generated for
* each column, including metadata such as column name, table OID, column index, type OID, length,
* type modifier, and format code. If there are no result columns, a NoData packet is generated.
*
* @param send A boolean flag indicating whether to send the generated packet immediately or just generate it. (Currently not supported).
* @param pg_query_result A pointer to the `PgSQL_Query_Result` object where the describe completion message will be copied.
* @param result A pointer to the `PGresult` object containing the describe completion data.
* @param stmt_type The type of statement being described (e.g., prepared statement 'S' or portal 'P').
*
* @return The number of bytes copied to the `PgSQL_Query_Result` object.
*
*/
unsigned int copy_describe_completion_to_PgSQL_Query_Result(bool send, PgSQL_Query_Result* pg_query_result,
const PGresult* result, uint8_t stmt_type);
private:
/**
* @brief Extracts the header information from a PostgreSQL packet.
*
* This function reads the header information from a received PostgreSQL
* packet and populates the `pgsql_hdr` structure with the packet type and
* length. It handles both the new (v3) and old (v2) packet formats.
*
* @param pkt A pointer to the beginning of the packet buffer.
* @param pkt_len The length of the packet buffer in bytes.
* @param hdr A pointer to a `pgsql_hdr` structure where the extracted header
* information will be stored.
*
* @return `true` if the header was successfully parsed, `false` otherwise.
*
* @note This function performs basic validation on the packet length and
* header fields to ensure that the packet is valid.
*/
bool get_header(unsigned char* pkt, unsigned int len, pgsql_hdr* hdr);
/**
* @brief Loads the connection parameters from a PostgreSQL startup packet.
*
* This function extracts the connection parameters (e.g., user, database,
* client encoding) from a PostgreSQL startup packet and stores them in the
* connection parameters object (`myconn->conn_params`).
*
* @param pkt A pointer to a `pgsql_hdr` structure containing the startup
* packet data.
* @param startup A boolean flag indicating whether this is a startup packet.
*
* @note This function iterates through the key-value pairs in the startup
* packet and stores them in the connection parameters object.
*/
bool load_conn_parameters(pgsql_hdr* pkt);
/**
* @brief Handles the client's first message in a SCRAM-SHA-256
* authentication exchange.
*
* This function receives the client's first message during the SCRAM-SHA-256
* authentication process. It parses the message, generates the server's
* first message, and sends it back to the client.
*
* @param scram_state A pointer to the `ScramState` structure that maintains
* the state of the SCRAM exchange.
* @param user A pointer to the `PgCredentials` structure containing the user
* credentials.
* @param data A pointer to the buffer containing the client's first message.
* @param datalen The length of the client's first message in bytes.
*
* @return `true` if the client's first message was successfully handled,
* `false` otherwise.
*
* @note This function performs the following steps:
* 1. Parses the client's first message to extract the authentication
* mechanism and client nonce.
* 2. Generates the server's first message, which includes the server
* nonce and salt.
* 3. Sends the server's first message to the client.
*/
bool scram_handle_client_first(ScramState* scram_state, PgCredentials* user, const unsigned char* data, uint32_t datalen);
/**
* @brief Handles the client's final message in a SCRAM-SHA-256
* authentication exchange.
*
* This function receives the client's final message during the SCRAM-SHA-256
* authentication process. It validates the client's proof, generates the
* server's final message, and sends it back to the client.
*
* @param scram_state A pointer to the `ScramState` structure that maintains
* the state of the SCRAM exchange.
* @param user A pointer to the `PgCredentials` structure containing the user
* credentials.
* @param data A pointer to the buffer containing the client's final message.
* @param datalen The length of the client's final message in bytes.
*
* @return `true` if the client's final message was successfully handled,
* `false` otherwise.
*
* @note This function performs the following steps:
* 1. Parses the client's final message to extract the client proof.
* 2. Verifies the client's proof against the expected value.
* 3. Generates the server's final message.
* 4. Sends the server's final message to the client.
*/
bool scram_handle_client_final(ScramState* scram_state, PgCredentials* user, const unsigned char* data, uint32_t datalen);
// parse options parameter
static std::vector<std::pair<std::string, std::string>> parse_options(const char* options);
PgSQL_Data_Stream** myds;
PgSQL_Connection_userinfo* userinfo;
PgSQL_Session* sess;
template<typename S>
friend void admin_session_handler(S* sess, void* _pa, PtrSize_t* pkt);
};
void SQLite3_to_Postgres(PtrSizeArray* psa, SQLite3_result* result, char* error, int affected_rows, const char* query_type, bool send_ready_for_query = true, char txn_state = 'I');
#endif // __POSTGRES_PROTOCOL_H