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())
	}
}
Beispiel #2
0
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
}