func parseLogRecord( dst *logRecord, data []byte, exit exitFn) { buf := bytes.NewBuffer(data) // Read the next nullable string from buf, returning a 'nil' // *string should it be null. nextNullableString := func() *string { np, err := femebe.ReadByte(buf) if err != nil { exit(err) } switch np { case 'P': s, err := femebe.ReadCString(buf) if err != nil { exit(err) } return &s case 'N': // 'N' is still followed by a NUL byte that // must be consumed. _, err := femebe.ReadCString(buf) if err != nil { exit(err) } return nil default: exit("Expected nullable string "+ "control character, got %c", np) } exit("Prior switch should always return") panic("exit should panic/return, " + "but the compiler doesn't know that") } // Read a non-nullable string from buf nextString := func() string { s, err := femebe.ReadCString(buf) if err != nil { exit(err) } return s } nextInt32 := func() int32 { i32, err := femebe.ReadInt32(buf) if err != nil { exit(err) } return i32 } nextInt64 := func() int64 { i64, err := readInt64(buf) if err != nil { exit(err) } return i64 } nextUint64 := func() uint64 { ui64, err := readUint64(buf) if err != nil { exit(err) } return ui64 } dst.LogTime = nextString() dst.UserName = nextNullableString() dst.DatabaseName = nextNullableString() dst.Pid = nextInt32() dst.ClientAddr = nextNullableString() dst.SessionId = nextString() dst.SeqNum = nextInt64() dst.PsDisplay = nextNullableString() dst.SessionStart = nextString() dst.Vxid = nextNullableString() dst.Txid = nextUint64() dst.ELevel = nextInt32() dst.SQLState = nextNullableString() dst.ErrMessage = nextNullableString() dst.ErrDetail = nextNullableString() dst.ErrHint = nextNullableString() dst.InternalQuery = nextNullableString() dst.InternalQueryPos = nextInt32() dst.ErrContext = nextNullableString() dst.UserQuery = nextNullableString() dst.UserQueryPos = nextInt32() dst.FileErrPos = nextNullableString() dst.ApplicationName = nextNullableString() if buf.Len() != 0 { exit("LogRecord message has mismatched "+ "length header and cString contents: remaining %d", buf.Len()) } }
func ReadStartupMessage(m *femebe.Message) (*Startup, error) { var err error if remainingSz := m.Size() - 4; remainingSz > 10000 { // Startup packets longer than this are considered // invalid. Copied from the PostgreSQL source code. err = ErrTooBig{fmt.Errorf( "Rejecting oversized startup packet: got %v", m.Size())} return nil, err } else if remainingSz < 4 { // We expect all initialization messages to // have at least a 4-byte header err = ErrWrongSize{ fmt.Errorf( "Expected message of at least 4 bytes; got %v", remainingSz)} return nil, err } body, err := m.Force() if err != nil { return nil, err } var b femebe.Reader b.InitReader(body) protoVer, _ := femebe.ReadInt32(&b) const SUPPORTED_PROTOVER = 0x00030000 if protoVer != SUPPORTED_PROTOVER { err = ErrStartupVersion{ fmt.Errorf("bad version: got %x expected %x", protoVer, SUPPORTED_PROTOVER)} return nil, err } params := make(map[string]string) for remaining := b.Len(); remaining > 1; { key, err := femebe.ReadCString(&b) if err != nil { return nil, err } val, err := femebe.ReadCString(&b) if err != nil { return nil, err } remaining -= len(key) + len(val) + 2 /* null bytes */ params[key] = val } // Fidelity check on the startup packet, whereby the last byte // must be a NUL. if d, _ := femebe.ReadByte(&b); d != '\000' { return nil, ErrStartupFmt{ errors.New("malformed startup packet")} } return &Startup{params}, nil }