github.com/datastax/go-cassandra-native-protocol@v0.0.0-20220706104457-5e8aad05cf90/message/result.go (about) 1 // Copyright 2020 DataStax 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package message 16 17 import ( 18 "errors" 19 "fmt" 20 "io" 21 22 "github.com/datastax/go-cassandra-native-protocol/primitive" 23 ) 24 25 type Result interface { 26 Message 27 GetResultType() primitive.ResultType 28 } 29 30 // VOID 31 32 // VoidResult is a response message sent when the executed query returns nothing. 33 // +k8s:deepcopy-gen=true 34 // +k8s:deepcopy-gen:interfaces=github.com/datastax/go-cassandra-native-protocol/message.Message 35 type VoidResult struct{} 36 37 func (m *VoidResult) IsResponse() bool { 38 return true 39 } 40 41 func (m *VoidResult) GetOpCode() primitive.OpCode { 42 return primitive.OpCodeResult 43 } 44 45 func (m *VoidResult) GetResultType() primitive.ResultType { 46 return primitive.ResultTypeVoid 47 } 48 49 func (m *VoidResult) String() string { 50 return "RESULT VOID" 51 } 52 53 // SET KEYSPACE 54 55 // SetKeyspaceResult is a response message sent when the executed query is a USE statement that alters the session's 56 // current keyspace. 57 // +k8s:deepcopy-gen=true 58 // +k8s:deepcopy-gen:interfaces=github.com/datastax/go-cassandra-native-protocol/message.Message 59 type SetKeyspaceResult struct { 60 Keyspace string 61 } 62 63 func (m *SetKeyspaceResult) IsResponse() bool { 64 return true 65 } 66 67 func (m *SetKeyspaceResult) GetOpCode() primitive.OpCode { 68 return primitive.OpCodeResult 69 } 70 71 func (m *SetKeyspaceResult) GetResultType() primitive.ResultType { 72 return primitive.ResultTypeSetKeyspace 73 } 74 75 func (m *SetKeyspaceResult) String() string { 76 return "RESULT SET KEYSPACE " + m.Keyspace 77 } 78 79 // SCHEMA CHANGE 80 81 // SchemaChangeResult is a response message sent when the executed query is a schema-altering query (DDL). 82 // Note: this struct is identical to SchemaChangeEvent. 83 // +k8s:deepcopy-gen=true 84 // +k8s:deepcopy-gen:interfaces=github.com/datastax/go-cassandra-native-protocol/message.Message 85 type SchemaChangeResult struct { 86 // The schema change type. 87 ChangeType primitive.SchemaChangeType 88 // The schema change target, that is, the kind of schema object affected by the change. This field has been 89 // introduced in protocol version 3. 90 Target primitive.SchemaChangeTarget 91 // The name of the keyspace affected by the change. 92 Keyspace string 93 // If the schema object affected by the change is not the keyspace itself, this field contains its name. Otherwise, 94 // this field is irrelevant. 95 Object string 96 // If the schema object affected by the change is a function or an aggregate, this field contains its arguments. 97 // Otherwise, this field is irrelevant. Valid from protocol version 4 onwards. 98 Arguments []string 99 } 100 101 func (m *SchemaChangeResult) IsResponse() bool { 102 return true 103 } 104 105 func (m *SchemaChangeResult) GetOpCode() primitive.OpCode { 106 return primitive.OpCodeResult 107 } 108 109 func (m *SchemaChangeResult) GetResultType() primitive.ResultType { 110 return primitive.ResultTypeSchemaChange 111 } 112 113 func (m *SchemaChangeResult) String() string { 114 return fmt.Sprintf("RESULT SCHEMA CHANGE (type=%v target=%v keyspace=%v object=%v args=%v)", 115 m.ChangeType, 116 m.Target, 117 m.Keyspace, 118 m.Object, 119 m.Arguments) 120 } 121 122 // PREPARED 123 124 // PreparedResult is a response message sent in reply to a Prepare request. 125 // +k8s:deepcopy-gen=true 126 // +k8s:deepcopy-gen:interfaces=github.com/datastax/go-cassandra-native-protocol/message.Message 127 type PreparedResult struct { 128 PreparedQueryId []byte 129 // The result set metadata id; valid for protocol version 5, if the prepared statement is a SELECT. Also valid in DSE v2. See Execute. 130 ResultMetadataId []byte 131 // Reflects the prepared statement's bound variables, if any, or empty (but not nil) if there are no bound variables. 132 VariablesMetadata *VariablesMetadata 133 // When the prepared statement is a SELECT, reflects the result set columns; empty (but not nil) otherwise. 134 ResultMetadata *RowsMetadata 135 } 136 137 func (m *PreparedResult) IsResponse() bool { 138 return true 139 } 140 141 func (m *PreparedResult) GetOpCode() primitive.OpCode { 142 return primitive.OpCodeResult 143 } 144 145 func (m *PreparedResult) GetResultType() primitive.ResultType { 146 return primitive.ResultTypePrepared 147 } 148 149 func (m *PreparedResult) String() string { 150 return fmt.Sprintf("RESULT PREPARED (%v)", m.PreparedQueryId) 151 } 152 153 // ROWS 154 155 type Column = []byte 156 157 type Row = []Column 158 159 type RowSet = []Row 160 161 // RowsResult is a response message sent when the executed query is a SELECT statement. 162 // +k8s:deepcopy-gen=true 163 // +k8s:deepcopy-gen:interfaces=github.com/datastax/go-cassandra-native-protocol/message.Message 164 type RowsResult struct { 165 Metadata *RowsMetadata 166 Data RowSet 167 } 168 169 func (m *RowsResult) IsResponse() bool { 170 return true 171 } 172 173 func (m *RowsResult) GetOpCode() primitive.OpCode { 174 return primitive.OpCodeResult 175 } 176 177 func (m *RowsResult) GetResultType() primitive.ResultType { 178 return primitive.ResultTypeRows 179 } 180 181 func (m *RowsResult) String() string { 182 return fmt.Sprintf("RESULT ROWS (%v rows x %v cols)", len(m.Data), m.Metadata.ColumnCount) 183 } 184 185 // CODEC 186 187 type resultCodec struct{} 188 189 func (c *resultCodec) Encode(msg Message, dest io.Writer, version primitive.ProtocolVersion) (err error) { 190 result, ok := msg.(Result) 191 if !ok { 192 return fmt.Errorf("expected message.Result, got %T", msg) 193 } 194 if err = primitive.CheckValidResultType(result.GetResultType()); err != nil { 195 return err 196 } else if err = primitive.WriteInt(int32(result.GetResultType()), dest); err != nil { 197 return fmt.Errorf("cannot write RESULT type: %w", err) 198 } 199 switch result.GetResultType() { 200 case primitive.ResultTypeVoid: 201 return nil 202 case primitive.ResultTypeSetKeyspace: 203 sk, ok := result.(*SetKeyspaceResult) 204 if !ok { 205 return fmt.Errorf("expected *message.SetKeyspaceResult, got %T", result) 206 } 207 if sk.Keyspace == "" { 208 return errors.New("RESULT SetKeyspace: cannot write empty keyspace") 209 } else if err = primitive.WriteString(sk.Keyspace, dest); err != nil { 210 return fmt.Errorf("cannot write RESULT SET KEYSPACE keyspace: %w", err) 211 } 212 case primitive.ResultTypeSchemaChange: 213 sce, ok := msg.(*SchemaChangeResult) 214 if !ok { 215 return fmt.Errorf("expected *message.SchemaChangeResult, got %T", msg) 216 } 217 if err = primitive.CheckValidSchemaChangeType(sce.ChangeType); err != nil { 218 return err 219 } else if err = primitive.WriteString(string(sce.ChangeType), dest); err != nil { 220 return fmt.Errorf("cannot write SchemaChangeResult.ChangeType: %w", err) 221 } 222 if version >= primitive.ProtocolVersion3 { 223 if err = primitive.CheckValidSchemaChangeTarget(sce.Target, version); err != nil { 224 return err 225 } else if err = primitive.WriteString(string(sce.Target), dest); err != nil { 226 return fmt.Errorf("cannot write SchemaChangeResult.Target: %w", err) 227 } 228 if sce.Keyspace == "" { 229 return errors.New("RESULT SchemaChange: cannot write empty keyspace") 230 } else if err = primitive.WriteString(sce.Keyspace, dest); err != nil { 231 return fmt.Errorf("cannot write SchemaChangeResult.Keyspace: %w", err) 232 } 233 switch sce.Target { 234 case primitive.SchemaChangeTargetKeyspace: 235 case primitive.SchemaChangeTargetTable: 236 fallthrough 237 case primitive.SchemaChangeTargetType: 238 if sce.Object == "" { 239 return errors.New("RESULT SchemaChange: cannot write empty object") 240 } else if err = primitive.WriteString(sce.Object, dest); err != nil { 241 return fmt.Errorf("cannot write SchemaChangeResult.Object: %w", err) 242 } 243 case primitive.SchemaChangeTargetAggregate: 244 fallthrough 245 case primitive.SchemaChangeTargetFunction: 246 if sce.Object == "" { 247 return errors.New("RESULT SchemaChange: cannot write empty object") 248 } else if err = primitive.WriteString(sce.Object, dest); err != nil { 249 return fmt.Errorf("cannot write SchemaChangeResult.Object: %w", err) 250 } 251 if err = primitive.WriteStringList(sce.Arguments, dest); err != nil { 252 return fmt.Errorf("cannot write SchemaChangeResult.Arguments: %w", err) 253 } 254 } 255 } else { 256 if err = primitive.CheckValidSchemaChangeTarget(sce.Target, version); err != nil { 257 return err 258 } 259 if sce.Keyspace == "" { 260 return errors.New("RESULT SchemaChange: cannot write empty keyspace") 261 } else if err = primitive.WriteString(sce.Keyspace, dest); err != nil { 262 return fmt.Errorf("cannot write SchemaChangeEvent.Keyspace: %w", err) 263 } 264 switch sce.Target { 265 case primitive.SchemaChangeTargetKeyspace: 266 if sce.Object != "" { 267 return errors.New("RESULT SchemaChange: table must be empty for keyspace targets") 268 } else if err = primitive.WriteString("", dest); err != nil { 269 return fmt.Errorf("cannot write SchemaChangeEvent.Object: %w", err) 270 } 271 case primitive.SchemaChangeTargetTable: 272 if sce.Object == "" { 273 return errors.New("RESULT SchemaChange: cannot write empty table") 274 } else if err = primitive.WriteString(sce.Object, dest); err != nil { 275 return fmt.Errorf("cannot write SchemaChangeEvent.Object: %w", err) 276 } 277 } 278 } 279 case primitive.ResultTypePrepared: 280 p, ok := msg.(*PreparedResult) 281 if !ok { 282 return fmt.Errorf("expected *message.PreparedResult, got %T", msg) 283 } 284 if len(p.PreparedQueryId) == 0 { 285 return errors.New("cannot write empty RESULT Prepared query id") 286 } else if err = primitive.WriteShortBytes(p.PreparedQueryId, dest); err != nil { 287 return fmt.Errorf("cannot write RESULT Prepared prepared query id: %w", err) 288 } 289 if version.SupportsResultMetadataId() { 290 if len(p.ResultMetadataId) == 0 { 291 return errors.New("cannot write empty RESULT Prepared result metadata id") 292 } else if err = primitive.WriteShortBytes(p.ResultMetadataId, dest); err != nil { 293 return fmt.Errorf("cannot write RESULT Prepared result metadata id: %w", err) 294 } 295 } 296 if err = encodeVariablesMetadata(p.VariablesMetadata, dest, version); err != nil { 297 return fmt.Errorf("cannot write RESULT Prepared variables metadata: %w", err) 298 } 299 if err = encodeRowsMetadata(p.ResultMetadata, dest, version); err != nil { 300 return fmt.Errorf("cannot write RESULT Prepared result metadata: %w", err) 301 } 302 case primitive.ResultTypeRows: 303 rows, ok := msg.(*RowsResult) 304 if !ok { 305 return fmt.Errorf("expected *message.RowsResult, got %T", msg) 306 } 307 if err = encodeRowsMetadata(rows.Metadata, dest, version); err != nil { 308 return fmt.Errorf("cannot write RESULT Rows metadata: %w", err) 309 } 310 if err = primitive.WriteInt(int32(len(rows.Data)), dest); err != nil { 311 return fmt.Errorf("cannot write RESULT Rows data length: %w", err) 312 } 313 for i, row := range rows.Data { 314 for j, col := range row { 315 if err = primitive.WriteBytes(col, dest); err != nil { 316 return fmt.Errorf("cannot write RESULT Rows data row %d col %d: %w", i, j, err) 317 } 318 } 319 } 320 default: 321 return fmt.Errorf("unknown RESULT type: %v", result.GetResultType()) 322 } 323 return nil 324 } 325 326 func (c *resultCodec) EncodedLength(msg Message, version primitive.ProtocolVersion) (length int, err error) { 327 result, ok := msg.(Result) 328 if !ok { 329 return -1, fmt.Errorf("expected interface Result, got %T", msg) 330 } 331 length += primitive.LengthOfInt 332 switch result.GetResultType() { 333 case primitive.ResultTypeVoid: 334 return length, nil 335 case primitive.ResultTypeSetKeyspace: 336 sk, ok := result.(*SetKeyspaceResult) 337 if !ok { 338 return -1, fmt.Errorf("expected *message.SetKeyspaceResult, got %T", result) 339 } 340 length += primitive.LengthOfString(sk.Keyspace) 341 case primitive.ResultTypeSchemaChange: 342 sc, ok := msg.(*SchemaChangeResult) 343 if !ok { 344 return -1, fmt.Errorf("expected *message.SchemaChangeResult, got %T", msg) 345 } 346 length += primitive.LengthOfString(string(sc.ChangeType)) 347 if err = primitive.CheckValidSchemaChangeTarget(sc.Target, version); err != nil { 348 return -1, err 349 } 350 if version >= primitive.ProtocolVersion3 { 351 length += primitive.LengthOfString(string(sc.Target)) 352 length += primitive.LengthOfString(sc.Keyspace) 353 switch sc.Target { 354 case primitive.SchemaChangeTargetKeyspace: 355 case primitive.SchemaChangeTargetTable: 356 fallthrough 357 case primitive.SchemaChangeTargetType: 358 length += primitive.LengthOfString(sc.Object) 359 case primitive.SchemaChangeTargetAggregate: 360 fallthrough 361 case primitive.SchemaChangeTargetFunction: 362 length += primitive.LengthOfString(sc.Object) 363 length += primitive.LengthOfStringList(sc.Arguments) 364 } 365 } else { 366 length += primitive.LengthOfString(sc.Keyspace) 367 length += primitive.LengthOfString(sc.Object) 368 } 369 case primitive.ResultTypePrepared: 370 p, ok := msg.(*PreparedResult) 371 if !ok { 372 return -1, fmt.Errorf("expected *message.PreparedResult, got %T", msg) 373 } 374 length += primitive.LengthOfShortBytes(p.PreparedQueryId) 375 if version.SupportsResultMetadataId() { 376 length += primitive.LengthOfShortBytes(p.ResultMetadataId) 377 } 378 if lengthOfMetadata, err := lengthOfVariablesMetadata(p.VariablesMetadata, version); err != nil { 379 return -1, fmt.Errorf("cannot compute length of RESULT Prepared variables metadata: %w", err) 380 } else { 381 length += lengthOfMetadata 382 } 383 if lengthOfMetadata, err := lengthOfRowsMetadata(p.ResultMetadata, version); err != nil { 384 return -1, fmt.Errorf("cannot compute length of RESULT Prepared result metadata: %w", err) 385 } else { 386 length += lengthOfMetadata 387 } 388 case primitive.ResultTypeRows: 389 rows, ok := msg.(*RowsResult) 390 if !ok { 391 return -1, fmt.Errorf("expected *message.RowsResult, got %T", msg) 392 } 393 if rows.Metadata == nil { 394 return -1, errors.New("cannot compute length of nil RESULT Rows metadata") 395 } else { 396 var lengthOfMetadata int 397 if lengthOfMetadata, err = lengthOfRowsMetadata(rows.Metadata, version); err != nil { 398 return -1, fmt.Errorf("cannot compute length of RESULT Rows metadata: %w", err) 399 } 400 length += lengthOfMetadata 401 } 402 length += primitive.LengthOfInt // number of rows 403 for _, row := range rows.Data { 404 for _, col := range row { 405 length += primitive.LengthOfBytes(col) 406 } 407 } 408 default: 409 return -1, fmt.Errorf("unknown RESULT type: %v", result.GetResultType()) 410 } 411 return length, nil 412 } 413 414 func (c *resultCodec) Decode(source io.Reader, version primitive.ProtocolVersion) (msg Message, err error) { 415 var resultType int32 416 if resultType, err = primitive.ReadInt(source); err != nil { 417 return nil, fmt.Errorf("cannot read RESULT type: %w", err) 418 } 419 switch primitive.ResultType(resultType) { 420 case primitive.ResultTypeVoid: 421 return &VoidResult{}, nil 422 case primitive.ResultTypeSetKeyspace: 423 setKeyspace := &SetKeyspaceResult{} 424 if setKeyspace.Keyspace, err = primitive.ReadString(source); err != nil { 425 return nil, fmt.Errorf("cannot read RESULT SetKeyspaceResult.Keyspace: %w", err) 426 } 427 return setKeyspace, nil 428 case primitive.ResultTypeSchemaChange: 429 sc := &SchemaChangeResult{} 430 var changeType string 431 if changeType, err = primitive.ReadString(source); err != nil { 432 return nil, fmt.Errorf("cannot read SchemaChangeResult.ChangeType: %w", err) 433 } 434 sc.ChangeType = primitive.SchemaChangeType(changeType) 435 if version >= primitive.ProtocolVersion3 { 436 var target string 437 if target, err = primitive.ReadString(source); err != nil { 438 return nil, fmt.Errorf("cannot read SchemaChangeResult.Target: %w", err) 439 } 440 sc.Target = primitive.SchemaChangeTarget(target) 441 if err = primitive.CheckValidSchemaChangeTarget(sc.Target, version); err != nil { 442 return nil, err 443 } 444 if sc.Keyspace, err = primitive.ReadString(source); err != nil { 445 return nil, fmt.Errorf("cannot read SchemaChangeResult.Keyspace: %w", err) 446 } 447 switch sc.Target { 448 case primitive.SchemaChangeTargetKeyspace: 449 case primitive.SchemaChangeTargetTable: 450 fallthrough 451 case primitive.SchemaChangeTargetType: 452 if sc.Object, err = primitive.ReadString(source); err != nil { 453 return nil, fmt.Errorf("cannot read SchemaChangeResult.Object: %w", err) 454 } 455 case primitive.SchemaChangeTargetAggregate: 456 fallthrough 457 case primitive.SchemaChangeTargetFunction: 458 if sc.Object, err = primitive.ReadString(source); err != nil { 459 return nil, fmt.Errorf("cannot read SchemaChangeResult.Object: %w", err) 460 } 461 if sc.Arguments, err = primitive.ReadStringList(source); err != nil { 462 return nil, fmt.Errorf("cannot read SchemaChangeResult.Arguments: %w", err) 463 } 464 default: 465 return nil, fmt.Errorf("unknown schema change target: %v", sc.Target) 466 } 467 } else { 468 if sc.Keyspace, err = primitive.ReadString(source); err != nil { 469 return nil, fmt.Errorf("cannot read SchemaChangeEvent.Keyspace: %w", err) 470 } 471 if sc.Object, err = primitive.ReadString(source); err != nil { 472 return nil, fmt.Errorf("cannot read SchemaChangeEvent.Object: %w", err) 473 } 474 if sc.Object == "" { 475 sc.Target = primitive.SchemaChangeTargetKeyspace 476 } else { 477 sc.Target = primitive.SchemaChangeTargetTable 478 } 479 } 480 return sc, nil 481 case primitive.ResultTypePrepared: 482 p := &PreparedResult{} 483 if p.PreparedQueryId, err = primitive.ReadShortBytes(source); err != nil { 484 return nil, fmt.Errorf("cannot read RESULT Prepared prepared query id: %w", err) 485 } 486 if version.SupportsResultMetadataId() { 487 if p.ResultMetadataId, err = primitive.ReadShortBytes(source); err != nil { 488 return nil, fmt.Errorf("cannot read RESULT Prepared result metadata id: %w", err) 489 } 490 } 491 if p.VariablesMetadata, err = decodeVariablesMetadata(source, version); err != nil { 492 return nil, fmt.Errorf("cannot read RESULT Prepared variables metadata: %w", err) 493 } 494 if p.ResultMetadata, err = decodeRowsMetadata(source, version); err != nil { 495 return nil, fmt.Errorf("cannot read RESULT Prepared result metadata: %w", err) 496 } 497 return p, nil 498 case primitive.ResultTypeRows: 499 rows := &RowsResult{} 500 if rows.Metadata, err = decodeRowsMetadata(source, version); err != nil { 501 return nil, fmt.Errorf("cannot read RESULT Rows metadata: %w", err) 502 } 503 var rowsCount int32 504 if rowsCount, err = primitive.ReadInt(source); err != nil { 505 return nil, fmt.Errorf("cannot read RESULT Rows data length: %w", err) 506 } 507 rows.Data = make(RowSet, rowsCount) 508 for i := 0; i < int(rowsCount); i++ { 509 rows.Data[i] = make(Row, rows.Metadata.ColumnCount) 510 for j := 0; j < int(rows.Metadata.ColumnCount); j++ { 511 if rows.Data[i][j], err = primitive.ReadBytes(source); err != nil { 512 return nil, fmt.Errorf("cannot read RESULT Rows data row %d col %d: %w", i, j, err) 513 } 514 } 515 } 516 return rows, nil 517 default: 518 return nil, fmt.Errorf("unknown RESULT type: %v", resultType) 519 } 520 } 521 522 func (c *resultCodec) GetOpCode() primitive.OpCode { 523 return primitive.OpCodeResult 524 }