Skip to content

Commit 71e4a43

Browse files
authored
Parse additional PostgreSQL Wire Protocol (PGWire) messages (#2073)
1 parent cbd02e3 commit 71e4a43

4 files changed

Lines changed: 880 additions & 40 deletions

File tree

Packet++/header/PostgresLayer.h

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,193 @@ namespace pcpp
308308
std::string getParameterValue() const;
309309
};
310310

311+
/// @class PostgresQueryMessage
312+
/// Represents a PostgreSQL Query message (Frontend)
313+
class PostgresQueryMessage : public PostgresMessage
314+
{
315+
public:
316+
/// A constructor that creates the layer from an existing packet raw data
317+
/// @param[in] data A pointer to the raw data
318+
/// @param[in] dataLen Size of the data in bytes
319+
PostgresQueryMessage(const uint8_t* data, size_t dataLen)
320+
: PostgresMessage(data, dataLen, PostgresMessageType::Frontend_Query)
321+
{}
322+
323+
/// @return The SQL query string
324+
std::string getQuery() const;
325+
};
326+
327+
/// @class PostgresRowDescriptionMessage
328+
/// Represents a PostgreSQL RowDescription message (backend)
329+
class PostgresRowDescriptionMessage : public PostgresMessage
330+
{
331+
public:
332+
/// @enum PostgresColumnFormat
333+
/// Represents the format of a column in a PostgreSQL RowDescription message.
334+
/// PostgreSQL supports two formats: text (0) and binary (1).
335+
enum class PostgresColumnFormat
336+
{
337+
/// Text format (format code 0)
338+
Text = 0,
339+
/// Binary format (format code 1)
340+
Binary = 1,
341+
/// Unknown format (format code >= 2)
342+
Unknown = 2
343+
};
344+
345+
/// @struct PostgresColumnInfo
346+
/// Represents metadata for a single column in a RowDescription message
347+
struct PostgresColumnInfo
348+
{
349+
/// Column name
350+
std::string name;
351+
/// Table OID (0 if not from a table column)
352+
uint32_t tableOID;
353+
/// Column index within the table
354+
uint16_t columnIndex;
355+
/// Data type OID
356+
uint32_t typeOID;
357+
/// Type size (-1 for variable length)
358+
int16_t typeSize;
359+
/// Type modifier (-1 if none)
360+
int32_t typeModifier;
361+
/// Format
362+
PostgresColumnFormat format;
363+
};
364+
365+
/// A constructor that creates the layer from an existing packet raw data
366+
/// @param[in] data A pointer to the raw data
367+
/// @param[in] dataLen Size of the data in bytes
368+
PostgresRowDescriptionMessage(const uint8_t* data, size_t dataLen)
369+
: PostgresMessage(data, dataLen, PostgresMessageType::Backend_RowDescription)
370+
{}
371+
372+
/// @return Vector of column metadata
373+
std::vector<PostgresColumnInfo> getColumnInfos() const;
374+
};
375+
376+
/// @class PostgresDataRowMessage
377+
/// Represents a PostgreSQL DataRow message (backend)
378+
class PostgresDataRowMessage : public PostgresMessage
379+
{
380+
public:
381+
/// @class ColumnData
382+
/// Represents raw column data in a PostgreSQL DataRow message
383+
class ColumnData
384+
{
385+
public:
386+
/// A constructor that creates ColumnData from raw bytes
387+
/// @param[in] data A pointer to the raw column data
388+
/// @param[in] dataLen Size of the data in bytes
389+
ColumnData(const uint8_t* data, size_t dataLen) : m_Data(data), m_DataLen(dataLen)
390+
{}
391+
392+
/// @return The raw column data as a vector of bytes
393+
std::vector<uint8_t> getData() const
394+
{
395+
if (!m_Data)
396+
{
397+
return {};
398+
}
399+
return { m_Data, m_Data + m_DataLen };
400+
}
401+
402+
/// @return The column data as a hex string
403+
std::string toHexString() const;
404+
405+
/// @return The column data as a UTF-8 string (empty if conversion fails)
406+
std::string toString() const;
407+
408+
/// @return True if the column value is NULL
409+
bool isNull() const
410+
{
411+
return m_Data == nullptr;
412+
}
413+
414+
private:
415+
const uint8_t* m_Data;
416+
size_t m_DataLen;
417+
};
418+
419+
/// A constructor that creates the layer from an existing packet raw data
420+
/// @param[in] data A pointer to the raw data
421+
/// @param[in] dataLen Size of the data in bytes
422+
PostgresDataRowMessage(const uint8_t* data, size_t dataLen)
423+
: PostgresMessage(data, dataLen, PostgresMessageType::Backend_DataRow)
424+
{}
425+
426+
/// @return Vector of column data values
427+
std::vector<ColumnData> getDataRow() const;
428+
};
429+
430+
/// @class PostgresErrorResponseMessage
431+
/// Represents a PostgreSQL ErrorResponse or NoticeResponse message (backend)
432+
class PostgresErrorResponseMessage : public PostgresMessage
433+
{
434+
public:
435+
/// @enum ErrorField
436+
/// Represents the field types in a PostgreSQL ErrorResponse or NoticeResponse message
437+
enum class ErrorField : uint8_t
438+
{
439+
/// Severity: the field contents are ERROR, FATAL, or PANIC (localized)
440+
Severity = 'S',
441+
/// Severity: the field contents are ERROR, FATAL, PANIC or DEBUG, LOG, INFO, NOTICE, WARNING, or DEBUG
442+
/// (non-localized)
443+
SeverityNonLocalized = 'V',
444+
/// SQLSTATE code
445+
SQLState = 'C',
446+
/// Primary human-readable error message
447+
Message = 'M',
448+
/// Optional secondary error message
449+
Detail = 'D',
450+
/// Optional hint
451+
Hint = 'H',
452+
/// Decimal integer indicating an error cursor position
453+
Position = 'P',
454+
/// Internal cursor position (where error occurred)
455+
InternalPosition = 'p',
456+
/// Text of internal query
457+
InternalQuery = 'q',
458+
/// Indicating context of error
459+
Where = 'W',
460+
/// Schema name
461+
Schema = 's',
462+
/// Table name
463+
Table = 't',
464+
/// Column name
465+
Column = 'c',
466+
/// Data type name
467+
DataType = 'd',
468+
/// Constraint name
469+
Constraint = 'n',
470+
/// File name of error
471+
File = 'F',
472+
/// Line number of error
473+
Line = 'L',
474+
/// Routine name
475+
Routine = 'R',
476+
/// Terminator (always '\0')
477+
Terminator = '\0'
478+
};
479+
480+
/// A map of error field type to value
481+
using FieldMap = std::unordered_map<ErrorField, std::string>;
482+
483+
/// A constructor that creates the layer from an existing packet raw data
484+
/// @param[in] data A pointer to the raw data
485+
/// @param[in] dataLen Size of the data in bytes
486+
PostgresErrorResponseMessage(const uint8_t* data, size_t dataLen)
487+
: PostgresMessage(data, dataLen, PostgresMessageType::Backend_ErrorResponse)
488+
{}
489+
490+
/// @return The error fields as a map
491+
const FieldMap& getFields() const;
492+
493+
private:
494+
mutable FieldMap m_Fields;
495+
mutable bool m_FieldsParsed = false;
496+
};
497+
311498
/// @class PostgresLayer
312499
/// Represents a PostgreSQL protocol layer
313500
class PostgresLayer : public Layer

0 commit comments

Comments
 (0)