« Previous | Next »

PostgresV3 Protocol State Machine

28 Apr 2013

PostgresV3 is a pure Smalltalk implementation of the PostgreSQL v3 wire protocol (and a database access API) by Levente Uzonyi and Balazs Kosi.

In code, the protocol is implemented as a state machine, created by "PG3ServerState createStateGraph", which invokes methods such as this:

initializingBackendStateDescription

(self state: #InitializingBackend)
  on: PG3BackendKeyData
  connectionDo: #registerBackendKeyData:
  goto: #InitializingBackend;

  on: PG3ReadyForQuery
  connectionDo: #readyForQuery:
  goto: #WaitingForQuery

"on:connectionDo:goto:" looks like this:

on: aPG3MessageClass connectionDo: selector goto: aSymbol
  transitions at: aPG3MessageClass put: { (self state: aSymbol). selector }

I wanted to map out all state transitions, to better visualize the protocol flow. This is done by walking through the "transitions" inst-var of each instance of PG3ServerState:

| skip states |

skip := #('PG3NoticeResponse' 'PG3NotificationResponse' 'PG3ParameterStatus').
states := OrderedCollection new.

PG3ServerState createStateGraph valuesDo: [ :inst |
  inst transitions keysAndValuesDo: [ :k :v |
    (skip includes: k asString) ifFalse: [
      states add: (Array with: inst with: k with: v first with: v second) ]]].

states do: [ :ea |
  Transcript show: 'From ', ea first name; 
    show: ' on ', ea second asString.
  ea fourth ifNotNil: [ Transcript show: ' perform #', ea fourth ].
  Transcript show: ' goto ', ea third name; cr; flush ].

Here's the output:

From InitializingBackend on PG3ReadyForQuery perform #readyForQuery: goto WaitingForQuery
From InitializingBackend on PG3BackendKeyData perform #registerBackendKeyData: goto InitializingBackend
From GotDataRow on PG3ErrorResponse perform #handleError: goto GotErrorResponseDuringSimpleQuery
From GotDataRow on PG3CommandComplete perform #commandComplete: goto GotCommandComplete
From GotDataRow on PG3DataRow perform #dataRow: goto GotDataRow
From GotErrorResponseDuringSimpleQuery on PG3ReadyForQuery perform #readyForQuery: goto WaitingForQuery
From GotEmptyQueryResponse on PG3ReadyForQuery perform #readyForQuery: goto WaitingForQuery
From GotEmptyQueryResponse on PG3ErrorResponse perform #handleError: goto GotErrorResponseDuringSimpleQuery
From Querying on PG3ErrorResponse perform #handleError: goto GotErrorResponseDuringSimpleQuery
From Querying on PG3CommandComplete perform #commandComplete: goto GotCommandComplete
From Querying on PG3RowDescription perform #rowDescription: goto GotRowDescription
From Querying on PG3EmptyQueryResponse goto GotEmptyQueryResponse
From Authenticating on PG3AuthenticationMD5Password perform #respondToAuthenticationMD5PasswordRequest: goto AuthenticatingWithMD5
From Authenticating on PG3AuthenticationOkMessage goto InitializingBackend
From AuthenticatingWithMD5 on PG3AuthenticationOkMessage goto InitializingBackend
From GotCommandComplete on PG3ReadyForQuery perform #readyForQuery: goto WaitingForQuery
From GotCommandComplete on PG3ErrorResponse perform #handleError: goto GotErrorResponseDuringSimpleQuery
From GotCommandComplete on PG3CommandComplete perform #commandComplete: goto GotCommandComplete
From GotCommandComplete on PG3RowDescription perform #rowDescription: goto GotRowDescription
From GotRowDescription on PG3ErrorResponse perform #handleError: goto GotErrorResponseDuringSimpleQuery
From GotRowDescription on PG3CommandComplete perform #commandComplete: goto GotCommandComplete
From GotRowDescription on PG3DataRow perform #dataRow: goto GotDataRow
Tags: PostgreSQL, visualization