github.com/datastax/go-cassandra-native-protocol@v0.0.0-20220706104457-5e8aad05cf90/message/query_options.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 // QueryOptions is the set of options common to Query and Execute messages. 26 // +k8s:deepcopy-gen=true 27 type QueryOptions struct { 28 // The consistency level to use when executing the query. This field is mandatory; its default is ANY, as the 29 // zero value of primitive.ConsistencyLevel is primitive.ConsistencyLevelAny. Note that ANY is NOT suitable for 30 // read queries. 31 Consistency primitive.ConsistencyLevel 32 33 // The positional values of the associated query. Positional values are designated in query strings with a 34 // question mark bind marker ('?'). Positional values are valid for all protocol versions. 35 // It is illegal to use both positional and named values at the same time. If this happens, positional values will 36 // be used and named values will be silently ignored. 37 PositionalValues []*primitive.Value 38 39 // The named values of the associated query. Named values are designated in query strings with a 40 // a bind marker starting with a colon (e.g. ':var1'). Named values can only be used with protocol version 3 or 41 // higher. 42 // It is illegal to use both positional and named values at the same time. If this happens, positional values will 43 // be used and named values will be silently ignored. 44 NamedValues map[string]*primitive.Value 45 46 // If true, asks the server to skip result set metadata when sending results. In such case, the RowsResult message 47 // returned as a response to the query (if any) will contain no result set metadata, and will have the NO_METADATA 48 // flag set; in other words, RowsResult.Metadata will be present but RowsResult.Metadata.Columns will be nil. 49 SkipMetadata bool 50 51 // The desired page size. A value of zero or negative is usually interpreted as "no pagination" and can result 52 // in the entire result set being returned in one single page. 53 PageSize int32 54 55 // Whether the page size is expressed in number of rows or number of bytes. Valid for DSE protocol versions only. 56 PageSizeInBytes bool 57 58 // PagingState is a [bytes] value coming from a previously-received RowsResult.Metadata object. If provided, the 59 // query will be executed but starting from a given paging state. 60 PagingState []byte 61 62 // The (optional) serial consistency level to use when executing the query. Valid for protocol versions 2 and 63 // higher. 64 SerialConsistency *primitive.ConsistencyLevel 65 66 // The default timestamp for the query in microseconds (negative values are discouraged but supported for 67 // backward compatibility reasons except for the smallest negative value (-2^63) that is forbidden). If provided, 68 // this will replace the server-side assigned timestamp as default timestamp. Note that a timestamp in the query 69 // itself (that is, if the query has a USING TIMESTAMP clause) will still override this timestamp. 70 // Default timestamps are valid for protocol versions 3 and higher. 71 DefaultTimestamp *int64 72 73 // Valid for protocol version 5 and DSE protocol version 2 only. 74 Keyspace string 75 76 // Introduced in Protocol Version 5, not present in DSE protocol versions. 77 NowInSeconds *int32 78 79 // Valid only for DSE protocol versions. 80 ContinuousPagingOptions *ContinuousPagingOptions 81 } 82 83 func (o *QueryOptions) String() string { 84 return fmt.Sprintf( 85 "[cl=%v, positionalVals=%v, namedVals=%v, skip=%v, psize=%v, state=%v, serialCl=%v]", 86 o.Consistency, 87 o.PositionalValues, 88 o.NamedValues, 89 o.SkipMetadata, 90 o.PageSize, 91 o.PagingState, 92 o.SerialConsistency) 93 } 94 95 func (o *QueryOptions) Flags() primitive.QueryFlag { 96 var flags primitive.QueryFlag 97 // prefer positional values, if provided, and ignore named ones. 98 if o.PositionalValues != nil { 99 flags = flags.Add(primitive.QueryFlagValues) 100 } else if o.NamedValues != nil { 101 flags = flags.Add(primitive.QueryFlagValues) 102 flags = flags.Add(primitive.QueryFlagValueNames) 103 } 104 if o.SkipMetadata { 105 flags = flags.Add(primitive.QueryFlagSkipMetadata) 106 } 107 if o.PageSize > 0 { 108 flags = flags.Add(primitive.QueryFlagPageSize) 109 if o.PageSizeInBytes { 110 flags = flags.Add(primitive.QueryFlagDsePageSizeBytes) 111 } 112 } 113 if o.PagingState != nil { 114 flags = flags.Add(primitive.QueryFlagPagingState) 115 } 116 if o.SerialConsistency != nil { 117 flags = flags.Add(primitive.QueryFlagSerialConsistency) 118 } 119 if o.DefaultTimestamp != nil { 120 flags = flags.Add(primitive.QueryFlagDefaultTimestamp) 121 } 122 if o.Keyspace != "" { 123 flags = flags.Add(primitive.QueryFlagWithKeyspace) 124 } 125 if o.NowInSeconds != nil { 126 flags = flags.Add(primitive.QueryFlagNowInSeconds) 127 } 128 if o.ContinuousPagingOptions != nil { 129 flags = flags.Add(primitive.QueryFlagDseWithContinuousPagingOptions) 130 } 131 return flags 132 } 133 134 func EncodeQueryOptions(options *QueryOptions, dest io.Writer, version primitive.ProtocolVersion) (err error) { 135 if options == nil { 136 options = &QueryOptions{} // use defaults if nil provided 137 } 138 if err := primitive.CheckValidConsistencyLevel(options.Consistency); err != nil { 139 return err 140 } else if err = primitive.WriteShort(uint16(options.Consistency), dest); err != nil { 141 return fmt.Errorf("cannot write consistency: %w", err) 142 } 143 flags := options.Flags() 144 if version.Uses4BytesQueryFlags() { 145 if err = primitive.WriteInt(int32(flags), dest); err != nil { 146 return fmt.Errorf("cannot write flags: %w", err) 147 } 148 } else { 149 if err = primitive.WriteByte(uint8(flags), dest); err != nil { 150 return fmt.Errorf("cannot write flags: %w", err) 151 } 152 } 153 if flags.Contains(primitive.QueryFlagValues) { 154 if flags.Contains(primitive.QueryFlagValueNames) { 155 if err = primitive.WriteNamedValues(options.NamedValues, dest, version); err != nil { 156 return fmt.Errorf("cannot write named [value]s: %w", err) 157 } 158 } else { 159 if err = primitive.WritePositionalValues(options.PositionalValues, dest, version); err != nil { 160 return fmt.Errorf("cannot write positional [value]s: %w", err) 161 } 162 } 163 } 164 if flags.Contains(primitive.QueryFlagPageSize) { 165 if err = primitive.WriteInt(options.PageSize, dest); err != nil { 166 return fmt.Errorf("cannot write page size: %w", err) 167 } 168 } 169 if flags.Contains(primitive.QueryFlagPagingState) { 170 if err = primitive.WriteBytes(options.PagingState, dest); err != nil { 171 return fmt.Errorf("cannot write paging state: %w", err) 172 } 173 } 174 if flags.Contains(primitive.QueryFlagSerialConsistency) { 175 if err := primitive.CheckSerialConsistencyLevel(*options.SerialConsistency); err != nil { 176 return err 177 } else if err = primitive.WriteShort(uint16(*options.SerialConsistency), dest); err != nil { 178 return fmt.Errorf("cannot write serial consistency: %w", err) 179 } 180 } 181 if flags.Contains(primitive.QueryFlagDefaultTimestamp) { 182 if err = primitive.WriteLong(*options.DefaultTimestamp, dest); err != nil { 183 return fmt.Errorf("cannot write default timestamp: %w", err) 184 } 185 } 186 if flags.Contains(primitive.QueryFlagWithKeyspace) { 187 if options.Keyspace == "" { 188 return errors.New("cannot write empty keyspace") 189 } else if err = primitive.WriteString(options.Keyspace, dest); err != nil { 190 return fmt.Errorf("cannot write keyspace: %w", err) 191 } 192 } 193 if flags.Contains(primitive.QueryFlagNowInSeconds) { 194 if err = primitive.WriteInt(*options.NowInSeconds, dest); err != nil { 195 return fmt.Errorf("cannot write now-in-seconds: %w", err) 196 } 197 } 198 if flags.Contains(primitive.QueryFlagDseWithContinuousPagingOptions) { 199 if err = EncodeContinuousPagingOptions(options.ContinuousPagingOptions, dest, version); err != nil { 200 return fmt.Errorf("cannot encode continuous paging options: %w", err) 201 } 202 } 203 return nil 204 } 205 206 func LengthOfQueryOptions(options *QueryOptions, version primitive.ProtocolVersion) (length int, err error) { 207 if options == nil { 208 options = &QueryOptions{} // use defaults if nil provided 209 } 210 length += primitive.LengthOfShort // consistency level 211 if version.Uses4BytesQueryFlags() { 212 length += primitive.LengthOfInt 213 } else { 214 length += primitive.LengthOfByte 215 } 216 var s int 217 flags := options.Flags() 218 if flags.Contains(primitive.QueryFlagValues) { 219 if flags.Contains(primitive.QueryFlagValueNames) { 220 s, err = primitive.LengthOfNamedValues(options.NamedValues) 221 } else { 222 s, err = primitive.LengthOfPositionalValues(options.PositionalValues) 223 } 224 } 225 if err != nil { 226 return -1, fmt.Errorf("cannot compute length of query options values: %w", err) 227 } 228 length += s 229 if flags.Contains(primitive.QueryFlagPageSize) { 230 length += primitive.LengthOfInt 231 } 232 if flags.Contains(primitive.QueryFlagPagingState) { 233 length += primitive.LengthOfBytes(options.PagingState) 234 } 235 if flags.Contains(primitive.QueryFlagSerialConsistency) { 236 length += primitive.LengthOfShort 237 } 238 if flags.Contains(primitive.QueryFlagDefaultTimestamp) { 239 length += primitive.LengthOfLong 240 } 241 if flags.Contains(primitive.QueryFlagWithKeyspace) { 242 length += primitive.LengthOfString(options.Keyspace) 243 } 244 if flags.Contains(primitive.QueryFlagNowInSeconds) { 245 length += primitive.LengthOfInt 246 } 247 if flags.Contains(primitive.QueryFlagDseWithContinuousPagingOptions) { 248 if lengthOfContinuousPagingOptions, err := LengthOfContinuousPagingOptions(options.ContinuousPagingOptions, version); err != nil { 249 return -1, fmt.Errorf("cannot compute length of continuous paging options: %w", err) 250 } else { 251 length += lengthOfContinuousPagingOptions 252 } 253 } 254 return 255 } 256 257 func DecodeQueryOptions(source io.Reader, version primitive.ProtocolVersion) (options *QueryOptions, err error) { 258 options = &QueryOptions{} 259 var consistency uint16 260 if consistency, err = primitive.ReadShort(source); err != nil { 261 return nil, fmt.Errorf("cannot read consistency: %w", err) 262 } 263 options.Consistency = primitive.ConsistencyLevel(consistency) 264 if err = primitive.CheckValidConsistencyLevel(options.Consistency); err != nil { 265 return nil, err 266 } 267 var flags primitive.QueryFlag 268 if version.Uses4BytesQueryFlags() { 269 var f int32 270 f, err = primitive.ReadInt(source) 271 flags = primitive.QueryFlag(f) 272 } else { 273 var f uint8 274 f, err = primitive.ReadByte(source) 275 flags = primitive.QueryFlag(f) 276 } 277 if err != nil { 278 return nil, fmt.Errorf("cannot read flags: %w", err) 279 } 280 if flags.Contains(primitive.QueryFlagValues) { 281 if flags.Contains(primitive.QueryFlagValueNames) { 282 options.NamedValues, err = primitive.ReadNamedValues(source, version) 283 } else { 284 options.PositionalValues, err = primitive.ReadPositionalValues(source, version) 285 } 286 } 287 if err != nil { 288 return nil, fmt.Errorf("cannot read [value]s: %w", err) 289 } 290 options.SkipMetadata = flags.Contains(primitive.QueryFlagSkipMetadata) 291 if flags.Contains(primitive.QueryFlagPageSize) { 292 if options.PageSize, err = primitive.ReadInt(source); err != nil { 293 return nil, fmt.Errorf("cannot read page size: %w", err) 294 } 295 if flags.Contains(primitive.QueryFlagDsePageSizeBytes) { 296 options.PageSizeInBytes = true 297 } 298 } 299 if flags.Contains(primitive.QueryFlagPagingState) { 300 if options.PagingState, err = primitive.ReadBytes(source); err != nil { 301 return nil, fmt.Errorf("cannot read paging state: %w", err) 302 } 303 } 304 if flags.Contains(primitive.QueryFlagSerialConsistency) { 305 var optionsSerialConsistencyUint uint16 306 if optionsSerialConsistencyUint, err = primitive.ReadShort(source); err != nil { 307 return nil, fmt.Errorf("cannot read serial consistency: %w", err) 308 } 309 optionsSerialConsistency := primitive.ConsistencyLevel(optionsSerialConsistencyUint) 310 if err = primitive.CheckValidConsistencyLevel(optionsSerialConsistency); err != nil { 311 return nil, err 312 } 313 options.SerialConsistency = &optionsSerialConsistency 314 } 315 if flags.Contains(primitive.QueryFlagDefaultTimestamp) { 316 var optionsDefaultTimestamp int64 317 if optionsDefaultTimestamp, err = primitive.ReadLong(source); err != nil { 318 return nil, fmt.Errorf("cannot read default timestamp: %w", err) 319 } 320 options.DefaultTimestamp = &optionsDefaultTimestamp 321 } 322 if flags.Contains(primitive.QueryFlagWithKeyspace) { 323 if options.Keyspace, err = primitive.ReadString(source); err != nil { 324 return nil, fmt.Errorf("cannot read keyspace: %w", err) 325 } 326 } 327 if flags.Contains(primitive.QueryFlagNowInSeconds) { 328 var optionsNowInSeconds int32 329 if optionsNowInSeconds, err = primitive.ReadInt(source); err != nil { 330 return nil, fmt.Errorf("cannot read now-in-seconds: %w", err) 331 } 332 options.NowInSeconds = &optionsNowInSeconds 333 } 334 if flags.Contains(primitive.QueryFlagDseWithContinuousPagingOptions) { 335 if options.ContinuousPagingOptions, err = DecodeContinuousPagingOptions(source, version); err != nil { 336 return nil, fmt.Errorf("cannot read continuous paging options: %w", err) 337 } 338 } 339 return options, nil 340 }