pgproto3

repository·master·Indexed 41 days ago

https://github.com/jackc/pgproto3

A Go package that implements the encoder and decoder for the PostgreSQL wire protocol version 3. It serves as a low-level library for building database drivers, proxies, mock servers, and other network-level PostgreSQL tools. This version is intended for use with pgx v4 and will reach end-of-life on July 1, 2025.

Tokens
28.8K
Snippets
135
Records
160
Agent score
51%

What's inside pgproto3

  1. Overview of pgproto3

    master

    The pgproto3 package provides an encoder and decoder for the PostgreSQL wire protocol version 3. It is designed to serve as a low-level foundation for building PostgreSQL-related tools such as drivers, proxies, mock servers, and load balancers.

    Note on Versioning and Lifecycle:

    • This specific repository is intended for use with pgx v4.
    • In pgx v5, this functionality has been moved into the main pgx repository.
    • This version will reach end-of-life on July 1, 2025. After this date, only security bug fixes will be provided.
  2. Install pgfortune

    master

    To use pgfortune, you must first install the system dependencies fortune and cowsay via your Unix package manager (e.g., apt, yum, or brew). Then, install the Go package using go get.

    # Install system dependencies first (apt, yum, brew, etc.)
    # Then run:
    go get -u github.com/jackc/pgproto3/example/pgfortune
  3. Run pgfortune mock server

    master

    Run the pgfortune binary to start the mock PostgreSQL server. By default, it listens on 127.0.0.1:15432 and executes the command fortune | cowsay -f elephant for every query received.

    You can customize the behavior using the following arguments:

    • --listen: Specify the address and port to listen on.
    • --response-command: Specify a custom shell command to execute in response to queries.
    $ pgfortune
  4. Distinguish between Frontend and Backend messages

    master

    The protocol uses specific interfaces to categorize messages based on their direction in the PostgreSQL communication flow. These interfaces use no-op methods to allow for type assertions.

    • FrontendMessage: A Message that is sent from the client to the server. It includes a Frontend() no-op method.
    • BackendMessage: A Message that is sent from the server to the client. It includes a Backend() no-op method.
    • AuthenticationResponseMessage: A specific type of BackendMessage used for authentication responses. It includes an AuthenticationResponse() no-op method.
    // Example of type assertion to check message direction
    if fm, ok := msg.(pgproto3.FrontendMessage); ok {
    	// handle frontend message
    	_ = fm.Frontend()
    } else if bm, ok := msg.(pgproto3.BackendMessage); ok {
    	// handle backend message
    	_ = bm.Backend()
    }
  5. Use ErrorResponse to handle PostgreSQL error messages

    master

    The ErrorResponse struct represents the wire format for error messages sent by a PostgreSQL backend. It includes fields for error severity, codes, messages, details, and contextual information like table names, column names, or file locations.

    To work with error messages, you can:

    1. Decode raw bytes received from the backend into an ErrorResponse struct.
    2. Encode an ErrorResponse struct into bytes to be sent by a backend.
    3. Marshal/Unmarshal the struct to and from JSON for logging or API responses.

    Note that SeverityUnlocalized is only available in PostgreSQL 9.6 and greater.

    // Example of decoding an error response
    var errResp pgproto3.ErrorResponse
    err := errResp.Decode(rawBytes)
    if err != nil {
        // handle decoding error
    }
    
    // Example of encoding an error response
    newBytes, err := errResp.Encode(nil)
  6. Example: Implementing a fake PostgreSQL server with pgfortune

    master
    For a practical demonstration of how to use pgproto3 to build a PostgreSQL-compatible service, refer to the example/pgfortune directory in the repository. This example implements a playful fake PostgreSQL server.
  7. Connect to pgfortune with psql

    master

    Once pgfortune is running, you can connect to it using a standard PostgreSQL client like psql. Use the -h flag for the host and -p for the port (defaulting to 15432). Any query sent to the server will return the output of the configured response command (e.g., a fortune in a cowsay elephant).

    $ psql -h 127.0.0.1 -p 15432
    
    jack@127.0.0.1:15432 jack=# select foo;
                       fortune
    ─────────────────────────────────────────────
      _________________________________________ 
     / Ships are safe in harbor, but they were 
     \ never meant to stay there.              /
      ----------------------------------------- 
      \     /\  ___  /\                         
       \   // \/   \/ \\                        
          ((    O O    ))                       
           \\ /     \\ //                        
            \/  | |  \/                         
             |  | |  |                          
             |  | |  |                          
             |   o   |                          
             | |   | |                          
             |m|   |m|                          
    
    (1 row)
    
    Time: 28.161 ms
  8. Marshal JSON for EmptyQueryResponse

    master
    The EmptyQueryResponse type implements encoding.json.Marshaler. When marshaled to JSON, it produces an object with a single Type field set to `
  9. Use the Query type for PostgreSQL wire format messages

    master

    The Query type represents a PostgreSQL 'Query' message (type identifier 'Q'). It is used to send SQL command strings to a PostgreSQL backend.

    To use it, you can initialize a Query struct with a String field containing your SQL. You can then use Encode to prepare the message for transmission or Decode to parse an incoming message from the wire.

    // Creating and encoding a query message
    query := pgproto3.Query{String: "SELECT * FROM users;"}
    // Encode returns the message including the 'Q' identifier and length header
    encoded, err := query.Encode(nil)
  10. Decode a NoticeResponse from bytes

    master

    To parse an incoming PostgreSQL notice message, use the Decode method. The input src byte slice must contain the complete message, excluding the initial 1-byte message type identifier and the 4-byte message length.

    err := noticeResponse.Decode(src)
  11. Marshal and Unmarshal Close messages via JSON

    master

    The Close struct supports standard JSON encoding and decoding.

    When marshaling to JSON, the output includes a Type field set to "Close", along with the ObjectType (as a string) and the Name.

    JSON structure:

    {
      "Type": "Close",
      "ObjectType": "S",
      "Name": "my_statement"
    }
  12. Marshal PasswordMessage to JSON

    master

    The PasswordMessage implements encoding/json.Marshaler. When marshaled to JSON, it produces an object with a Type field set to "PasswordMessage" and the Password field.

    Example JSON output:

    {
      "Type": "PasswordMessage",
      "Password": "your_password"
    }
    {
      "Type": "PasswordMessage",
      "Password": "mysecretpassword"
    }