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  }