#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 detach() { std::pair 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 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 }; #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>& param_status = std::vector>()); 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); 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> parse_options(const char* options); PgSQL_Data_Stream** myds; PgSQL_Connection_userinfo* userinfo; PgSQL_Session* sess; template 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