瀏覽代碼

Merge pull request #161 from ChannelMeter/error_response_refactor

Refactor ErrorFrame response in order to expose ErrorFrame message bodies.
Ben Hood 11 年之前
父節點
當前提交
142a1f6f39
共有 4 個文件被更改,包括 158 次插入29 次删除
  1. 2 4
      conn.go
  2. 76 0
      errors.go
  3. 27 0
      errors_test.go
  4. 53 25
      frame.go

+ 2 - 4
conn.go

@@ -399,7 +399,7 @@ func (c *Conn) executeQuery(qry *Query) *Iter {
 	case resultKeyspaceFrame:
 		return &Iter{}
 	case errorFrame:
-		if x.Code == errUnprepared && len(qry.values) > 0 {
+		if x.Code() == errUnprepared && len(qry.values) > 0 {
 			c.prepMu.Lock()
 			if val, ok := c.prep[qry.stmt]; ok && val != nil {
 				delete(c.prep, qry.stmt)
@@ -583,9 +583,7 @@ func (c *Conn) decodeFrame(f frame, trace Tracer) (rval interface{}, err error)
 	case opSupported:
 		return supportedFrame{}, nil
 	case opError:
-		code := f.readInt()
-		msg := f.readString()
-		return errorFrame{code, msg}, nil
+		return f.readError(), nil
 	default:
 		return nil, NewErrProtocol("Decoding frame: unknown op", op)
 	}

+ 76 - 0
errors.go

@@ -0,0 +1,76 @@
+package gocql
+
+const (
+	errServer        = 0x0000
+	errProtocol      = 0x000A
+	errCredentials   = 0x0100
+	errUnavailable   = 0x1000
+	errOverloaded    = 0x1001
+	errBootstrapping = 0x1002
+	errTruncate      = 0x1003
+	errWriteTimeout  = 0x1100
+	errReadTimeout   = 0x1200
+	errSyntax        = 0x2000
+	errUnauthorized  = 0x2100
+	errInvalid       = 0x2200
+	errConfig        = 0x2300
+	errAlreadyExists = 0x2400
+	errUnprepared    = 0x2500
+)
+
+type RequestError interface {
+	Code() int
+	Message() string
+	Error() string
+}
+
+type errorFrame struct {
+	code    int
+	message string
+}
+
+func (e errorFrame) Code() int {
+	return e.code
+}
+
+func (e errorFrame) Message() string {
+	return e.message
+}
+
+func (e errorFrame) Error() string {
+	return e.Message()
+}
+
+type RequestErrUnavailable struct {
+	errorFrame
+	Consistency Consistency
+	Required    int
+	Alive       int
+}
+
+type RequestErrWriteTimeout struct {
+	errorFrame
+	Consistency Consistency
+	Received    int
+	BlockFor    int
+	WriteType   string
+}
+
+type RequestErrReadTimeout struct {
+	errorFrame
+	Consistency Consistency
+	Received    int
+	BlockFor    int
+	DataPresent byte
+}
+
+type RequestErrAlreadyExists struct {
+	errorFrame
+	Keyspace string
+	Table    string
+}
+
+type RequestErrUnprepared struct {
+	errorFrame
+	StatementId []byte
+}

+ 27 - 0
errors_test.go

@@ -0,0 +1,27 @@
+package gocql
+
+import (
+	"testing"
+)
+
+func TestErrorsParse(t *testing.T) {
+	session := createSession(t)
+	defer session.Close()
+
+	if err := session.Query(`CREATE TABLE errors_parse (id int primary key)`).Exec(); err != nil {
+		t.Fatal("create:", err)
+	}
+
+	if err := session.Query(`CREATE TABLE errors_parse (id int primary key)`).Exec(); err == nil {
+		t.Fatal("Should have gotten already exists error from cassandra server.")
+	} else {
+		switch e := err.(type) {
+		case RequestErrAlreadyExists:
+			if e.Table != "errors_parse" {
+				t.Fatal("Failed to parse error response from cassandra for ErrAlreadyExists.")
+			}
+		default:
+			t.Fatal("Failed to parse error response from cassandra for ErrAlreadyExists.")
+		}
+	}
+}

+ 53 - 25
frame.go

@@ -42,22 +42,6 @@ const (
 	flagPageState   uint8 = 8
 	flagHasMore     uint8 = 2
 
-	errServer        = 0x0000
-	errProtocol      = 0x000A
-	errCredentials   = 0x0100
-	errUnavailable   = 0x1000
-	errOverloaded    = 0x1001
-	errBootstrapping = 0x1002
-	errTruncate      = 0x1003
-	errWriteTimeout  = 0x1100
-	errReadTimeout   = 0x1200
-	errSyntax        = 0x2000
-	errUnauthorized  = 0x2100
-	errInvalid       = 0x2200
-	errConfig        = 0x2300
-	errAlreadyExists = 0x2400
-	errUnprepared    = 0x2500
-
 	headerSize = 8
 
 	apacheCassandraTypePrefix = "org.apache.cassandra.db.marshal."
@@ -291,6 +275,59 @@ func (f *frame) readMetaData() ([]ColumnInfo, []byte) {
 	return columns, pageState
 }
 
+func (f *frame) readError() RequestError {
+	code := f.readInt()
+	msg := f.readString()
+	errD := errorFrame{code, msg}
+	switch code {
+	case errUnavailable:
+		cl := Consistency(f.readShort())
+		required := f.readInt()
+		alive := f.readInt()
+		return RequestErrUnavailable{errorFrame: errD,
+			Consistency: cl,
+			Required:    required,
+			Alive:       alive}
+	case errWriteTimeout:
+		cl := Consistency(f.readShort())
+		received := f.readInt()
+		blockfor := f.readInt()
+		writeType := f.readString()
+		return RequestErrWriteTimeout{errorFrame: errD,
+			Consistency: cl,
+			Received:    received,
+			BlockFor:    blockfor,
+			WriteType:   writeType,
+		}
+	case errReadTimeout:
+		cl := Consistency(f.readShort())
+		received := f.readInt()
+		blockfor := f.readInt()
+		dataPresent := (*f)[0]
+		*f = (*f)[1:]
+		return RequestErrReadTimeout{errorFrame: errD,
+			Consistency: cl,
+			Received:    received,
+			BlockFor:    blockfor,
+			DataPresent: dataPresent,
+		}
+	case errAlreadyExists:
+		ks := f.readString()
+		table := f.readString()
+		return RequestErrAlreadyExists{errorFrame: errD,
+			Keyspace: ks,
+			Table:    table,
+		}
+	case errUnprepared:
+		stmtId := f.readShortBytes()
+		return RequestErrUnprepared{errorFrame: errD,
+			StatementId: stmtId,
+		}
+	default:
+		return errD
+	}
+}
+
 func (f *frame) writeConsistency(c Consistency) {
 	f.writeShort(consistencyCodes[c])
 }
@@ -333,15 +370,6 @@ type resultPreparedFrame struct {
 	Values     []ColumnInfo
 }
 
-type errorFrame struct {
-	Code    int
-	Message string
-}
-
-func (e errorFrame) Error() string {
-	return e.Message
-}
-
 type operation interface {
 	encodeFrame(version uint8, dst frame) (frame, error)
 }