diff --git a/.vscode/settings.json b/.vscode/settings.json index adab567..5974784 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,12 @@ { "cSpell.words": [ "Atomicity", + "Bools", "Fatalf", "Guid", "Newff", + "OUUID", + "OUUIDs", "Onheap", "Parellelism", "Printf", diff --git a/README.md b/README.md index 149b98b..c5b6161 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,14 @@ Connect to server: ctx := context.Background() // connect -c, err := ignite.NewClient(ctx, "tcp", "localhost:10800", 1, 0, 0) +c, err := ignite.Connect(ctx, ignite.ConnInfo{ + Network: "tcp", + Host: "localhost", + Port: 10800, + Major: 1, + Minor: 0, + Patch: 0, +}) if err != nil { t.Fatalf("failed connect to server: %v", err) } @@ -61,7 +68,7 @@ defer c.Close() ``` -See [example](https://github.com/amsokol/ignite-go-client/blob/master/examples_test.go) for more. +See [example](https://github.com/amsokol/ignite-go-client/blob/master/examples_test.go#L97) for more. See ["_test.go" files](https://github.com/amsokol/ignite-go-client/tree/master/binary/v1) for other examples. @@ -83,7 +90,7 @@ Connect to server: ctx := context.Background() // open connection -db, err := sql.Open("ignite", "tcp://localhost:10800/TestDB?version=1.0.0&&page-size=10000&timeout=5000") +db, err := sql.Open("ignite", "tcp://localhost:10800/ExampleDB?version=1.0.0&&page-size=10000&timeout=5000") if err != nil { t.Fatalf("failed to open connection: %v", err) } @@ -91,7 +98,7 @@ defer db.Close() ``` -See [example](https://github.com/amsokol/ignite-go-client/blob/master/examples_test.go) for more. +See [example](https://github.com/amsokol/ignite-go-client/blob/master/examples_test.go#L14) for more. Connection URL format: @@ -112,7 +119,7 @@ protocol://host:port/cache?param1=value1¶m2=value2¶mN=valueN | Name | Mandatory | Description | Default value | |--------------------|-----------|---------------------------------------------------------------|-----------------------------------| -| schema | no | Database schema | "" (PUBLIC schema will be used) | +| schema | no | Database schema | "" (PUBLIC schema will be used) | | version | no | Binary protocol version in Semantic Version format | 1.0.0 | | page-size | no | Query cursor page size | 10000 | | max-rows | no | Max rows to return by query | 0 (looks like it means unlimited) | @@ -142,7 +149,7 @@ protocol://host:port/cache?param1=value1¶m2=value2¶mN=valueN 1. Run tests into the root folder of this project: ```shell -# go test -count=1 -parallel 1 ./... +# go test ./... ``` ### Type mapping @@ -159,7 +166,7 @@ protocol://host:port/cache?param1=value1¶m2=value2¶mN=valueN | bool | bool | | String | string | | UUID (Guid) | uuid.UUID ([UUID library from Google](https://github.com/google/uuid)) | -| date* | ignite.Date | +| Date* | ignite.Date / time.Time | | byte array | []byte | | short array | []int16 | | int array | []int32 | @@ -169,8 +176,8 @@ protocol://host:port/cache?param1=value1¶m2=value2¶mN=valueN | char array | []ignite.Char | | bool array | []bool | | String array | []string | -| UUID (Guid) array | Not supported. Need help from Apache Ignite team. | -| Date array | Not supported. Need help from Apache Ignite team. | +| UUID (Guid) array | []uuid.UUID | +| Date array* | []ignite.Date / []time.Time | | Object array | Not supported. Need help. | | Collection | Not supported. Need help. | | Map | Not supported. Need help. | @@ -179,16 +186,30 @@ protocol://host:port/cache?param1=value1¶m2=value2¶mN=valueN | Decimal | Not supported. Need help. | | Decimal array | Not supported. Need help. | | Timestamp | time.Time | -| Timestamp array | Not supported. Need help. | -| Time** | ignite.Time | -| Time array | Not supported. Need help. | +| Timestamp array | []time.Time | +| Time** | ignite.Time / time.Time | +| Time array** | []ignite.Time / []time.Time | | NULL | nil | -*`date` is outdated type. It's recommended to use `Timestamp` type. -If you still need `date` type use `ignite.NativeTime2Date` and `ignite.Date2NativeTime` functions to convert between Golang `time.Time` and `ignite.Date` types. +*`Date` is outdated type. It's recommended to use `Timestamp` type. +If you still need `Date` type use `ignite.ToDate()` function when you **put** date: -**`Time` is outdated type. It's recommended to use `Timestamp` type. -If you still need `Time` type use `ignite.NativeTime2Time` and `ignite.Time2NativeTime` functions to convert between Golang `time.Time` and `ignite.Time` types. +```go +t := time.Date(2018, 4, 3, 0, 0, 0, 0, time.UTC) +err := c.CachePut("CacheGet", false, "Date", ToDate(t)) // ToDate() converts time.Time to ignite.Date +... +t, err = c.CacheGet("CacheGet", false, "Date") // 't' is time.Time, you don't need any converting +``` + +*`Time` is outdated type. It's recommended to use `Timestamp` type. +If you still need `Time` type use `ignite.ToTime()` function when you **put** time: + +```go +t := time.Date(1, 1, 1, 14, 25, 32, int(time.Millisecond*123), time.UTC) +err := c.CachePut("CacheGet", false, "Time", ToTime(t)) // ToTime() converts time.Time to ignite.Time (year, month and day are ignored) +... +t, err = c.CacheGet("CacheGet", false, "Time") // 't' is time.Time (where year=1, month=1 and day=1), you don't need any converting +``` ### SQL and Scan Queries supported operations diff --git a/binary/errors/ignite.go b/binary/errors/ignite.go index 972db5b..8a60826 100644 --- a/binary/errors/ignite.go +++ b/binary/errors/ignite.go @@ -4,11 +4,6 @@ import ( "fmt" ) -const ( - // StatusSuccess means success - StatusSuccess = 0 -) - // IgniteError is Apache Ignite error type IgniteError struct { // Apache Ignite specific status and message diff --git a/binary/v1/client-cache-configuration.go b/binary/v1/client-cache-configuration.go index 358dce7..9dc48e1 100644 --- a/binary/v1/client-cache-configuration.go +++ b/binary/v1/client-cache-configuration.go @@ -9,36 +9,36 @@ import ( // https://apacheignite.readme.io/docs/binary-client-protocol-cache-configuration-operations const ( - cacheConfigurationAtomicityModeCode int16 = 2 - cacheConfigurationBackupsCode int16 = 3 - cacheConfigurationCacheModeCode int16 = 1 - cacheConfigurationCopyOnReadCode int16 = 5 - cacheConfigurationDataRegionNameCode int16 = 100 - cacheConfigurationEagerTTLCode int16 = 405 - cacheConfigurationEnableStatisticsCode int16 = 406 - cacheConfigurationGroupNameCode int16 = 400 - cacheConfigurationLockTimeoutCode int16 = 402 - cacheConfigurationMaxConcurrentAsyncOperationsCode int16 = 403 - cacheConfigurationMaxQueryIteratorsCode int16 = 206 - cacheConfigurationNameCode int16 = 0 - cacheConfigurationOnheapCacheEnabledCode int16 = 101 - cacheConfigurationPartitionLossPolicyCode int16 = 404 - cacheConfigurationQueryDetailMetricsSizeCode int16 = 202 - cacheConfigurationQueryParellelismCode int16 = 201 - cacheConfigurationReadFromBackupCode int16 = 6 - cacheConfigurationRebalanceBatchSizeCode int16 = 303 - cacheConfigurationRebalanceBatchesPrefetchCountCode int16 = 304 - cacheConfigurationRebalanceDelayCode int16 = 301 - cacheConfigurationRebalanceModeCode int16 = 300 - cacheConfigurationRebalanceOrderCode int16 = 305 - cacheConfigurationRebalanceThrottleCode int16 = 306 - cacheConfigurationRebalanceTimeoutCode int16 = 302 - cacheConfigurationSQLEscapeAllCode int16 = 205 - cacheConfigurationSQLIndexInlineMaxSizeCode int16 = 204 - cacheConfigurationSQLSchemaCode int16 = 203 - cacheConfigurationWriteSynchronizationModeCode int16 = 4 - cacheConfigurationCacheKeyConfigurationsCode int16 = 401 - cacheConfigurationQueryEntitiesCode int16 = 200 + cacheConfigurationAtomicityModeCode = 2 + cacheConfigurationBackupsCode = 3 + cacheConfigurationCacheModeCode = 1 + cacheConfigurationCopyOnReadCode = 5 + cacheConfigurationDataRegionNameCode = 100 + cacheConfigurationEagerTTLCode = 405 + cacheConfigurationEnableStatisticsCode = 406 + cacheConfigurationGroupNameCode = 400 + cacheConfigurationLockTimeoutCode = 402 + cacheConfigurationMaxConcurrentAsyncOperationsCode = 403 + cacheConfigurationMaxQueryIteratorsCode = 206 + cacheConfigurationNameCode = 0 + cacheConfigurationOnheapCacheEnabledCode = 101 + cacheConfigurationPartitionLossPolicyCode = 404 + cacheConfigurationQueryDetailMetricsSizeCode = 202 + cacheConfigurationQueryParellelismCode = 201 + cacheConfigurationReadFromBackupCode = 6 + cacheConfigurationRebalanceBatchSizeCode = 303 + cacheConfigurationRebalanceBatchesPrefetchCountCode = 304 + cacheConfigurationRebalanceDelayCode = 301 + cacheConfigurationRebalanceModeCode = 300 + cacheConfigurationRebalanceOrderCode = 305 + cacheConfigurationRebalanceThrottleCode = 306 + cacheConfigurationRebalanceTimeoutCode = 302 + cacheConfigurationSQLEscapeAllCode = 205 + cacheConfigurationSQLIndexInlineMaxSizeCode = 204 + cacheConfigurationSQLSchemaCode = 203 + cacheConfigurationWriteSynchronizationModeCode = 4 + cacheConfigurationCacheKeyConfigurationsCode = 401 + cacheConfigurationQueryEntitiesCode = 200 ) const ( @@ -203,48 +203,72 @@ type CacheConfigurationRefs struct { // CacheCreateWithName Creates a cache with a given name. // Cache template can be applied if there is a '*' in the cache name. +// https://apacheignite.readme.io/docs/binary-client-protocol-cache-configuration-operations#section-op_cache_create_with_name func (c *client) CacheCreateWithName(cache string) error { - r, err := c.Exec(OpCacheCreateWithName, cache) - if err != nil { + // request and response + req := NewRequestOperation(OpCacheCreateWithName) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteOString(cache); err != nil { + return errors.Wrapf(err, "failed to write cache name") + } + + // execute operation + if err := c.Do(req, res); err != nil { return errors.Wrapf(err, "failed to execute OP_CACHE_CREATE_WITH_NAME operation") } - return r.CheckStatus() + return res.CheckStatus() } // CacheGetOrCreateWithName creates a cache with a given name. // Cache template can be applied if there is a '*' in the cache name. // Does nothing if the cache exists. func (c *client) CacheGetOrCreateWithName(cache string) error { - r, err := c.Exec(OpCacheGetOrCreateWithName, cache) - if err != nil { + // request and response + req := NewRequestOperation(OpCacheGetOrCreateWithName) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteOString(cache); err != nil { + return errors.Wrapf(err, "failed to write cache name") + } + + // execute operation + if err := c.Do(req, res); err != nil { return errors.Wrapf(err, "failed to execute OP_CACHE_GET_OR_CREATE_WITH_NAME operation") } - return r.CheckStatus() + return res.CheckStatus() } // CacheGetNames returns existing cache names. func (c *client) CacheGetNames() ([]string, error) { - r, err := c.Exec(OpCacheGetNames) - if err != nil { + // request and response + req := NewRequestOperation(OpCacheGetNames) + res := NewResponseOperation(req.UID) + + // execute operation + if err := c.Do(req, res); err != nil { return nil, errors.Wrapf(err, "failed to execute OP_CACHE_GET_NAMES operation") } - if err = r.CheckStatus(); err != nil { + + if err := res.CheckStatus(); err != nil { return nil, err } // get cache count - var count int32 - if err := r.ReadPrimitives(&count); err != nil { - return nil, errors.Wrapf(err, "failed to read cache count") + count, err := res.ReadInt() + if err != nil { + return nil, errors.Wrapf(err, "failed to read cache name count") } // read cache names names := make([]string, 0, int(count)) for i := 0; i < int(count); i++ { - var name string - if err := r.ReadPrimitives(&name); err != nil { + name, err := res.ReadOString() + if err != nil { return nil, errors.Wrapf(err, "failed to read cache name with index %d", i) } names = append(names, name) @@ -255,131 +279,225 @@ func (c *client) CacheGetNames() ([]string, error) { // CacheGetConfiguration gets configuration for the given cache. func (c *client) CacheGetConfiguration(cache string, flag byte) (*CacheConfiguration, error) { - r, err := c.Exec(OpCacheGetConfiguration, HashCode(cache), flag) - if err != nil { + // request and response + req := NewRequestOperation(OpCacheGetConfiguration) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return nil, errors.Wrapf(err, "failed to write cache name") + } + if err := req.WriteByte(flag); err != nil { + return nil, errors.Wrapf(err, "failed to write flag") + } + + // execute operation + if err := c.Do(req, res); err != nil { return nil, errors.Wrapf(err, "failed to execute OP_CACHE_GET_CONFIGURATION operation") } - if err = r.CheckStatus(); err != nil { + + if err := res.CheckStatus(); err != nil { return nil, err } + // get cache configuration + var err error var cc CacheConfiguration - var count, length int32 - if err = r.ReadPrimitives( - &length, - &cc.AtomicityMode, - &cc.Backups, - &cc.CacheMode, - &cc.CopyOnRead, - &cc.DataRegionName, - &cc.EagerTTL, - &cc.EnableStatistics, - &cc.GroupName, - &cc.LockTimeout, - &cc.MaxConcurrentAsyncOperations, - &cc.MaxQueryIterators, - &cc.Name, - &cc.OnheapCacheEnabled, - &cc.PartitionLossPolicy, - &cc.QueryDetailMetricsSize, - &cc.QueryParellelism, - &cc.ReadFromBackup, - &cc.RebalanceBatchSize, - &cc.RebalanceBatchesPrefetchCount, - &cc.RebalanceDelay, - &cc.RebalanceMode, - &cc.RebalanceOrder, - &cc.RebalanceThrottle, - &cc.RebalanceTimeout, - &cc.SQLEscapeAll, - &cc.SQLIndexInlineMaxSize, - &cc.SQLSchema, - &cc.WriteSynchronizationMode, - &count); err != nil { - return nil, errors.Wrapf(err, "failed to read response data") + if _, err = res.ReadInt(); err != nil { + return nil, errors.Wrapf(err, "failed to read length of the configuration in bytes") + } + if cc.AtomicityMode, err = res.ReadInt(); err != nil { + return nil, errors.Wrapf(err, "failed to read AtomicityMode") + } + if cc.Backups, err = res.ReadInt(); err != nil { + return nil, errors.Wrapf(err, "failed to read Backups") + } + if cc.CacheMode, err = res.ReadInt(); err != nil { + return nil, errors.Wrapf(err, "failed to read CacheMode") + } + if cc.CopyOnRead, err = res.ReadBool(); err != nil { + return nil, errors.Wrapf(err, "failed to read CopyOnRead") + } + if cc.DataRegionName, err = res.ReadOString(); err != nil { + return nil, errors.Wrapf(err, "failed to read DataRegionName") + } + if cc.EagerTTL, err = res.ReadBool(); err != nil { + return nil, errors.Wrapf(err, "failed to read EagerTTL") + } + if cc.EnableStatistics, err = res.ReadBool(); err != nil { + return nil, errors.Wrapf(err, "failed to read EnableStatistics") + } + if cc.GroupName, err = res.ReadOString(); err != nil { + return nil, errors.Wrapf(err, "failed to read GroupName") + } + if cc.LockTimeout, err = res.ReadLong(); err != nil { + return nil, errors.Wrapf(err, "failed to read LockTimeout") + } + if cc.MaxConcurrentAsyncOperations, err = res.ReadInt(); err != nil { + return nil, errors.Wrapf(err, "failed to read MaxConcurrentAsyncOperations") + } + if cc.MaxQueryIterators, err = res.ReadInt(); err != nil { + return nil, errors.Wrapf(err, "failed to read MaxQueryIterators") + } + if cc.Name, err = res.ReadOString(); err != nil { + return nil, errors.Wrapf(err, "failed to read Name") + } + if cc.OnheapCacheEnabled, err = res.ReadBool(); err != nil { + return nil, errors.Wrapf(err, "failed to read OnheapCacheEnabled") + } + if cc.PartitionLossPolicy, err = res.ReadInt(); err != nil { + return nil, errors.Wrapf(err, "failed to read PartitionLossPolicy") + } + if cc.QueryDetailMetricsSize, err = res.ReadInt(); err != nil { + return nil, errors.Wrapf(err, "failed to read QueryDetailMetricsSize") + } + if cc.QueryParellelism, err = res.ReadInt(); err != nil { + return nil, errors.Wrapf(err, "failed to read QueryParellelism") + } + if cc.ReadFromBackup, err = res.ReadBool(); err != nil { + return nil, errors.Wrapf(err, "failed to read ReadFromBackup") + } + if cc.RebalanceBatchSize, err = res.ReadInt(); err != nil { + return nil, errors.Wrapf(err, "failed to read RebalanceBatchSize") + } + if cc.RebalanceBatchesPrefetchCount, err = res.ReadLong(); err != nil { + return nil, errors.Wrapf(err, "failed to read RebalanceBatchesPrefetchCount") + } + if cc.RebalanceDelay, err = res.ReadLong(); err != nil { + return nil, errors.Wrapf(err, "failed to read RebalanceDelay") + } + if cc.RebalanceMode, err = res.ReadInt(); err != nil { + return nil, errors.Wrapf(err, "failed to read RebalanceMode") + } + if cc.RebalanceOrder, err = res.ReadInt(); err != nil { + return nil, errors.Wrapf(err, "failed to read RebalanceOrder") + } + if cc.RebalanceThrottle, err = res.ReadLong(); err != nil { + return nil, errors.Wrapf(err, "failed to read RebalanceThrottle") + } + if cc.RebalanceTimeout, err = res.ReadLong(); err != nil { + return nil, errors.Wrapf(err, "failed to read RebalanceTimeout") + } + if cc.SQLEscapeAll, err = res.ReadBool(); err != nil { + return nil, errors.Wrapf(err, "failed to read SQLEscapeAll") + } + if cc.SQLIndexInlineMaxSize, err = res.ReadInt(); err != nil { + return nil, errors.Wrapf(err, "failed to read SQLIndexInlineMaxSize") + } + if cc.SQLSchema, err = res.ReadOString(); err != nil { + return nil, errors.Wrapf(err, "failed to read SQLSchema") + } + if cc.WriteSynchronizationMode, err = res.ReadInt(); err != nil { + return nil, errors.Wrapf(err, "failed to read WriteSynchronizationMode") + } + // get CacheKeyConfiguration count + var count int32 + if count, err = res.ReadInt(); err != nil { + return nil, errors.Wrapf(err, "failed to read CacheKeyConfiguration count") } - cc.CacheKeyConfigurations = make([]CacheKeyConfiguration, 0, int(count)) for i := 0; i < int(count); i++ { var ckc CacheKeyConfiguration - if err = r.ReadPrimitives(&ckc.TypeName, &ckc.AffinityKeyFieldName); err != nil { - return nil, errors.Wrapf(err, "failed to read CacheKeyConfiguration data") + if ckc.TypeName, err = res.ReadOString(); err != nil { + return nil, errors.Wrapf(err, "failed to read CacheKeyConfiguration.TypeName") + } + if ckc.AffinityKeyFieldName, err = res.ReadOString(); err != nil { + return nil, errors.Wrapf(err, "failed to read CacheKeyConfiguration.AffinityKeyFieldName") } cc.CacheKeyConfigurations = append(cc.CacheKeyConfigurations, ckc) } - - if err = r.ReadPrimitives(&count); err != nil { + // get QueryEntities count + if count, err = res.ReadInt(); err != nil { return nil, errors.Wrapf(err, "failed to read QueryEntity count") } cc.QueryEntities = make([]QueryEntity, 0, int(count)) for i := 0; i < int(count); i++ { var qe QueryEntity - var count2 int32 - if err = r.ReadPrimitives( - &qe.KeyTypeName, - &qe.ValueTypeName, - &qe.TableName, - &qe.KeyFieldName, - &qe.ValueFieldName, - &count2); err != nil { - return nil, errors.Wrapf(err, "failed to read QueryEntity data") + if qe.KeyTypeName, err = res.ReadOString(); err != nil { + return nil, errors.Wrapf(err, "failed to read QueryEntity.KeyTypeName") + } + if qe.ValueTypeName, err = res.ReadOString(); err != nil { + return nil, errors.Wrapf(err, "failed to read QueryEntity.ValueTypeName") + } + if qe.TableName, err = res.ReadOString(); err != nil { + return nil, errors.Wrapf(err, "failed to read QueryEntity.TableName") } + if qe.KeyFieldName, err = res.ReadOString(); err != nil { + return nil, errors.Wrapf(err, "failed to read QueryEntity.KeyFieldName") + } + if qe.ValueFieldName, err = res.ReadOString(); err != nil { + return nil, errors.Wrapf(err, "failed to read QueryEntity.ValueFieldName") + } + + var count2 int32 - // read QueryField + // read QueryFields + if count2, err = res.ReadInt(); err != nil { + return nil, errors.Wrapf(err, "failed to read QueryField count") + } qe.QueryFields = make([]QueryField, 0, int(count2)) for j := 0; j < int(count2); j++ { var qf QueryField - if err = r.ReadPrimitives( - &qf.Name, - &qf.TypeName, - &qf.IsKeyField, - &qf.IsNotNullConstraintField); err != nil { - return nil, errors.Wrapf(err, "failed to read QueryField data") + if qf.Name, err = res.ReadOString(); err != nil { + return nil, errors.Wrapf(err, "failed to read QueryField.Name") + } + if qf.TypeName, err = res.ReadOString(); err != nil { + return nil, errors.Wrapf(err, "failed to read QueryField.TypeName") + } + if qf.IsKeyField, err = res.ReadBool(); err != nil { + return nil, errors.Wrapf(err, "failed to read QueryField.IsKeyField") + } + if qf.IsNotNullConstraintField, err = res.ReadBool(); err != nil { + return nil, errors.Wrapf(err, "failed to read QueryField.IsNotNullConstraintField") } - qe.QueryFields = append(qe.QueryFields, qf) } // read FieldNameAliases - if err = r.ReadPrimitives( - &count2); err != nil { + if count2, err = res.ReadInt(); err != nil { return nil, errors.Wrapf(err, "failed to read FieldNameAlias count") } qe.FieldNameAliases = make([]FieldNameAlias, 0, int(count2)) for j := 0; j < int(count2); j++ { var fna FieldNameAlias - if err = r.ReadPrimitives( - &fna.Name, - &fna.Alias); err != nil { - return nil, errors.Wrapf(err, "failed to read FieldNameAlias data") + if fna.Name, err = res.ReadOString(); err != nil { + return nil, errors.Wrapf(err, "failed to read FieldNameAlias.Name") + } + if fna.Alias, err = res.ReadOString(); err != nil { + return nil, errors.Wrapf(err, "failed to read FieldNameAlias.Alias") } qe.FieldNameAliases = append(qe.FieldNameAliases, fna) } // read QueryIndexes - if err = r.ReadPrimitives( - &count2); err != nil { + if count2, err = res.ReadInt(); err != nil { return nil, errors.Wrapf(err, "failed to read QueryIndex count") } qe.QueryIndexes = make([]QueryIndex, 0, int(count2)) for j := 0; j < int(count2); j++ { var qi QueryIndex - var count3 int32 - if err = r.ReadPrimitives( - &qi.Name, - &qi.Type, - &qi.InlineSize, - &count3); err != nil { - return nil, errors.Wrapf(err, "failed to read QueryIndex data") + if qi.Name, err = res.ReadOString(); err != nil { + return nil, errors.Wrapf(err, "failed to read QueryIndex.Name") + } + if qi.Type, err = res.ReadByte(); err != nil { + return nil, errors.Wrapf(err, "failed to read QueryIndex.Type") + } + if qi.InlineSize, err = res.ReadInt(); err != nil { + return nil, errors.Wrapf(err, "failed to read QueryIndex.InlineSize") } // read Fields + var count3 int32 + if count3, err = res.ReadInt(); err != nil { + return nil, errors.Wrapf(err, "failed to read Field count") + } qi.Fields = make([]Field, 0, int(count3)) for k := 0; k < int(count3); k++ { var f Field - if err = r.ReadPrimitives( - &f.Name, - &f.IsDescensing); err != nil { - return nil, errors.Wrapf(err, "failed to read Field data") + if f.Name, err = res.ReadOString(); err != nil { + return nil, errors.Wrapf(err, "failed to read Field.Name") + } + if f.IsDescensing, err = res.ReadBool(); err != nil { + return nil, errors.Wrapf(err, "failed to read Field.InlineSize") } qi.Fields = append(qi.Fields, f) } @@ -397,213 +515,339 @@ func (c *client) CacheCreateWithConfiguration(cc *CacheConfigurationRefs) error return c.cacheCreateWithConfiguration(OpCacheCreateWithConfiguration, cc) } -func (c *client) cacheCreateWithConfiguration(code OperationCode, cc *CacheConfigurationRefs) error { - o := c.Prepare(code) - count := 0 +// CacheGetOrCreateWithConfiguration creates cache with provided configuration. +// Does nothing if the name is already in use. +func (c *client) CacheGetOrCreateWithConfiguration(cc *CacheConfigurationRefs) error { + return c.cacheCreateWithConfiguration(OpCacheGetOrCreateWithConfiguration, cc) +} + +func (c *client) cacheCreateWithConfiguration(code int16, cc *CacheConfigurationRefs) error { + // request and response + req := NewRequestCacheCreateWithConfiguration(code) + res := NewResponseOperation(req.UID) if cc.AtomicityMode != nil { - if err := o.WritePrimitives(cacheConfigurationAtomicityModeCode, *cc.AtomicityMode); err != nil { - return errors.Wrapf(err, "failed to write AtomicityMode property") + if err := req.WriteShort(cacheConfigurationAtomicityModeCode); err != nil { + return errors.Wrapf(err, "failed to write AtomicityMode property code") } - count++ + if err := req.WriteInt(*cc.AtomicityMode); err != nil { + return errors.Wrapf(err, "failed to write AtomicityMode property value") + } + req.Count++ } if cc.Backups != nil { - if err := o.WritePrimitives(cacheConfigurationBackupsCode, *cc.Backups); err != nil { - return errors.Wrapf(err, "failed to write Backups property") + if err := req.WriteShort(cacheConfigurationBackupsCode); err != nil { + return errors.Wrapf(err, "failed to write Backups property code") + } + if err := req.WriteInt(*cc.Backups); err != nil { + return errors.Wrapf(err, "failed to write Backups property value") } - count++ + req.Count++ } if cc.CacheMode != nil { - if err := o.WritePrimitives(cacheConfigurationCacheModeCode, *cc.CacheMode); err != nil { - return errors.Wrapf(err, "failed to write CacheMode property") + if err := req.WriteShort(cacheConfigurationCacheModeCode); err != nil { + return errors.Wrapf(err, "failed to write CacheMode property code") } - count++ + if err := req.WriteInt(*cc.CacheMode); err != nil { + return errors.Wrapf(err, "failed to write CacheMode property value") + } + req.Count++ } if cc.CopyOnRead != nil { - if err := o.WritePrimitives(cacheConfigurationCopyOnReadCode, *cc.CopyOnRead); err != nil { - return errors.Wrapf(err, "failed to write CopyOnRead property") + if err := req.WriteShort(cacheConfigurationCopyOnReadCode); err != nil { + return errors.Wrapf(err, "failed to write CopyOnRead property code") + } + if err := req.WriteBool(*cc.CopyOnRead); err != nil { + return errors.Wrapf(err, "failed to write CopyOnRead property value") } - count++ + req.Count++ } if cc.DataRegionName != nil { - if err := o.WritePrimitives(cacheConfigurationDataRegionNameCode, *cc.DataRegionName); err != nil { - return errors.Wrapf(err, "failed to write DataRegionName property") + if err := req.WriteShort(cacheConfigurationDataRegionNameCode); err != nil { + return errors.Wrapf(err, "failed to write DataRegionName property code") } - count++ + if err := req.WriteOString(*cc.DataRegionName); err != nil { + return errors.Wrapf(err, "failed to write DataRegionName property value") + } + req.Count++ } if cc.EagerTTL != nil { - if err := o.WritePrimitives(cacheConfigurationEagerTTLCode, *cc.EagerTTL); err != nil { - return errors.Wrapf(err, "failed to write EagerTTL property") + if err := req.WriteShort(cacheConfigurationEagerTTLCode); err != nil { + return errors.Wrapf(err, "failed to write EagerTTL property code") + } + if err := req.WriteBool(*cc.EagerTTL); err != nil { + return errors.Wrapf(err, "failed to write EagerTTL property value") } - count++ + req.Count++ } if cc.EnableStatistics != nil { - if err := o.WritePrimitives(cacheConfigurationEnableStatisticsCode, *cc.EnableStatistics); err != nil { - return errors.Wrapf(err, "failed to write EnableStatistics property") + if err := req.WriteShort(cacheConfigurationEnableStatisticsCode); err != nil { + return errors.Wrapf(err, "failed to write EnableStatistics property code") } - count++ + if err := req.WriteBool(*cc.EnableStatistics); err != nil { + return errors.Wrapf(err, "failed to write EnableStatistics property value") + } + req.Count++ } if cc.GroupName != nil { - if err := o.WritePrimitives(cacheConfigurationGroupNameCode, *cc.GroupName); err != nil { - return errors.Wrapf(err, "failed to write GroupName property") + if err := req.WriteShort(cacheConfigurationGroupNameCode); err != nil { + return errors.Wrapf(err, "failed to write GroupName property code") + } + if err := req.WriteOString(*cc.GroupName); err != nil { + return errors.Wrapf(err, "failed to write GroupName property value") } - count++ + req.Count++ } if cc.LockTimeout != nil { - if err := o.WritePrimitives(cacheConfigurationLockTimeoutCode, *cc.LockTimeout); err != nil { - return errors.Wrapf(err, "failed to write LockTimeout property") + if err := req.WriteShort(cacheConfigurationLockTimeoutCode); err != nil { + return errors.Wrapf(err, "failed to write LockTimeout property code") } - count++ + if err := req.WriteLong(*cc.LockTimeout); err != nil { + return errors.Wrapf(err, "failed to write LockTimeout property value") + } + req.Count++ } if cc.MaxConcurrentAsyncOperations != nil { - if err := o.WritePrimitives(cacheConfigurationMaxConcurrentAsyncOperationsCode, *cc.MaxConcurrentAsyncOperations); err != nil { - return errors.Wrapf(err, "failed to write MaxConcurrentAsyncOperations property") + if err := req.WriteShort(cacheConfigurationMaxConcurrentAsyncOperationsCode); err != nil { + return errors.Wrapf(err, "failed to write MaxConcurrentAsyncOperations property code") + } + if err := req.WriteInt(*cc.MaxConcurrentAsyncOperations); err != nil { + return errors.Wrapf(err, "failed to write MaxConcurrentAsyncOperations property value") } - count++ + req.Count++ } if cc.MaxQueryIterators != nil { - if err := o.WritePrimitives(cacheConfigurationMaxQueryIteratorsCode, *cc.MaxQueryIterators); err != nil { - return errors.Wrapf(err, "failed to write MaxQueryIterators property") + if err := req.WriteShort(cacheConfigurationMaxQueryIteratorsCode); err != nil { + return errors.Wrapf(err, "failed to write MaxQueryIterators property code") + } + if err := req.WriteInt(*cc.MaxQueryIterators); err != nil { + return errors.Wrapf(err, "failed to write MaxQueryIterators property value") } - count++ + req.Count++ } if cc.Name != nil { - if err := o.WritePrimitives(cacheConfigurationNameCode, *cc.Name); err != nil { - return errors.Wrapf(err, "failed to write Name property") + if err := req.WriteShort(cacheConfigurationNameCode); err != nil { + return errors.Wrapf(err, "failed to write Name property code") } - count++ + if err := req.WriteOString(*cc.Name); err != nil { + return errors.Wrapf(err, "failed to write Name property value") + } + req.Count++ } if cc.OnheapCacheEnabled != nil { - if err := o.WritePrimitives(cacheConfigurationOnheapCacheEnabledCode, *cc.OnheapCacheEnabled); err != nil { - return errors.Wrapf(err, "failed to write OnheapCacheEnabled property") + if err := req.WriteShort(cacheConfigurationOnheapCacheEnabledCode); err != nil { + return errors.Wrapf(err, "failed to write OnheapCacheEnabled property code") + } + if err := req.WriteBool(*cc.OnheapCacheEnabled); err != nil { + return errors.Wrapf(err, "failed to write OnheapCacheEnabled property value") } - count++ + req.Count++ } if cc.PartitionLossPolicy != nil { - if err := o.WritePrimitives(cacheConfigurationPartitionLossPolicyCode, *cc.PartitionLossPolicy); err != nil { - return errors.Wrapf(err, "failed to write PartitionLossPolicy property") + if err := req.WriteShort(cacheConfigurationPartitionLossPolicyCode); err != nil { + return errors.Wrapf(err, "failed to write PartitionLossPolicy property code") } - count++ + if err := req.WriteInt(*cc.PartitionLossPolicy); err != nil { + return errors.Wrapf(err, "failed to write PartitionLossPolicy property value") + } + req.Count++ } if cc.QueryDetailMetricsSize != nil { - if err := o.WritePrimitives(cacheConfigurationQueryDetailMetricsSizeCode, *cc.QueryDetailMetricsSize); err != nil { - return errors.Wrapf(err, "failed to write QueryDetailMetricsSize property") + if err := req.WriteShort(cacheConfigurationQueryDetailMetricsSizeCode); err != nil { + return errors.Wrapf(err, "failed to write QueryDetailMetricsSize property code") + } + if err := req.WriteInt(*cc.QueryDetailMetricsSize); err != nil { + return errors.Wrapf(err, "failed to write QueryDetailMetricsSize property value") } - count++ + req.Count++ } if cc.QueryParellelism != nil { - if err := o.WritePrimitives(cacheConfigurationQueryParellelismCode, *cc.QueryParellelism); err != nil { - return errors.Wrapf(err, "failed to write QueryParellelism property") + if err := req.WriteShort(cacheConfigurationQueryParellelismCode); err != nil { + return errors.Wrapf(err, "failed to write QueryParellelism property code") } - count++ + if err := req.WriteInt(*cc.QueryParellelism); err != nil { + return errors.Wrapf(err, "failed to write QueryParellelism property value") + } + req.Count++ } if cc.ReadFromBackup != nil { - if err := o.WritePrimitives(cacheConfigurationReadFromBackupCode, *cc.ReadFromBackup); err != nil { - return errors.Wrapf(err, "failed to write ReadFromBackup property") + if err := req.WriteShort(cacheConfigurationReadFromBackupCode); err != nil { + return errors.Wrapf(err, "failed to write ReadFromBackup property code") + } + if err := req.WriteBool(*cc.ReadFromBackup); err != nil { + return errors.Wrapf(err, "failed to write ReadFromBackup property value") } - count++ + req.Count++ } if cc.RebalanceBatchSize != nil { - if err := o.WritePrimitives(cacheConfigurationRebalanceBatchSizeCode, *cc.RebalanceBatchSize); err != nil { - return errors.Wrapf(err, "failed to write RebalanceBatchSize property") + if err := req.WriteShort(cacheConfigurationRebalanceBatchSizeCode); err != nil { + return errors.Wrapf(err, "failed to write RebalanceBatchSize property code") } - count++ + if err := req.WriteInt(*cc.RebalanceBatchSize); err != nil { + return errors.Wrapf(err, "failed to write RebalanceBatchSize property value") + } + req.Count++ } if cc.RebalanceBatchesPrefetchCount != nil { - if err := o.WritePrimitives(cacheConfigurationRebalanceBatchesPrefetchCountCode, *cc.RebalanceBatchesPrefetchCount); err != nil { - return errors.Wrapf(err, "failed to write RebalanceBatchesPrefetchCount property") + if err := req.WriteShort(cacheConfigurationRebalanceBatchesPrefetchCountCode); err != nil { + return errors.Wrapf(err, "failed to write RebalanceBatchesPrefetchCount property code") + } + if err := req.WriteLong(*cc.RebalanceBatchesPrefetchCount); err != nil { + return errors.Wrapf(err, "failed to write RebalanceBatchesPrefetchCount property value") } - count++ + req.Count++ } if cc.RebalanceDelay != nil { - if err := o.WritePrimitives(cacheConfigurationRebalanceDelayCode, *cc.RebalanceDelay); err != nil { - return errors.Wrapf(err, "failed to write RebalanceDelay property") + if err := req.WriteShort(cacheConfigurationRebalanceDelayCode); err != nil { + return errors.Wrapf(err, "failed to write RebalanceDelay property code") } - count++ + if err := req.WriteLong(*cc.RebalanceDelay); err != nil { + return errors.Wrapf(err, "failed to write RebalanceDelay property value") + } + req.Count++ } if cc.RebalanceMode != nil { - if err := o.WritePrimitives(cacheConfigurationRebalanceModeCode, *cc.RebalanceMode); err != nil { - return errors.Wrapf(err, "failed to write RebalanceMode property") + if err := req.WriteShort(cacheConfigurationRebalanceModeCode); err != nil { + return errors.Wrapf(err, "failed to write RebalanceMode property code") + } + if err := req.WriteInt(*cc.RebalanceMode); err != nil { + return errors.Wrapf(err, "failed to write RebalanceMode property value") } - count++ + req.Count++ } if cc.RebalanceOrder != nil { - if err := o.WritePrimitives(cacheConfigurationRebalanceOrderCode, *cc.RebalanceOrder); err != nil { - return errors.Wrapf(err, "failed to write RebalanceOrder property") + if err := req.WriteShort(cacheConfigurationRebalanceOrderCode); err != nil { + return errors.Wrapf(err, "failed to write RebalanceOrder property code") } - count++ + if err := req.WriteInt(*cc.RebalanceOrder); err != nil { + return errors.Wrapf(err, "failed to write RebalanceOrder property value") + } + req.Count++ } if cc.RebalanceThrottle != nil { - if err := o.WritePrimitives(cacheConfigurationRebalanceThrottleCode, *cc.RebalanceThrottle); err != nil { - return errors.Wrapf(err, "failed to write RebalanceThrottle property") + if err := req.WriteShort(cacheConfigurationRebalanceThrottleCode); err != nil { + return errors.Wrapf(err, "failed to write RebalanceThrottle property code") + } + if err := req.WriteLong(*cc.RebalanceThrottle); err != nil { + return errors.Wrapf(err, "failed to write RebalanceThrottle property value") } - count++ + req.Count++ } if cc.RebalanceTimeout != nil { - if err := o.WritePrimitives(cacheConfigurationRebalanceTimeoutCode, *cc.RebalanceTimeout); err != nil { - return errors.Wrapf(err, "failed to write RebalanceTimeout property") + if err := req.WriteShort(cacheConfigurationRebalanceTimeoutCode); err != nil { + return errors.Wrapf(err, "failed to write RebalanceTimeout property code") } - count++ + if err := req.WriteLong(*cc.RebalanceTimeout); err != nil { + return errors.Wrapf(err, "failed to write RebalanceTimeout property value") + } + req.Count++ } if cc.SQLEscapeAll != nil { - if err := o.WritePrimitives(cacheConfigurationSQLEscapeAllCode, *cc.SQLEscapeAll); err != nil { - return errors.Wrapf(err, "failed to write SQLEscapeAll property") + if err := req.WriteShort(cacheConfigurationSQLEscapeAllCode); err != nil { + return errors.Wrapf(err, "failed to write SQLEscapeAll property code") + } + if err := req.WriteBool(*cc.SQLEscapeAll); err != nil { + return errors.Wrapf(err, "failed to write SQLEscapeAll property value") } - count++ + req.Count++ } if cc.SQLIndexInlineMaxSize != nil { - if err := o.WritePrimitives(cacheConfigurationSQLIndexInlineMaxSizeCode, *cc.SQLIndexInlineMaxSize); err != nil { - return errors.Wrapf(err, "failed to write SQLIndexInlineMaxSize property") + if err := req.WriteShort(cacheConfigurationSQLIndexInlineMaxSizeCode); err != nil { + return errors.Wrapf(err, "failed to write SQLIndexInlineMaxSize property code") } - count++ + if err := req.WriteInt(*cc.SQLIndexInlineMaxSize); err != nil { + return errors.Wrapf(err, "failed to write SQLIndexInlineMaxSize property value") + } + req.Count++ } if cc.SQLSchema != nil { - if err := o.WritePrimitives(cacheConfigurationSQLSchemaCode, *cc.SQLSchema); err != nil { - return errors.Wrapf(err, "failed to write SQLSchema property") + if err := req.WriteShort(cacheConfigurationSQLSchemaCode); err != nil { + return errors.Wrapf(err, "failed to write SQLSchema property code") + } + if err := req.WriteOString(*cc.SQLSchema); err != nil { + return errors.Wrapf(err, "failed to write SQLSchema property value") } - count++ + req.Count++ } if cc.WriteSynchronizationMode != nil { - if err := o.WritePrimitives(cacheConfigurationWriteSynchronizationModeCode, *cc.WriteSynchronizationMode); err != nil { - return errors.Wrapf(err, "failed to write WriteSynchronizationMode property") + if err := req.WriteShort(cacheConfigurationWriteSynchronizationModeCode); err != nil { + return errors.Wrapf(err, "failed to write WriteSynchronizationMode property code") + } + if err := req.WriteInt(*cc.WriteSynchronizationMode); err != nil { + return errors.Wrapf(err, "failed to write WriteSynchronizationMode property value") } - count++ + req.Count++ } if cc.WriteSynchronizationMode != nil { - if err := o.WritePrimitives(cacheConfigurationWriteSynchronizationModeCode, *cc.WriteSynchronizationMode); err != nil { - return errors.Wrapf(err, "failed to write WriteSynchronizationMode property") + if err := req.WriteShort(cacheConfigurationWriteSynchronizationModeCode); err != nil { + return errors.Wrapf(err, "failed to write WriteSynchronizationMode property code") } - count++ + if err := req.WriteInt(*cc.WriteSynchronizationMode); err != nil { + return errors.Wrapf(err, "failed to write WriteSynchronizationMode property value") + } + req.Count++ } if cc.CacheKeyConfigurations != nil && len(cc.CacheKeyConfigurations) > 0 { - if err := o.WritePrimitives(cacheConfigurationCacheKeyConfigurationsCode, int32(len(cc.CacheKeyConfigurations))); err != nil { - return errors.Wrapf(err, "failed to write CacheKeyConfigurations code and count") + if err := req.WriteShort(cacheConfigurationCacheKeyConfigurationsCode); err != nil { + return errors.Wrapf(err, "failed to write CacheKeyConfigurations code") + } + if err := req.WriteInt(int32(len(cc.CacheKeyConfigurations))); err != nil { + return errors.Wrapf(err, "failed to write CacheKeyConfigurations count") } for i, v := range cc.CacheKeyConfigurations { - if err := o.WritePrimitives(v.TypeName, v.AffinityKeyFieldName); err != nil { - return errors.Wrapf(err, "failed to write CacheKeyConfiguration with index %d", i) + if err := req.WriteOString(v.TypeName); err != nil { + return errors.Wrapf(err, "failed to write CacheKeyConfiguration.TypeName with index %d", i) + } + if err := req.WriteOString(v.AffinityKeyFieldName); err != nil { + return errors.Wrapf(err, "failed to write CacheKeyConfiguration.AffinityKeyFieldName with index %d", i) } } - count++ + req.Count++ } if cc.QueryEntities != nil && len(cc.QueryEntities) > 0 { - if err := o.WritePrimitives(cacheConfigurationQueryEntitiesCode, int32(len(cc.QueryEntities))); err != nil { - return errors.Wrapf(err, "failed to write QueryEntities code and count") + if err := req.WriteShort(cacheConfigurationQueryEntitiesCode); err != nil { + return errors.Wrapf(err, "failed to write QueryEntities code") + } + if err := req.WriteInt(int32(len(cc.QueryEntities))); err != nil { + return errors.Wrapf(err, "failed to write QueryEntity count") } for i, v := range cc.QueryEntities { + if err := req.WriteOString(v.KeyTypeName); err != nil { + return errors.Wrapf(err, "failed to write QueryEntity.KeyTypeName with index %d", i) + } + if err := req.WriteOString(v.ValueTypeName); err != nil { + return errors.Wrapf(err, "failed to write QueryEntity.ValueTypeName with index %d", i) + } + if err := req.WriteOString(v.TableName); err != nil { + return errors.Wrapf(err, "failed to write QueryEntity.TableName with index %d", i) + } + if err := req.WriteOString(v.KeyFieldName); err != nil { + return errors.Wrapf(err, "failed to write QueryEntity.KeyFieldName with index %d", i) + } + if err := req.WriteOString(v.ValueFieldName); err != nil { + return errors.Wrapf(err, "failed to write QueryEntity.ValueFieldName with index %d", i) + } var l int32 if v.QueryFields != nil { l = int32(len(v.QueryFields)) } - if err := o.WritePrimitives(v.KeyTypeName, v.ValueTypeName, v.TableName, v.KeyFieldName, v.ValueFieldName, - l); err != nil { - return errors.Wrapf(err, "failed to write QueryEntity with index %d", i) + if err := req.WriteInt(l); err != nil { + return errors.Wrapf(err, "failed to write QueryField count") } if l > 0 { // write QueryFields for j, v2 := range v.QueryFields { - if err := o.WritePrimitives(v2.Name, v2.TypeName, v2.IsKeyField, v2.IsNotNullConstraintField); err != nil { - return errors.Wrapf(err, "failed to write QueryField with index %d", j) + if err := req.WriteOString(v2.Name); err != nil { + return errors.Wrapf(err, "failed to write QueryField.Name with index %d", j) + } + if err := req.WriteOString(v2.TypeName); err != nil { + return errors.Wrapf(err, "failed to write QueryField.TypeName with index %d", j) + } + if err := req.WriteBool(v2.IsKeyField); err != nil { + return errors.Wrapf(err, "failed to write QueryField.IsKeyField with index %d", j) + } + if err := req.WriteBool(v2.IsNotNullConstraintField); err != nil { + return errors.Wrapf(err, "failed to write QueryField.IsNotNullConstraintField with index %d", j) } } } @@ -612,13 +856,16 @@ func (c *client) cacheCreateWithConfiguration(code OperationCode, cc *CacheConfi if v.FieldNameAliases != nil { l = int32(len(v.FieldNameAliases)) } - if err := o.WritePrimitives(l); err != nil { - return errors.Wrapf(err, "failed to write FieldNameAliases count") + if err := req.WriteInt(l); err != nil { + return errors.Wrapf(err, "failed to write FieldNameAlias count") } if l > 0 { for j, v2 := range v.FieldNameAliases { - if err := o.WritePrimitives(v2.Name, v2.Alias); err != nil { - return errors.Wrapf(err, "failed to write FieldNameAlias with index %d", j) + if err := req.WriteOString(v2.Name); err != nil { + return errors.Wrapf(err, "failed to write FieldNameAlias.Name with index %d", j) + } + if err := req.WriteOString(v2.Alias); err != nil { + return errors.Wrapf(err, "failed to write FieldNameAlias.Alias with index %d", j) } } } @@ -627,63 +874,67 @@ func (c *client) cacheCreateWithConfiguration(code OperationCode, cc *CacheConfi if v.QueryIndexes != nil { l = int32(len(v.QueryIndexes)) } - if err := o.WritePrimitives(l); err != nil { - return errors.Wrapf(err, "failed to write QueryIndexes count") + if err := req.WriteInt(l); err != nil { + return errors.Wrapf(err, "failed to write QueryIndex count") } if l > 0 { for j, v2 := range v.QueryIndexes { - if err := o.WritePrimitives(v2.Name, v2.Type, v2.InlineSize); err != nil { - return errors.Wrapf(err, "failed to write QueryIndex with index %d", j) + if err := req.WriteOString(v2.Name); err != nil { + return errors.Wrapf(err, "failed to write QueryIndex.Name with index %d", j) + } + if err := req.WriteByte(v2.Type); err != nil { + return errors.Wrapf(err, "failed to write QueryIndex.Type with index %d", j) + } + if err := req.WriteInt(v2.InlineSize); err != nil { + return errors.Wrapf(err, "failed to write QueryIndex.InlineSize with index %d", j) } // write Fields l = 0 if v2.Fields != nil { l = int32(len(v2.Fields)) } - if err := o.WritePrimitives(l); err != nil { - return errors.Wrapf(err, "failed to write Fields count") + if err := req.WriteInt(l); err != nil { + return errors.Wrapf(err, "failed to write Field count") } if l > 0 { for k, v3 := range v2.Fields { - if err := o.WritePrimitives(v3.Name, v3.IsDescensing); err != nil { - return errors.Wrapf(err, "failed to write Field with index %d", k) + if err := req.WriteOString(v3.Name); err != nil { + return errors.Wrapf(err, "failed to write Field.Name with index %d", k) + } + if err := req.WriteBool(v3.IsDescensing); err != nil { + return errors.Wrapf(err, "failed to write Field.IsDescensing with index %d", k) } } } } } } - count++ - } - - if count == 0 { - return errors.Errorf("no one property provided") + req.Count++ } - // execute - if err := o.WritePrefix(int32(o.Data.Len()), int16(count)); err != nil { - return errors.Wrapf(err, "failed to write message data length and property count") + // execute operation + if err := c.Do(req, res); err != nil { + return errors.Wrapf(err, "failed to execute operation to create cache with configuration") } - r, err := c.Call(o) - if err != nil { - return errors.Wrapf(err, "failed to execute operation") - } - - return r.CheckStatus() -} -// CacheGetOrCreateWithConfiguration creates cache with provided configuration. -// Does nothing if the name is already in use. -func (c *client) CacheGetOrCreateWithConfiguration(cc *CacheConfigurationRefs) error { - return c.cacheCreateWithConfiguration(OpCacheGetOrCreateWithConfiguration, cc) + return res.CheckStatus() } // CacheDestroy destroys cache with a given name. func (c *client) CacheDestroy(cache string) error { - r, err := c.Exec(OpCacheDestroy, HashCode(cache)) - if err != nil { + // request and response + req := NewRequestOperation(OpCacheDestroy) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return errors.Wrapf(err, "failed to write cache name hash") + } + + // execute operation + if err := c.Do(req, res); err != nil { return errors.Wrapf(err, "failed to execute OP_CACHE_DESTROY operation") } - return r.CheckStatus() + return res.CheckStatus() } diff --git a/binary/v1/client-cache-configuration_test.go b/binary/v1/client-cache-configuration_test.go index bb4f710..b4f31fc 100644 --- a/binary/v1/client-cache-configuration_test.go +++ b/binary/v1/client-cache-configuration_test.go @@ -1,16 +1,24 @@ package ignite import ( + "context" "reflect" - "sort" "testing" ) +var testConnInfo = ConnInfo{ + Network: "tcp", + Host: "localhost", + Port: 10800, + Major: 1, + Minor: 0, + Patch: 0, +} + func Test_client_CacheCreateWithName(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() @@ -19,22 +27,22 @@ func Test_client_CacheCreateWithName(t *testing.T) { } tests := []struct { name string - c *client + c Client args args wantErr bool }{ { - name: "success test", + name: "1", c: c, args: args{ - cache: "TestCache1", + cache: "CacheCreateWithName", }, }, { - name: "failed test", + name: "2", c: c, args: args{ - cache: "TestCache1", + cache: "CacheCreateWithName", }, wantErr: true, }, @@ -46,16 +54,12 @@ func Test_client_CacheCreateWithName(t *testing.T) { } }) } - - // clear test data - c.CacheDestroy("TestCache1") } func Test_client_CacheGetOrCreateWithName(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() @@ -64,22 +68,22 @@ func Test_client_CacheGetOrCreateWithName(t *testing.T) { } tests := []struct { name string - c *client + c Client args args wantErr bool }{ { - name: "success test", + name: "1", c: c, args: args{ - cache: "TestCache1", + cache: "CacheGetOrCreateWithName", }, }, { - name: "success test", + name: "2", c: c, args: args{ - cache: "TestCache1", + cache: "CacheGetOrCreateWithName", }, }, } @@ -90,41 +94,25 @@ func Test_client_CacheGetOrCreateWithName(t *testing.T) { } }) } - - // clear test data - c.CacheDestroy("TestCache1") } func Test_client_CacheGetNames(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") - if err = c.CacheCreateWithName("TestCache2"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache2") - if err = c.CacheCreateWithName("TestCache3"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache3") tests := []struct { name string - c *client - want []string + c Client + want string wantErr bool }{ { - name: "test success", + name: "1", c: c, - want: []string{"TestCache1", "TestCache2", "TestCache3", "TestDB1", "TestDB2", "TestDB3"}, + want: "CacheGetNames", }, } for _, tt := range tests { @@ -134,25 +122,26 @@ func Test_client_CacheGetNames(t *testing.T) { t.Errorf("client.CacheGetNames() error = %v, wantErr %v", err, tt.wantErr) return } - sort.Strings(got) - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("client.CacheGetNames() = %v, want %v", got, tt.want) + var found bool + for _, v := range got { + if v == tt.want { + found = true + break + } + } + if !found { + t.Errorf("client.CacheGetNames() , want \"%v\", but not found", tt.want) } }) } } func Test_client_CacheGetConfiguration(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") type args struct { cache string @@ -160,60 +149,85 @@ func Test_client_CacheGetConfiguration(t *testing.T) { } tests := []struct { name string - c *client + c Client args args want *CacheConfiguration wantErr bool }{ { - name: "success test", + name: "1", c: c, args: args{ - cache: "TestCache1", - flag: 0, + cache: "CacheGetConfiguration", }, - }, - { - name: "failed test", - c: c, - args: args{ - cache: "TestCache2", - flag: 0, + want: &CacheConfiguration{ + AtomicityMode: 0, + Backups: 0, + CacheMode: 2, + CopyOnRead: true, + DataRegionName: "", + EagerTTL: true, + EnableStatistics: false, + GroupName: "", + LockTimeout: 0, + MaxConcurrentAsyncOperations: 500, + MaxQueryIterators: 1024, + Name: "CacheGetConfiguration", + OnheapCacheEnabled: false, + PartitionLossPolicy: 4, + QueryDetailMetricsSize: 0, + QueryParellelism: 1, + ReadFromBackup: true, + RebalanceBatchSize: 524288, + RebalanceBatchesPrefetchCount: 2, + RebalanceDelay: 0, + RebalanceMode: 1, + RebalanceOrder: 0, + RebalanceThrottle: 0, + RebalanceTimeout: 10000, + SQLEscapeAll: false, + SQLIndexInlineMaxSize: -1, + SQLSchema: "", + WriteSynchronizationMode: 0, + CacheKeyConfigurations: []CacheKeyConfiguration{}, + QueryEntities: []QueryEntity{}, }, - wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - _, err := tt.c.CacheGetConfiguration(tt.args.cache, tt.args.flag) + got, err := tt.c.CacheGetConfiguration(tt.args.cache, tt.args.flag) if (err != nil) != tt.wantErr { t.Errorf("client.CacheGetConfiguration() error = %v, wantErr %v", err, tt.wantErr) return } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("client.CacheGetConfiguration() = %#v, want %#v", got, tt.want) + } }) } } func Test_client_CacheCreateWithConfiguration(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - cache := "TestCache1" + + cache := "CacheCreateWithConfiguration" type args struct { cc *CacheConfigurationRefs } tests := []struct { name string - c *client + c Client args args wantErr bool }{ { - name: "success test", + name: "1", c: c, args: args{ cc: &CacheConfigurationRefs{ @@ -222,7 +236,7 @@ func Test_client_CacheCreateWithConfiguration(t *testing.T) { }, }, { - name: "failed test", + name: "2", c: c, args: args{ cc: &CacheConfigurationRefs{ @@ -239,31 +253,28 @@ func Test_client_CacheCreateWithConfiguration(t *testing.T) { } }) } - - // clear test data - c.CacheDestroy(cache) } func Test_client_CacheGetOrCreateWithConfiguration(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - cache := "TestCache1" + + cache := "CacheGetOrCreateWithConfiguration" type args struct { cc *CacheConfigurationRefs } tests := []struct { name string - c *client + c Client args args wantErr bool }{ { - name: "success test", + name: "1", c: c, args: args{ cc: &CacheConfigurationRefs{ @@ -272,7 +283,7 @@ func Test_client_CacheGetOrCreateWithConfiguration(t *testing.T) { }, }, { - name: "success test", + name: "2", c: c, args: args{ cc: &CacheConfigurationRefs{ @@ -288,43 +299,36 @@ func Test_client_CacheGetOrCreateWithConfiguration(t *testing.T) { } }) } - - // clear test data - c.CacheDestroy(cache) } func Test_client_CacheDestroy(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } type args struct { cache string } tests := []struct { name string - c *client + c Client args args wantErr bool }{ { - name: "success test", + name: "1", c: c, args: args{ - cache: "TestCache1", + cache: "CacheDestroy", }, }, { - name: "failed test", + name: "1", c: c, args: args{ - cache: "TestCache1", + cache: "CacheDestroy", }, wantErr: true, }, diff --git a/binary/v1/client-key-value-queries.go b/binary/v1/client-key-value-queries.go index 5996d3f..3e66a0e 100644 --- a/binary/v1/client-key-value-queries.go +++ b/binary/v1/client-key-value-queries.go @@ -4,73 +4,95 @@ import ( "github.com/amsokol/ignite-go-client/binary/errors" ) +const ( + // PeekModeAll is ALL + PeekModeAll = 0 + // PeekModeNear is NEAR + PeekModeNear = 1 + // PeekModePrimary is PRIMARY + PeekModePrimary = 2 + // PeekModeBackup is BACKUP + PeekModeBackup = 3 +) + // Key-Value Queries // See for details: // https://apacheignite.readme.io/docs/binary-client-protocol-key-value-operations // CacheGet retrieves a value from cache by key. func (c *client) CacheGet(cache string, binary bool, key interface{}) (interface{}, error) { - o := c.Prepare(OpCacheGet) - // prepare data - if err := o.WritePrimitives(HashCode(cache), binary); err != nil { - return nil, errors.Wrapf(err, "failed to write cache id and binary flag") + // request and response + req := NewRequestOperation(OpCacheGet) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return nil, errors.Wrapf(err, "failed to write cache name") } - if err := o.WriteObjects(key); err != nil { - return nil, errors.Wrapf(err, "failed to write cache key and value") + if err := req.WriteBool(binary); err != nil { + return nil, errors.Wrapf(err, "failed to write binary flag") + } + if err := req.WriteObject(key); err != nil { + return nil, errors.Wrapf(err, "failed to write cache key") } - // execute - r, err := c.Call(o) - if err != nil { + // execute operation + if err := c.Do(req, res); err != nil { return nil, errors.Wrapf(err, "failed to execute OP_CACHE_GET operation") } - if err = r.CheckStatus(); err != nil { + if err := res.CheckStatus(); err != nil { return nil, err } - // read response data - object, err := r.ReadObject() - if err != nil { - return nil, errors.Wrapf(err, "failed to read value object") - } - - return object, nil + return res.ReadObject() } // CacheGetAll retrieves multiple key-value pairs from cache. func (c *client) CacheGetAll(cache string, binary bool, keys []interface{}) (map[interface{}]interface{}, error) { - o := c.Prepare(OpCacheGetAll) - // prepare data - if err := o.WritePrimitives(HashCode(cache), binary, int32(len(keys))); err != nil { - return nil, errors.Wrapf(err, "failed to write cache id, binary flag and key count") + // request and response + req := NewRequestOperation(OpCacheGetAll) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return nil, errors.Wrapf(err, "failed to write cache name") + } + if err := req.WriteBool(binary); err != nil { + return nil, errors.Wrapf(err, "failed to write binary flag") + } + if err := req.WriteInt(int32(len(keys))); err != nil { + return nil, errors.Wrapf(err, "failed to write key count") } for i, k := range keys { - if err := o.WriteObjects(k); err != nil { + if err := req.WriteObject(k); err != nil { return nil, errors.Wrapf(err, "failed to write cache key with index %d", i) } } - // execute - r, err := c.Call(o) - if err != nil { + // execute operation + if err := c.Do(req, res); err != nil { return nil, errors.Wrapf(err, "failed to execute OP_CACHE_GET_ALL operation") } - if err = r.CheckStatus(); err != nil { + if err := res.CheckStatus(); err != nil { return nil, err } // read response data - var count int32 - if err := r.ReadPrimitives(&count); err != nil { + count, err := res.ReadInt() + if err != nil { return nil, errors.Wrapf(err, "failed to read pairs count") } data := map[interface{}]interface{}{} for i := 0; i < int(count); i++ { - pair, err := r.ReadObjects(2) + key, err := res.ReadObject() + if err != nil { + return nil, errors.Wrapf(err, "failed to read key with index %d", i) + } + value, err := res.ReadObject() if err != nil { - return nil, errors.Wrapf(err, "failed to read pair with index %d", i) + return nil, errors.Wrapf(err, "failed to read value with index %d", i) } - data[pair[0]] = pair[1] + data[key] = value } return data, nil @@ -78,485 +100,567 @@ func (c *client) CacheGetAll(cache string, binary bool, keys []interface{}) (map // CachePut puts a value with a given key to cache (overwriting existing value if any). func (c *client) CachePut(cache string, binary bool, key interface{}, value interface{}) error { - o := c.Prepare(OpCachePut) - // prepare data - if err := o.WritePrimitives(HashCode(cache), binary); err != nil { - return errors.Wrapf(err, "failed to write cache id and binary flag") + // request and response + req := NewRequestOperation(OpCachePut) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return errors.Wrapf(err, "failed to write cache name") } - if err := o.WriteObjects(key, value); err != nil { - return errors.Wrapf(err, "failed to write cache key and value") + if err := req.WriteBool(binary); err != nil { + return errors.Wrapf(err, "failed to write binary flag") + } + if err := req.WriteObject(key); err != nil { + return errors.Wrapf(err, "failed to write cache key") + } + if err := req.WriteObject(value); err != nil { + return errors.Wrapf(err, "failed to write cache value") } - // execute - r, err := c.Call(o) - if err != nil { + // execute operation + if err := c.Do(req, res); err != nil { return errors.Wrapf(err, "failed to execute OP_CACHE_PUT operation") } - return r.CheckStatus() + return res.CheckStatus() } // CachePutAll puts a value with a given key to cache (overwriting existing value if any). func (c *client) CachePutAll(cache string, binary bool, data map[interface{}]interface{}) error { - o := c.Prepare(OpCachePutAll) - // prepare data - if err := o.WritePrimitives(HashCode(cache), binary, int32(len(data))); err != nil { - return errors.Wrapf(err, "failed to write cache id, binary flag and pairs count") + // request and response + req := NewRequestOperation(OpCachePutAll) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return errors.Wrapf(err, "failed to write cache name") + } + if err := req.WriteBool(binary); err != nil { + return errors.Wrapf(err, "failed to write binary flag") + } + if err := req.WriteInt(int32(len(data))); err != nil { + return errors.Wrapf(err, "failed to write key count") } for k, v := range data { - if err := o.WriteObjects(k, v); err != nil { - return errors.Wrapf(err, "failed to write cache key and value") + if err := req.WriteObject(k); err != nil { + return errors.Wrapf(err, "failed to write cache key") + } + if err := req.WriteObject(v); err != nil { + return errors.Wrapf(err, "failed to write cache value") } } - // execute - r, err := c.Call(o) - if err != nil { + // execute operation + if err := c.Do(req, res); err != nil { return errors.Wrapf(err, "failed to execute OP_CACHE_PUT_ALL operation") } - return r.CheckStatus() + return res.CheckStatus() } // CacheContainsKey returns a value indicating whether given key is present in cache. func (c *client) CacheContainsKey(cache string, binary bool, key interface{}) (bool, error) { - o := c.Prepare(OpCacheContainsKey) - // prepare data - if err := o.WritePrimitives(HashCode(cache), binary); err != nil { - return false, errors.Wrapf(err, "failed to write cache id and binary flag") + // request and response + req := NewRequestOperation(OpCacheContainsKey) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return false, errors.Wrapf(err, "failed to write cache name") + } + if err := req.WriteBool(binary); err != nil { + return false, errors.Wrapf(err, "failed to write binary flag") } - if err := o.WriteObjects(key); err != nil { + if err := req.WriteObject(key); err != nil { return false, errors.Wrapf(err, "failed to write cache key") } - // execute - r, err := c.Call(o) - if err != nil { + // execute operation + if err := c.Do(req, res); err != nil { return false, errors.Wrapf(err, "failed to execute OP_CACHE_CONTAINS_KEY operation") } - if err = r.CheckStatus(); err != nil { + if err := res.CheckStatus(); err != nil { return false, err } - // read response data - var res bool - if err = r.ReadPrimitives(&res); err != nil { - return false, errors.Wrapf(err, "failed to read result value") - } - - return res, nil + return res.ReadBool() } // CacheContainsKeys returns a value indicating whether all given keys are present in cache. func (c *client) CacheContainsKeys(cache string, binary bool, keys []interface{}) (bool, error) { - o := c.Prepare(OpCacheContainsKeys) - // prepare data - if err := o.WritePrimitives(HashCode(cache), binary, int32(len(keys))); err != nil { - return false, errors.Wrapf(err, "failed to write cache id, binary flag and key count") + // request and response + req := NewRequestOperation(OpCacheContainsKeys) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return false, errors.Wrapf(err, "failed to write cache name") + } + if err := req.WriteBool(binary); err != nil { + return false, errors.Wrapf(err, "failed to write binary flag") + } + if err := req.WriteInt(int32(len(keys))); err != nil { + return false, errors.Wrapf(err, "failed to write key count") } for i, k := range keys { - if err := o.WriteObjects(k); err != nil { + if err := req.WriteObject(k); err != nil { return false, errors.Wrapf(err, "failed to write cache key with index %d", i) } } - // execute - r, err := c.Call(o) - if err != nil { + // execute operation + if err := c.Do(req, res); err != nil { return false, errors.Wrapf(err, "failed to execute OP_CACHE_CONTAINS_KEYS operation") } - if err = r.CheckStatus(); err != nil { + if err := res.CheckStatus(); err != nil { return false, err } - // read response data - var res bool - if err := r.ReadPrimitives(&res); err != nil { - return false, errors.Wrapf(err, "failed to read result value") - } - - return res, nil + return res.ReadBool() } // CacheGetAndPut puts a value with a given key to cache, and returns the previous value for that key. func (c *client) CacheGetAndPut(cache string, binary bool, key interface{}, value interface{}) (interface{}, error) { - o := c.Prepare(OpCacheGetAndPut) - // prepare data - if err := o.WritePrimitives(HashCode(cache), binary); err != nil { - return nil, errors.Wrapf(err, "failed to write cache id and binary flag") + // request and response + req := NewRequestOperation(OpCacheGetAndPut) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return nil, errors.Wrapf(err, "failed to write cache name") + } + if err := req.WriteBool(binary); err != nil { + return nil, errors.Wrapf(err, "failed to write binary flag") } - if err := o.WriteObjects(key, value); err != nil { - return nil, errors.Wrapf(err, "failed to write cache key and value") + if err := req.WriteObject(key); err != nil { + return nil, errors.Wrapf(err, "failed to write cache key") + } + if err := req.WriteObject(value); err != nil { + return nil, errors.Wrapf(err, "failed to write cache value") } - // execute - r, err := c.Call(o) - if err != nil { + // execute operation + if err := c.Do(req, res); err != nil { return nil, errors.Wrapf(err, "failed to execute OP_CACHE_GET_AND_PUT operation") } - if err = r.CheckStatus(); err != nil { + if err := res.CheckStatus(); err != nil { return nil, err } - // read response data - object, err := r.ReadObject() - if err != nil { - return nil, errors.Wrapf(err, "failed to read value object") - } - - return object, nil + return res.ReadObject() } // CacheGetAndReplace puts a value with a given key to cache, returning previous value for that key, // if and only if there is a value currently mapped for that key. func (c *client) CacheGetAndReplace(cache string, binary bool, key interface{}, value interface{}) (interface{}, error) { - o := c.Prepare(OpCacheGetAndReplace) - // prepare data - if err := o.WritePrimitives(HashCode(cache), binary); err != nil { - return nil, errors.Wrapf(err, "failed to write cache id and binary flag") + // request and response + req := NewRequestOperation(OpCacheGetAndReplace) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return nil, errors.Wrapf(err, "failed to write cache name") } - if err := o.WriteObjects(key, value); err != nil { - return nil, errors.Wrapf(err, "failed to write cache key and value") + if err := req.WriteBool(binary); err != nil { + return nil, errors.Wrapf(err, "failed to write binary flag") } - - // execute - r, err := c.Call(o) - if err != nil { - return nil, errors.Wrapf(err, "failed to execute operation") + if err := req.WriteObject(key); err != nil { + return nil, errors.Wrapf(err, "failed to write cache key") } - if err = r.CheckStatus(); err != nil { - return nil, err + if err := req.WriteObject(value); err != nil { + return nil, errors.Wrapf(err, "failed to write cache value") } - // read response data - object, err := r.ReadObject() - if err != nil { - return nil, errors.Wrapf(err, "failed to read value object") + // execute operation + if err := c.Do(req, res); err != nil { + return nil, errors.Wrapf(err, "failed to execute OP_CACHE_GET_AND_REPLACE operation") + } + if err := res.CheckStatus(); err != nil { + return nil, err } - return object, nil + return res.ReadObject() } // CacheGetAndRemove removes the cache entry with specified key, returning the value. func (c *client) CacheGetAndRemove(cache string, binary bool, key interface{}) (interface{}, error) { - o := c.Prepare(OpCacheGetAndRemove) - // prepare data - if err := o.WritePrimitives(HashCode(cache), binary); err != nil { - return nil, errors.Wrapf(err, "failed to write cache id and binary flag") + // request and response + req := NewRequestOperation(OpCacheGetAndRemove) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return nil, errors.Wrapf(err, "failed to write cache name") + } + if err := req.WriteBool(binary); err != nil { + return nil, errors.Wrapf(err, "failed to write binary flag") } - if err := o.WriteObjects(key); err != nil { - return nil, errors.Wrapf(err, "failed to write cache key and value") + if err := req.WriteObject(key); err != nil { + return nil, errors.Wrapf(err, "failed to write cache key") } - // execute - r, err := c.Call(o) - if err != nil { + // execute operation + if err := c.Do(req, res); err != nil { return nil, errors.Wrapf(err, "failed to execute OP_CACHE_GET_AND_REMOVE operation") } - if err = r.CheckStatus(); err != nil { + if err := res.CheckStatus(); err != nil { return nil, err } - // read response data - object, err := r.ReadObject() - if err != nil { - return nil, errors.Wrapf(err, "failed to read value object") - } - - return object, nil + return res.ReadObject() } // CachePutIfAbsent puts a value with a given key to cache only if the key does not already exist. func (c *client) CachePutIfAbsent(cache string, binary bool, key interface{}, value interface{}) (bool, error) { - o := c.Prepare(OpCachePutIfAbsent) - // prepare data - if err := o.WritePrimitives(HashCode(cache), binary); err != nil { - return false, errors.Wrapf(err, "failed to write cache id and binary flag") + // request and response + req := NewRequestOperation(OpCachePutIfAbsent) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return false, errors.Wrapf(err, "failed to write cache name") + } + if err := req.WriteBool(binary); err != nil { + return false, errors.Wrapf(err, "failed to write binary flag") + } + if err := req.WriteObject(key); err != nil { + return false, errors.Wrapf(err, "failed to write cache key") } - if err := o.WriteObjects(key, value); err != nil { - return false, errors.Wrapf(err, "failed to write cache key and value") + if err := req.WriteObject(value); err != nil { + return false, errors.Wrapf(err, "failed to write cache value") } - // execute - r, err := c.Call(o) - if err != nil { + // execute operation + if err := c.Do(req, res); err != nil { return false, errors.Wrapf(err, "failed to execute OP_CACHE_PUT_IF_ABSENT operation") } - if err = r.CheckStatus(); err != nil { + if err := res.CheckStatus(); err != nil { return false, err } - // read response data - var res bool - if err := r.ReadPrimitives(&res); err != nil { - return false, errors.Wrapf(err, "failed to read result value") - } - - return res, nil + return res.ReadBool() } // CacheGetAndPutIfAbsent puts a value with a given key to cache only if the key does not already exist. func (c *client) CacheGetAndPutIfAbsent(cache string, binary bool, key interface{}, value interface{}) (interface{}, error) { - o := c.Prepare(OpCacheGetAndPutIfAbsent) - // prepare data - if err := o.WritePrimitives(HashCode(cache), binary); err != nil { - return nil, errors.Wrapf(err, "failed to write cache id and binary flag") + // request and response + req := NewRequestOperation(OpCacheGetAndPutIfAbsent) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return nil, errors.Wrapf(err, "failed to write cache name") + } + if err := req.WriteBool(binary); err != nil { + return nil, errors.Wrapf(err, "failed to write binary flag") } - if err := o.WriteObjects(key, value); err != nil { - return nil, errors.Wrapf(err, "failed to write cache key and value") + if err := req.WriteObject(key); err != nil { + return nil, errors.Wrapf(err, "failed to write cache key") + } + if err := req.WriteObject(value); err != nil { + return nil, errors.Wrapf(err, "failed to write cache value") } - // execute - r, err := c.Call(o) - if err != nil { + // execute operation + if err := c.Do(req, res); err != nil { return nil, errors.Wrapf(err, "failed to execute OP_CACHE_GET_AND_PUT_IF_ABSENT operation") } - if err = r.CheckStatus(); err != nil { + if err := res.CheckStatus(); err != nil { return nil, err } - // read response data - object, err := r.ReadObject() - if err != nil { - return nil, errors.Wrapf(err, "failed to read value object") - } - - return object, nil + return res.ReadObject() } // CacheReplace puts a value with a given key to cache only if the key already exists. func (c *client) CacheReplace(cache string, binary bool, key interface{}, value interface{}) (bool, error) { - o := c.Prepare(OpCacheReplace) - // prepare data - if err := o.WritePrimitives(HashCode(cache), binary); err != nil { - return false, errors.Wrapf(err, "failed to write cache id and binary flag") + // request and response + req := NewRequestOperation(OpCacheReplace) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return false, errors.Wrapf(err, "failed to write cache name") + } + if err := req.WriteBool(binary); err != nil { + return false, errors.Wrapf(err, "failed to write binary flag") } - if err := o.WriteObjects(key, value); err != nil { - return false, errors.Wrapf(err, "failed to write cache key and value") + if err := req.WriteObject(key); err != nil { + return false, errors.Wrapf(err, "failed to write cache key") + } + if err := req.WriteObject(value); err != nil { + return false, errors.Wrapf(err, "failed to write cache value") } - // execute - r, err := c.Call(o) - if err != nil { + // execute operation + if err := c.Do(req, res); err != nil { return false, errors.Wrapf(err, "failed to execute OP_CACHE_REPLACE operation") } - if err = r.CheckStatus(); err != nil { + if err := res.CheckStatus(); err != nil { return false, err } - // read response data - var res bool - if err := r.ReadPrimitives(&res); err != nil { - return false, errors.Wrapf(err, "failed to read result value") - } - - return res, nil + return res.ReadBool() } // CacheReplaceIfEquals puts a value with a given key to cache only if // the key already exists and value equals provided value. func (c *client) CacheReplaceIfEquals(cache string, binary bool, key interface{}, valueCompare interface{}, valueNew interface{}) (bool, error) { - o := c.Prepare(OpCacheReplaceIfEquals) - // prepare data - if err := o.WritePrimitives(HashCode(cache), binary); err != nil { - return false, errors.Wrapf(err, "failed to write cache id and binary flag") + // request and response + req := NewRequestOperation(OpCacheReplaceIfEquals) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return false, errors.Wrapf(err, "failed to write cache name") } - if err := o.WriteObjects(key, valueCompare, valueNew); err != nil { - return false, errors.Wrapf(err, "failed to write cache key and values") + if err := req.WriteBool(binary); err != nil { + return false, errors.Wrapf(err, "failed to write binary flag") + } + if err := req.WriteObject(key); err != nil { + return false, errors.Wrapf(err, "failed to write cache key") + } + if err := req.WriteObject(valueCompare); err != nil { + return false, errors.Wrapf(err, "failed to write cache value to compare") + } + if err := req.WriteObject(valueNew); err != nil { + return false, errors.Wrapf(err, "failed to write new cache value") } - // execute - r, err := c.Call(o) - if err != nil { + // execute operation + if err := c.Do(req, res); err != nil { return false, errors.Wrapf(err, "failed to execute OP_CACHE_REPLACE_IF_EQUALS operation") } - if err = r.CheckStatus(); err != nil { + if err := res.CheckStatus(); err != nil { return false, err } - // read response data - var res bool - if err := r.ReadPrimitives(&res); err != nil { - return false, errors.Wrapf(err, "failed to read result value") - } - - return res, nil + return res.ReadBool() } // CacheClear clears the cache without notifying listeners or cache writers. func (c *client) CacheClear(cache string, binary bool) error { - r, err := c.Exec(OpCacheClear, HashCode(cache), binary) - if err != nil { + // request and response + req := NewRequestOperation(OpCacheClear) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return errors.Wrapf(err, "failed to write cache name") + } + if err := req.WriteBool(binary); err != nil { + return errors.Wrapf(err, "failed to write binary flag") + } + + // execute operation + if err := c.Do(req, res); err != nil { return errors.Wrapf(err, "failed to execute OP_CACHE_CLEAR operation") } - return r.CheckStatus() + return res.CheckStatus() } // CacheClearKey clears the cache key without notifying listeners or cache writers. func (c *client) CacheClearKey(cache string, binary bool, key interface{}) error { - o := c.Prepare(OpCacheClearKey) - // prepare data - if err := o.WritePrimitives(HashCode(cache), binary); err != nil { - return errors.Wrapf(err, "failed to write cache id and binary flag") + // request and response + req := NewRequestOperation(OpCacheClearKey) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return errors.Wrapf(err, "failed to write cache name") + } + if err := req.WriteBool(binary); err != nil { + return errors.Wrapf(err, "failed to write binary flag") } - if err := o.WriteObjects(key); err != nil { + if err := req.WriteObject(key); err != nil { return errors.Wrapf(err, "failed to write cache key") } - // execute - r, err := c.Call(o) - if err != nil { + // execute operation + if err := c.Do(req, res); err != nil { return errors.Wrapf(err, "failed to execute OP_CACHE_CLEAR_KEY operation") } - return r.CheckStatus() + return res.CheckStatus() } // CacheClearKeys clears the cache keys without notifying listeners or cache writers. func (c *client) CacheClearKeys(cache string, binary bool, keys []interface{}) error { - o := c.Prepare(OpCacheClearKeys) - // prepare data - if err := o.WritePrimitives(HashCode(cache), binary, int32(len(keys))); err != nil { - return errors.Wrapf(err, "failed to write cache id, binary flag and key count") + // request and response + req := NewRequestOperation(OpCacheClearKeys) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return errors.Wrapf(err, "failed to write cache name") + } + if err := req.WriteBool(binary); err != nil { + return errors.Wrapf(err, "failed to write binary flag") + } + if err := req.WriteInt(int32(len(keys))); err != nil { + return errors.Wrapf(err, "failed to write key count") } for i, k := range keys { - if err := o.WriteObjects(k); err != nil { + if err := req.WriteObject(k); err != nil { return errors.Wrapf(err, "failed to write cache key with index %d", i) } } - // execute - r, err := c.Call(o) - if err != nil { + // execute operation + if err := c.Do(req, res); err != nil { return errors.Wrapf(err, "failed to execute OP_CACHE_CLEAR_KEYS operation") } - return r.CheckStatus() + return res.CheckStatus() } // CacheRemoveKey removes an entry with a given key, notifying listeners and cache writers. func (c *client) CacheRemoveKey(cache string, binary bool, key interface{}) (bool, error) { - o := c.Prepare(OpCacheRemoveKey) - // prepare data - if err := o.WritePrimitives(HashCode(cache), binary); err != nil { - return false, errors.Wrapf(err, "failed to write cache id and binary flag") + // request and response + req := NewRequestOperation(OpCacheRemoveKey) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return false, errors.Wrapf(err, "failed to write cache name") + } + if err := req.WriteBool(binary); err != nil { + return false, errors.Wrapf(err, "failed to write binary flag") } - if err := o.WriteObjects(key); err != nil { + if err := req.WriteObject(key); err != nil { return false, errors.Wrapf(err, "failed to write cache key") } - // execute - r, err := c.Call(o) - if err != nil { + // execute operation + if err := c.Do(req, res); err != nil { return false, errors.Wrapf(err, "failed to execute OP_CACHE_REMOVE_KEY operation") } - if err = r.CheckStatus(); err != nil { + if err := res.CheckStatus(); err != nil { return false, err } - // read response data - var res bool - if err := r.ReadPrimitives(&res); err != nil { - return false, errors.Wrapf(err, "failed to read result value") - } - - return res, nil + return res.ReadBool() } // CacheRemoveIfEquals removes an entry with a given key if provided value is equal to actual value, // notifying listeners and cache writers. func (c *client) CacheRemoveIfEquals(cache string, binary bool, key interface{}, value interface{}) (bool, error) { - o := c.Prepare(OpCacheRemoveIfEquals) - // prepare data - if err := o.WritePrimitives(HashCode(cache), binary); err != nil { - return false, errors.Wrapf(err, "failed to write cache id and binary flag") + // request and response + req := NewRequestOperation(OpCacheRemoveIfEquals) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return false, errors.Wrapf(err, "failed to write cache name") + } + if err := req.WriteBool(binary); err != nil { + return false, errors.Wrapf(err, "failed to write binary flag") } - if err := o.WriteObjects(key, value); err != nil { + if err := req.WriteObject(key); err != nil { return false, errors.Wrapf(err, "failed to write cache key") } + if err := req.WriteObject(value); err != nil { + return false, errors.Wrapf(err, "failed to write cache value") + } - // execute - r, err := c.Call(o) - if err != nil { + // execute operation + if err := c.Do(req, res); err != nil { return false, errors.Wrapf(err, "failed to execute OP_CACHE_REMOVE_IF_EQUALS operation") } - if err = r.CheckStatus(); err != nil { + if err := res.CheckStatus(); err != nil { return false, err } - // read response data - var res bool - if err := r.ReadPrimitives(&res); err != nil { - return false, errors.Wrapf(err, "failed to read result value") - } - - return res, nil + return res.ReadBool() } // CacheGetSize gets the number of entries in cache. -func (c *client) CacheGetSize(cache string, binary bool, count int, modes []byte) (int64, error) { - o := c.Prepare(OpCacheGetSize) - // prepare data - if err := o.WritePrimitives(HashCode(cache), binary, int32(count)); err != nil { - return 0, errors.Wrapf(err, "failed to write cache id, binary flag and mode count") +func (c *client) CacheGetSize(cache string, binary bool, modes []byte) (int64, error) { + // request and response + req := NewRequestOperation(OpCacheGetSize) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return 0, errors.Wrapf(err, "failed to write cache name") + } + if err := req.WriteBool(binary); err != nil { + return 0, errors.Wrapf(err, "failed to write binary flag") } + var count int32 if modes != nil || len(modes) > 0 { + count = int32(len(modes)) + } + if err := req.WriteInt(count); err != nil { + return 0, errors.Wrapf(err, "failed to write binary flag") + } + if count > 0 { for i, m := range modes { - if err := o.WritePrimitives(m); err != nil { + if err := req.WriteByte(m); err != nil { return 0, errors.Wrapf(err, "failed to write mode with index %d", i) } } - } else { - if err := o.WritePrimitives(byte(0)); err != nil { - return 0, errors.Wrapf(err, "failed to write mode ALL") - } } - // execute - r, err := c.Call(o) - if err != nil { + // execute operation + if err := c.Do(req, res); err != nil { return 0, errors.Wrapf(err, "failed to execute OP_CACHE_GET_SIZE operation") } - if err = r.CheckStatus(); err != nil { + if err := res.CheckStatus(); err != nil { return 0, err } - // read response data - var size int64 - if err := r.ReadPrimitives(&size); err != nil { - return 0, errors.Wrapf(err, "failed to read result value") - } - - return size, nil + return res.ReadLong() } // CacheRemoveKeys removes entries with given keys, notifying listeners and cache writers. func (c *client) CacheRemoveKeys(cache string, binary bool, keys []interface{}) error { - o := c.Prepare(OpCacheRemoveKeys) - // prepare data - if err := o.WritePrimitives(HashCode(cache), binary, int32(len(keys))); err != nil { - return errors.Wrapf(err, "failed to write cache id, binary flag and key count") + // request and response + req := NewRequestOperation(OpCacheRemoveKeys) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return errors.Wrapf(err, "failed to write cache name") + } + if err := req.WriteBool(binary); err != nil { + return errors.Wrapf(err, "failed to write binary flag") + } + if err := req.WriteInt(int32(len(keys))); err != nil { + return errors.Wrapf(err, "failed to write key count") } for i, k := range keys { - if err := o.WriteObjects(k); err != nil { + if err := req.WriteObject(k); err != nil { return errors.Wrapf(err, "failed to write cache key with index %d", i) } } - // execute - r, err := c.Call(o) - if err != nil { + // execute operation + if err := c.Do(req, res); err != nil { return errors.Wrapf(err, "failed to execute OP_CACHE_REMOVE_KEYS operation") } - return r.CheckStatus() + return res.CheckStatus() } // CacheRemoveAll destroys cache with a given name. func (c *client) CacheRemoveAll(cache string, binary bool) error { - r, err := c.Exec(OpCacheRemoveAll, HashCode(cache), binary) - if err != nil { + // request and response + req := NewRequestOperation(OpCacheRemoveAll) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return errors.Wrapf(err, "failed to write cache name") + } + if err := req.WriteBool(binary); err != nil { + return errors.Wrapf(err, "failed to write binary flag") + } + + // execute operation + if err := c.Do(req, res); err != nil { return errors.Wrapf(err, "failed to execute OP_CACHE_REMOVE_ALL operation") } - return r.CheckStatus() + return res.CheckStatus() } diff --git a/binary/v1/client-key-value-queries_test.go b/binary/v1/client-key-value-queries_test.go index 0e93882..631ee4e 100644 --- a/binary/v1/client-key-value-queries_test.go +++ b/binary/v1/client-key-value-queries_test.go @@ -1,6 +1,7 @@ package ignite import ( + "context" "reflect" "testing" "time" @@ -9,21 +10,52 @@ import ( ) func Test_client_CacheGet(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") - - // put test values - testClientCachePut(t, c) + c.CachePut("CacheGet", false, "byte", byte(123)) + c.CachePut("CacheGet", false, "short", int16(12345)) + c.CachePut("CacheGet", false, "int", int32(1234567890)) + c.CachePut("CacheGet", false, "long", int64(1234567890123456789)) + c.CachePut("CacheGet", false, "float", float32(123456.789)) + c.CachePut("CacheGet", false, "double", float64(123456789.12345)) + c.CachePut("CacheGet", false, "char", Char('A')) + c.CachePut("CacheGet", false, "bool", true) + c.CachePut("CacheGet", false, "string", "test string") uid, _ := uuid.Parse("d6589da7-f8b1-4687-b5bd-2ddc7362a4a4") + c.CachePut("CacheGet", false, "UUID", uid) + dm := time.Date(2018, 4, 3, 0, 0, 0, 0, time.UTC) + c.CachePut("CacheGet", false, "Date", ToDate(dm)) + c.CachePut("CacheGet", false, "byte array", []byte{1, 2, 3}) + c.CachePut("CacheGet", false, "short array", []int16{1, 2, 3}) + c.CachePut("CacheGet", false, "int array", []int32{1, 2, 3}) + c.CachePut("CacheGet", false, "long array", []int64{1, 2, 3}) + c.CachePut("CacheGet", false, "float array", []float32{1.1, 2.2, 3.3}) + c.CachePut("CacheGet", false, "double array", []float64{1.1, 2.2, 3.3}) + c.CachePut("CacheGet", false, "char array", []Char{'A', 'B', 'Я'}) + c.CachePut("CacheGet", false, "bool array", []bool{true, false, true}) + c.CachePut("CacheGet", false, "string array", []string{"one", "two", "три"}) + uid1, _ := uuid.Parse("a0c07c4c-7e2e-43d3-8eda-176881477c81") + uid2, _ := uuid.Parse("4015b55f-72f0-48a4-8d01-64168d50f627") + uid3, _ := uuid.Parse("827d1bf0-c5d4-4443-8708-d8b5de31fe74") + c.CachePut("CacheGet", false, "UUID array", []uuid.UUID{uid1, uid2, uid3}) + dm2 := time.Date(2019, 5, 4, 0, 0, 0, 0, time.UTC) + dm3 := time.Date(2020, 6, 5, 0, 0, 0, 0, time.UTC) + c.CachePut("CacheGet", false, "date array", []Date{ToDate(dm), ToDate(dm2), ToDate(dm3)}) tm := time.Date(2018, 4, 3, 14, 25, 32, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + c.CachePut("CacheGet", false, "Timestamp", tm) + tm1 := time.Date(2018, 4, 3, 14, 25, 32, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + tm2 := time.Date(2019, 5, 4, 15, 26, 33, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + tm3 := time.Date(2020, 6, 5, 16, 27, 34, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + c.CachePut("CacheGet", false, "Timestamp array", []time.Time{tm1, tm2, tm3}) + tm4 := time.Date(1, 1, 1, 14, 25, 32, int(time.Millisecond*123), time.UTC) + c.CachePut("CacheGet", false, "Time", ToTime(tm4)) + tm5 := time.Date(1, 1, 1, 14, 25, 32, int(time.Millisecond*123), time.UTC) + tm6 := time.Date(1, 1, 1, 15, 26, 33, int(time.Millisecond*123), time.UTC) + tm7 := time.Date(1, 1, 1, 16, 27, 34, int(time.Millisecond*123), time.UTC) + c.CachePut("CacheGet", false, "Time array", []Time{ToTime(tm5), ToTime(tm6), ToTime(tm7)}) type args struct { cache string @@ -32,689 +64,326 @@ func Test_client_CacheGet(t *testing.T) { } tests := []struct { name string - c *client + c Client args args want interface{} wantErr bool }{ { - name: "success test 1", + name: "byte", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key1", + cache: "CacheGet", + key: "byte", }, want: byte(123), }, { - name: "success test 2", + name: "short", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key2", + cache: "CacheGet", + key: "short", }, - want: int16(1234), + want: int16(12345), }, { - name: "success test 3", + name: "int", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key3", + cache: "CacheGet", + key: "int", }, - want: int32(1234), + want: int32(1234567890), }, { - name: "success test 4", + name: "long", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key4", + cache: "CacheGet", + key: "long", }, - want: int64(123456789), + want: int64(1234567890123456789), }, { - name: "success test 5", + name: "float", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key5", + cache: "CacheGet", + key: "float", }, - want: float32(1.123), + want: float32(123456.789), }, { - name: "success test 6", + name: "double", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key6", + cache: "CacheGet", + key: "double", }, - want: float64(1.123456), + want: float64(123456789.12345), }, { - name: "success test 7", + name: "char", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key7", + cache: "CacheGet", + key: "char", }, - want: Char('W'), + want: Char('A'), }, { - name: "success test 8", + name: "bool", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key8", + cache: "CacheGet", + key: "bool", }, want: true, }, { - name: "success test 9", + name: "string", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key9", + cache: "CacheGet", + key: "string", }, - want: "value", + want: "test string", }, { - name: "success test 10", + name: "UUID", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key10", + cache: "CacheGet", + key: "UUID", }, want: uid, }, { - name: "success test 11", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key11", - }, - want: NativeTime2Date(tm), - }, - { - name: "success test 12", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key12", - }, - want: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, - }, - { - name: "success test 13", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key13", - }, - want: []int16{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, - }, - { - name: "success test 14", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key14", - }, - want: []int32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, - }, - { - name: "success test 15", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key15", - }, - want: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, - }, - { - name: "success test 16", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key16", - }, - want: []float32{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}, - }, - { - name: "success test 17", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key17", - }, - want: []float64{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}, - }, - { - name: "success test 18", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key18", - }, - want: []Char{'a', 'b', 'c'}, - }, - { - name: "success test 19", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key19", - }, - want: []bool{true, false}, - }, - { - name: "success test 20", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key20", - }, - want: []string{"abc", "def"}, - }, - /* - { - name: "success test 21", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key21", - }, - want: []uuid.UUID{uid1, uid2}, - }, - */ - /* - { - name: "success test 22", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key22", - }, - want: []Date{12345, 67890}, - }, - */ - { - name: "success test 33", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key33", - }, - want: tm, - }, - { - name: "success test 36", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key36", - }, - want: NativeTime2Time(tm), - }, - { - name: "success test NULL", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key-does-not-exist", - }, - want: nil, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.c.CacheGet(tt.args.cache, tt.args.binary, tt.args.key) - if (err != nil) != tt.wantErr { - t.Errorf("client.CacheGet() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("client.CacheGet() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_client_CacheGetAll(t *testing.T) { - // get test data - c, err := getTestClient() - if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) - } - defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") - - testClientCachePutAll(t, c) - - type args struct { - cache string - binary bool - keys []interface{} - } - tests := []struct { - name string - c *client - args args - want map[interface{}]interface{} - wantErr bool - }{ - { - name: "success test 1", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - keys: []interface{}{ - byte(123), - float64(678), - "key", - }, - }, - want: map[interface{}]interface{}{ - byte(123): "test", - float64(678): Char('w'), - "key": int64(128), - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.c.CacheGetAll(tt.args.cache, tt.args.binary, tt.args.keys) - if (err != nil) != tt.wantErr { - t.Errorf("client.CacheGetAll() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("client.CacheGetAll() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_client_CachePut(t *testing.T) { - // get test data - c, err := getTestClient() - if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) - } - defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") - - testClientCachePut(t, c) -} - -func testClientCachePut(t *testing.T, c *client) { - uid, _ := uuid.Parse("d6589da7-f8b1-4687-b5bd-2ddc7362a4a4") - tm := time.Date(2018, 4, 3, 14, 25, 32, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) - - type args struct { - cache string - binary bool - key interface{} - value interface{} - } - tests := []struct { - name string - c *client - args args - wantErr bool - }{ - { - name: "success test 1", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key1", - value: byte(123), - }, - }, - { - name: "success test 2", + name: "Date", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key2", - value: int16(1234), + cache: "CacheGet", + key: "Date", }, + want: dm, }, { - name: "success test 3", + name: "byte array", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key3", - value: int32(1234), + cache: "CacheGet", + key: "byte array", }, + want: []byte{1, 2, 3}, }, { - name: "success test 4", + name: "short array", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key4", - value: int64(123456789), + cache: "CacheGet", + key: "short array", }, + want: []int16{1, 2, 3}, }, { - name: "success test 5", + name: "int array", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key5", - value: float32(1.123), + cache: "CacheGet", + key: "int array", }, + want: []int32{1, 2, 3}, }, { - name: "success test 6", + name: "long array", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key6", - value: float64(1.123456), + cache: "CacheGet", + key: "long array", }, + want: []int64{1, 2, 3}, }, { - name: "success test 7", + name: "float array", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key7", - value: Char('W'), + cache: "CacheGet", + key: "float array", }, + want: []float32{1.1, 2.2, 3.3}, }, { - name: "success test 8", + name: "double array", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key8", - value: true, + cache: "CacheGet", + key: "double array", }, + want: []float64{1.1, 2.2, 3.3}, }, { - name: "success test 9", + name: "char array", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key9", - value: "value", + cache: "CacheGet", + key: "char array", }, + want: []Char{'A', 'B', 'Я'}, }, { - name: "success test 10", + name: "bool array", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key10", - value: uid, + cache: "CacheGet", + key: "bool array", }, + want: []bool{true, false, true}, }, { - name: "success test 11", + name: "string array", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key11", - value: NativeTime2Date(tm), + cache: "CacheGet", + key: "string array", }, + want: []string{"one", "two", "три"}, }, { - name: "success test 12", + name: "UUID array", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key12", - value: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + cache: "CacheGet", + key: "UUID array", }, + want: []uuid.UUID{uid1, uid2, uid3}, }, { - name: "success test 13", + name: "date array", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key13", - value: []int16{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, - }, - }, - { - name: "success test 14", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key14", - value: []int32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + cache: "CacheGet", + key: "date array", }, + want: []time.Time{dm, dm2, dm3}, }, { - name: "success test 15", + name: "Timestamp", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key15", - value: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, - }, - }, - { - name: "success test 16", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key16", - value: []float32{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}, - }, - }, - { - name: "success test 17", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key17", - value: []float64{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}, - }, - }, - { - name: "success test 18", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key18", - value: []Char{'a', 'b', 'c'}, + cache: "CacheGet", + key: "Timestamp", }, + want: tm, }, { - name: "success test 19", + name: "Timestamp array", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key19", - value: []bool{true, false}, + cache: "CacheGet", + key: "Timestamp array", }, + want: []time.Time{tm1, tm2, tm3}, }, { - name: "success test 20", + name: "Time", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key20", - value: []string{"abc", "def"}, + cache: "CacheGet", + key: "Time", }, + want: tm4, }, - /* - { - name: "success test 21", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key21", - value: []uuid.UUID{uid1, uid2}, - }, - }, - */ - /* - { - name: "success test 22", - c: c, - args: args{ - cache: "TestCache1", - binary: false, - key: "key22", - value: []Date{12345, 67890}, - }, - }, - */ { - name: "success test 33", + name: "Time array", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key33", - value: tm, + cache: "CacheGet", + key: "Time array", }, + want: []time.Time{tm5, tm6, tm7}, }, { - name: "success test 36", + name: "NULL", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key36", - value: NativeTime2Time(tm), + cache: "CacheGet", + key: "NULL", }, + want: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := tt.c.CachePut(tt.args.cache, tt.args.binary, tt.args.key, tt.args.value); (err != nil) != tt.wantErr { - t.Errorf("client.CachePut() error = %v, wantErr %v", err, tt.wantErr) + got, err := tt.c.CacheGet(tt.args.cache, tt.args.binary, tt.args.key) + if (err != nil) != tt.wantErr { + t.Errorf("client.CacheGet() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("client.CacheGet() = %#v, want %#v", got, tt.want) } }) } } -func Test_client_CachePutAll(t *testing.T) { - // get test data - c, err := getTestClient() +func Test_client_CacheGetAll(t *testing.T) { + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) + err = c.CachePutAll("CacheGetAll", false, + map[interface{}]interface{}{"key1": "value1", Char('Q'): int32(12345), true: float64(123456.789)}) + if err != nil { + t.Fatal(err) } - defer c.CacheDestroy("TestCache1") - - testClientCachePutAll(t, c) -} -func testClientCachePutAll(t *testing.T, c *client) { type args struct { cache string binary bool - data map[interface{}]interface{} + keys []interface{} } tests := []struct { name string - c *client + c Client args args + want map[interface{}]interface{} wantErr bool }{ { - name: "success test 1", + name: "1", c: c, args: args{ - cache: "TestCache1", - binary: false, - data: map[interface{}]interface{}{ - byte(123): "test", - float64(678): Char('w'), - "key": int64(128), - }, + cache: "CacheGetAll", + keys: []interface{}{"key1", Char('Q'), true}, }, + want: map[interface{}]interface{}{"key1": "value1", Char('Q'): int32(12345), true: float64(123456.789)}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := tt.c.CachePutAll(tt.args.cache, tt.args.binary, tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("client.CachePutAll() error = %v, wantErr %v", err, tt.wantErr) + got, err := tt.c.CacheGetAll(tt.args.cache, tt.args.binary, tt.args.keys) + if (err != nil) != tt.wantErr { + t.Errorf("client.CacheGetAll() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("client.CacheGetAll() = %#v, want %#v", got, tt.want) } }) } } func Test_client_CacheContainsKey(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") - - // put test values - if err = c.CachePut("TestCache1", false, "key", "value"); err != nil { - t.Fatalf("failed to put test pair: %s", err.Error()) + err = c.CachePut("CacheContainsKey", false, "key1", "value1") + if err != nil { + t.Fatal(err) } type args struct { @@ -724,28 +393,26 @@ func Test_client_CacheContainsKey(t *testing.T) { } tests := []struct { name string - c *client + c Client args args want bool wantErr bool }{ { - name: "success test 1", + name: "1", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key", + cache: "CacheContainsKey", + key: "key1", }, want: true, }, { - name: "success test 2", + name: "2", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key-not-found", + cache: "CacheContainsKey", + key: "key2", }, want: false, }, @@ -765,23 +432,15 @@ func Test_client_CacheContainsKey(t *testing.T) { } func Test_client_CacheContainsKeys(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") - - // put test values - if err = c.CachePut("TestCache1", false, "key1", "value1"); err != nil { - t.Fatalf("failed to put test pair: %s", err.Error()) - } - if err = c.CachePut("TestCache1", false, "key2", "value1"); err != nil { - t.Fatalf("failed to put test pair: %s", err.Error()) + err = c.CachePutAll("CacheContainsKeys", false, + map[interface{}]interface{}{"key1": "value1", Char('Q'): int32(12345), true: float64(123456.789)}) + if err != nil { + t.Fatal(err) } type args struct { @@ -791,28 +450,26 @@ func Test_client_CacheContainsKeys(t *testing.T) { } tests := []struct { name string - c *client + c Client args args want bool wantErr bool }{ { - name: "success test 1", + name: "1", c: c, args: args{ - cache: "TestCache1", - binary: false, - keys: []interface{}{"key1", "key2"}, + cache: "CacheContainsKeys", + keys: []interface{}{"key1", Char('Q'), true}, }, want: true, }, { - name: "success test 2", + name: "2", c: c, args: args{ - cache: "TestCache1", - binary: false, - keys: []interface{}{"key1", "key-not-found"}, + cache: "CacheContainsKeys", + keys: []interface{}{"key2", Char('Q'), true}, }, want: false, }, @@ -832,21 +489,12 @@ func Test_client_CacheContainsKeys(t *testing.T) { } func Test_client_CacheGetAndPut(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") - - // put test values - if err = c.CachePut("TestCache1", false, "key", "value 1"); err != nil { - t.Fatalf("failed to put test pair: %s", err.Error()) - } + c.CachePut("CacheGetAndPut", false, "key", "value 1") type args struct { cache string @@ -856,30 +504,28 @@ func Test_client_CacheGetAndPut(t *testing.T) { } tests := []struct { name string - c *client + c Client args args want interface{} wantErr bool }{ { - name: "success test 1", + name: "1", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key", - value: "value 2", + cache: "CacheGetAndPut", + key: "key", + value: "value 2", }, want: "value 1", }, { - name: "success test 2", + name: "2", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key-not-exist", - value: "value", + cache: "CacheGetAndPut", + key: "key-not-exist", + value: "value", }, want: nil, }, @@ -899,21 +545,12 @@ func Test_client_CacheGetAndPut(t *testing.T) { } func Test_client_CacheGetAndReplace(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") - - // put test values - if err = c.CachePut("TestCache1", false, "key", "value 1"); err != nil { - t.Fatalf("failed to put test pair: %s", err.Error()) - } + c.CachePut("CacheGetAndReplace", false, "key", "value 1") type args struct { cache string @@ -923,16 +560,16 @@ func Test_client_CacheGetAndReplace(t *testing.T) { } tests := []struct { name string - c *client + c Client args args want interface{} wantErr bool }{ { - name: "success test 1", + name: "1", c: c, args: args{ - cache: "TestCache1", + cache: "CacheGetAndReplace", binary: false, key: "key", value: "value 2", @@ -940,10 +577,10 @@ func Test_client_CacheGetAndReplace(t *testing.T) { want: "value 1", }, { - name: "success test 2", + name: "2", c: c, args: args{ - cache: "TestCache1", + cache: "CacheGetAndReplace", binary: false, key: "key-not-exist", value: "value", @@ -966,21 +603,12 @@ func Test_client_CacheGetAndReplace(t *testing.T) { } func Test_client_CacheGetAndRemove(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") - - // put test values - if err = c.CachePut("TestCache1", false, "key", "value 1"); err != nil { - t.Fatalf("failed to put test pair: %s", err.Error()) - } + c.CachePut("CacheGetAndRemove", false, "key", "value 1") type args struct { cache string @@ -989,18 +617,17 @@ func Test_client_CacheGetAndRemove(t *testing.T) { } tests := []struct { name string - c *client + c Client args args want interface{} wantErr bool }{ { - name: "success test 1", + name: "1", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key", + cache: "CacheGetAndRemove", + key: "key", }, want: "value 1", }, @@ -1020,21 +647,11 @@ func Test_client_CacheGetAndRemove(t *testing.T) { } func Test_client_CachePutIfAbsent(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") - - // put test values - if err = c.CachePut("TestCache1", false, "key2", "value 2"); err != nil { - t.Fatalf("failed to put test pair: %s", err.Error()) - } type args struct { cache string @@ -1044,29 +661,29 @@ func Test_client_CachePutIfAbsent(t *testing.T) { } tests := []struct { name string - c *client + c Client args args want bool wantErr bool }{ { - name: "success test 1", + name: "1", c: c, args: args{ - cache: "TestCache1", + cache: "CachePutIfAbsent", binary: false, - key: "key1", + key: "key", value: byte(123), }, want: true, }, { - name: "success test 2", + name: "2", c: c, args: args{ - cache: "TestCache1", + cache: "CachePutIfAbsent", binary: false, - key: "key2", + key: "key", value: byte(45), }, want: false, @@ -1086,21 +703,16 @@ func Test_client_CachePutIfAbsent(t *testing.T) { } } -func Test_client_CacheReplace(t *testing.T) { - // get test data - c, err := getTestClient() +func Test_client_CacheGetAndPutIfAbsent(t *testing.T) { + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") // put test values - if err = c.CachePut("TestCache1", false, "key", "value 1"); err != nil { - t.Fatalf("failed to put test pair: %s", err.Error()) + if err = c.CachePut("CacheGetAndPutIfAbsent", false, "key", "value 1"); err != nil { + t.Fatal(err) } type args struct { @@ -1111,75 +723,69 @@ func Test_client_CacheReplace(t *testing.T) { } tests := []struct { name string - c *client + c Client args args - want bool + want interface{} wantErr bool }{ { - name: "success test 1", + name: "1", c: c, args: args{ - cache: "TestCache1", + cache: "CacheGetAndPutIfAbsent", binary: false, key: "key", value: "value 2", }, - want: true, + want: "value 1", }, { - name: "success test 2", + name: "2", c: c, args: args{ - cache: "TestCache1", + cache: "CacheGetAndPutIfAbsent", binary: false, - key: "key-not-found", - value: "value 3", + key: "key-not-exist", + value: "value", }, - want: false, + want: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := tt.c.CacheReplace(tt.args.cache, tt.args.binary, tt.args.key, tt.args.value) + got, err := tt.c.CacheGetAndPutIfAbsent(tt.args.cache, tt.args.binary, tt.args.key, tt.args.value) if (err != nil) != tt.wantErr { - t.Errorf("client.CacheReplace() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("client.CacheGetAndPutIfAbsent() error = %v, wantErr %v", err, tt.wantErr) return } - if got != tt.want { - t.Errorf("client.CacheReplace() = %v, want %v", got, tt.want) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("client.CacheGetAndPutIfAbsent() = %v, want %v", got, tt.want) } }) } } -func Test_client_CacheReplaceIfEquals(t *testing.T) { - // get test data - c, err := getTestClient() +func Test_client_CacheReplace(t *testing.T) { + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") // put test values - if err = c.CachePut("TestCache1", false, "key", "value 1"); err != nil { - t.Fatalf("failed to put test pair: %s", err.Error()) + if err = c.CachePut("CacheReplace", false, "key", "value 1"); err != nil { + t.Fatal(err) } type args struct { - cache string - binary bool - key interface{} - valueCompare interface{} - valueNew interface{} + cache string + binary bool + key interface{} + value interface{} } tests := []struct { name string - c *client + c Client args args want bool wantErr bool @@ -1188,123 +794,114 @@ func Test_client_CacheReplaceIfEquals(t *testing.T) { name: "success test 1", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key", - valueCompare: "value 1", - valueNew: "value 2", + cache: "CacheReplace", + binary: false, + key: "key", + value: "value 2", }, want: true, }, { - name: "success test 1", + name: "success test 2", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key", - valueCompare: "value 1", - valueNew: "value 3", + cache: "CacheReplace", + binary: false, + key: "key-not-found", + value: "value 3", }, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := tt.c.CacheReplaceIfEquals(tt.args.cache, tt.args.binary, tt.args.key, tt.args.valueCompare, tt.args.valueNew) + got, err := tt.c.CacheReplace(tt.args.cache, tt.args.binary, tt.args.key, tt.args.value) if (err != nil) != tt.wantErr { - t.Errorf("client.CacheReplaceIfEquals() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("client.CacheReplace() error = %v, wantErr %v", err, tt.wantErr) return } if got != tt.want { - t.Errorf("client.CacheReplaceIfEquals() = %v, want %v", got, tt.want) + t.Errorf("client.CacheReplace() = %v, want %v", got, tt.want) } }) } } -func Test_client_CacheGetAndPutIfAbsent(t *testing.T) { - // get test data - c, err := getTestClient() +func Test_client_CacheReplaceIfEquals(t *testing.T) { + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") // put test values - if err = c.CachePut("TestCache1", false, "key", "value 1"); err != nil { + if err = c.CachePut("CacheReplaceIfEquals", false, "key", "value 1"); err != nil { t.Fatalf("failed to put test pair: %s", err.Error()) } type args struct { - cache string - binary bool - key interface{} - value interface{} + cache string + binary bool + key interface{} + valueCompare interface{} + valueNew interface{} } tests := []struct { name string - c *client + c Client args args - want interface{} + want bool wantErr bool }{ { - name: "success test 1", + name: "1", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key", - value: "value 2", + cache: "CacheReplaceIfEquals", + binary: false, + key: "key", + valueCompare: "value 1", + valueNew: "value 2", }, - want: "value 1", + want: true, }, { - name: "success test 2", + name: "1", c: c, args: args{ - cache: "TestCache1", - binary: false, - key: "key-not-exist", - value: "value", + cache: "CacheReplaceIfEquals", + binary: false, + key: "key", + valueCompare: "value 1", + valueNew: "value 3", }, - want: nil, + want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := tt.c.CacheGetAndPutIfAbsent(tt.args.cache, tt.args.binary, tt.args.key, tt.args.value) + got, err := tt.c.CacheReplaceIfEquals(tt.args.cache, tt.args.binary, tt.args.key, tt.args.valueCompare, tt.args.valueNew) if (err != nil) != tt.wantErr { - t.Errorf("client.CacheGetAndPutIfAbsent() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("client.CacheReplaceIfEquals() error = %v, wantErr %v", err, tt.wantErr) return } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("client.CacheGetAndPutIfAbsent() = %v, want %v", got, tt.want) + if got != tt.want { + t.Errorf("client.CacheReplaceIfEquals() = %v, want %v", got, tt.want) } }) } } func Test_client_CacheClear(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") // put test values - if err = c.CachePut("TestCache1", false, "key", "value"); err != nil { - t.Fatalf("failed to put test pair: %s", err.Error()) + if err = c.CachePut("CacheClear", false, "key", "value 1"); err != nil { + t.Fatal(err) } type args struct { @@ -1313,15 +910,15 @@ func Test_client_CacheClear(t *testing.T) { } tests := []struct { name string - c *client + c Client args args wantErr bool }{ { - name: "success test 1", + name: "1", c: c, args: args{ - cache: "TestCache1", + cache: "CacheClear", binary: false, }, }, @@ -1336,20 +933,15 @@ func Test_client_CacheClear(t *testing.T) { } func Test_client_CacheClearKey(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") // put test values - if err = c.CachePut("TestCache1", false, "key", "value"); err != nil { - t.Fatalf("failed to put test pair: %s", err.Error()) + if err = c.CachePut("CacheClearKey", false, "key", "value 1"); err != nil { + t.Fatal(err) } type args struct { @@ -1359,15 +951,15 @@ func Test_client_CacheClearKey(t *testing.T) { } tests := []struct { name string - c *client + c Client args args wantErr bool }{ { - name: "success test 1", + name: "1", c: c, args: args{ - cache: "TestCache1", + cache: "CacheClearKey", binary: false, key: "key", }, @@ -1383,23 +975,18 @@ func Test_client_CacheClearKey(t *testing.T) { } func Test_client_CacheClearKeys(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") // put test values - if err = c.CachePut("TestCache1", false, "key1", "value1"); err != nil { - t.Fatalf("failed to put test pair: %s", err.Error()) + if err = c.CachePut("CacheClearKeys", false, "key1", "value 1"); err != nil { + t.Fatal(err) } - if err = c.CachePut("TestCache1", false, "key2", "value1"); err != nil { - t.Fatalf("failed to put test pair: %s", err.Error()) + if err = c.CachePut("CacheClearKeys", false, "key2", "value 2"); err != nil { + t.Fatal(err) } type args struct { @@ -1409,15 +996,15 @@ func Test_client_CacheClearKeys(t *testing.T) { } tests := []struct { name string - c *client + c Client args args wantErr bool }{ { - name: "success test 1", + name: "1", c: c, args: args{ - cache: "TestCache1", + cache: "CacheClearKeys", binary: false, keys: []interface{}{"key1", "key2"}, }, @@ -1433,20 +1020,15 @@ func Test_client_CacheClearKeys(t *testing.T) { } func Test_client_CacheRemoveKey(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") // put test values - if err = c.CachePut("TestCache1", false, "key", "value"); err != nil { - t.Fatalf("failed to put test pair: %s", err.Error()) + if err = c.CachePut("CacheRemoveKey", false, "key", "value 1"); err != nil { + t.Fatal(err) } type args struct { @@ -1456,26 +1038,26 @@ func Test_client_CacheRemoveKey(t *testing.T) { } tests := []struct { name string - c *client + c Client args args want bool wantErr bool }{ { - name: "success test 1", + name: "1", c: c, args: args{ - cache: "TestCache1", + cache: "CacheRemoveKey", binary: false, key: "key", }, want: true, }, { - name: "success test 1", + name: "1", c: c, args: args{ - cache: "TestCache1", + cache: "CacheRemoveKey", binary: false, key: "key", }, @@ -1497,20 +1079,15 @@ func Test_client_CacheRemoveKey(t *testing.T) { } func Test_client_CacheRemoveIfEquals(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") // put test values - if err = c.CachePut("TestCache1", false, "key", "value"); err != nil { - t.Fatalf("failed to put test pair: %s", err.Error()) + if err = c.CachePut("CacheRemoveIfEquals", false, "key", "value"); err != nil { + t.Fatal(err) } type args struct { @@ -1521,16 +1098,16 @@ func Test_client_CacheRemoveIfEquals(t *testing.T) { } tests := []struct { name string - c *client + c Client args args want bool wantErr bool }{ { - name: "success test 1", + name: "1", c: c, args: args{ - cache: "TestCache1", + cache: "CacheRemoveIfEquals", binary: false, key: "key", value: "invalid value", @@ -1538,10 +1115,10 @@ func Test_client_CacheRemoveIfEquals(t *testing.T) { want: false, }, { - name: "success test 1", + name: "1", c: c, args: args{ - cache: "TestCache1", + cache: "CacheRemoveIfEquals", binary: false, key: "key", value: "value", @@ -1564,53 +1141,45 @@ func Test_client_CacheRemoveIfEquals(t *testing.T) { } func Test_client_CacheGetSize(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") // put test values - if err = c.CachePut("TestCache1", false, "key", "value"); err != nil { - t.Fatalf("failed to put test pair: %s", err.Error()) + if err = c.CachePut("CacheGetSize", false, "key", "value"); err != nil { + t.Fatal(err) } type args struct { cache string binary bool - count int modes []byte } tests := []struct { name string - c *client + c Client args args want int64 wantErr bool }{ { - name: "success test 1", + name: "1", c: c, args: args{ - cache: "TestCache1", + cache: "CacheGetSize", binary: false, - count: 0, modes: []byte{0}, }, want: 1, }, { - name: "success test 1", + name: "2", c: c, args: args{ - cache: "TestCache1", + cache: "CacheGetSize", binary: false, - count: 0, modes: nil, }, want: 1, @@ -1618,7 +1187,7 @@ func Test_client_CacheGetSize(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := tt.c.CacheGetSize(tt.args.cache, tt.args.binary, tt.args.count, tt.args.modes) + got, err := tt.c.CacheGetSize(tt.args.cache, tt.args.binary, tt.args.modes) if (err != nil) != tt.wantErr { t.Errorf("client.CacheGetSize() error = %v, wantErr %v", err, tt.wantErr) return @@ -1631,23 +1200,18 @@ func Test_client_CacheGetSize(t *testing.T) { } func Test_client_CacheRemoveKeys(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") // put test values - if err = c.CachePut("TestCache1", false, "key1", "value1"); err != nil { - t.Fatalf("failed to put test pair: %s", err.Error()) + if err = c.CachePut("CacheRemoveKeys", false, "key1", "value 1"); err != nil { + t.Fatal(err) } - if err = c.CachePut("TestCache1", false, "key2", "value1"); err != nil { - t.Fatalf("failed to put test pair: %s", err.Error()) + if err = c.CachePut("CacheRemoveKeys", false, "key2", "value 2"); err != nil { + t.Fatal(err) } type args struct { @@ -1657,17 +1221,16 @@ func Test_client_CacheRemoveKeys(t *testing.T) { } tests := []struct { name string - c *client + c Client args args wantErr bool }{ { - name: "success test 1", + name: "1", c: c, args: args{ - cache: "TestCache1", - binary: false, - keys: []interface{}{"key1", "key2"}, + cache: "CacheRemoveKeys", + keys: []interface{}{"key1", "key2"}, }, }, } @@ -1681,23 +1244,18 @@ func Test_client_CacheRemoveKeys(t *testing.T) { } func Test_client_CacheRemoveAll(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - if err = c.CacheCreateWithName("TestCache1"); err != nil { - t.Fatalf("failed to create test cache: %s", err.Error()) - } - defer c.CacheDestroy("TestCache1") // put test values - if err = c.CachePut("TestCache1", false, "key1", "value1"); err != nil { - t.Fatalf("failed to put test pair: %s", err.Error()) + if err = c.CachePut("CacheRemoveAll", false, "key1", "value 1"); err != nil { + t.Fatal(err) } - if err = c.CachePut("TestCache1", false, "key2", "value1"); err != nil { - t.Fatalf("failed to put test pair: %s", err.Error()) + if err = c.CachePut("CacheRemoveAll", false, "key2", "value 2"); err != nil { + t.Fatal(err) } type args struct { @@ -1706,7 +1264,7 @@ func Test_client_CacheRemoveAll(t *testing.T) { } tests := []struct { name string - c *client + c Client args args wantErr bool }{ @@ -1714,8 +1272,7 @@ func Test_client_CacheRemoveAll(t *testing.T) { name: "success test 1", c: c, args: args{ - cache: "TestCache1", - binary: false, + cache: "CacheRemoveAll", }, }, } diff --git a/binary/v1/client-sql-and-scan-queries.go b/binary/v1/client-sql-and-scan-queries.go index 3530ce9..e82136b 100644 --- a/binary/v1/client-sql-and-scan-queries.go +++ b/binary/v1/client-sql-and-scan-queries.go @@ -139,12 +139,25 @@ type QuerySQLFieldsResult struct { } func (c *client) QuerySQL(cache string, binary bool, data QuerySQLData) (QuerySQLResult, error) { - o := c.Prepare(OpQuerySQL) + // request and response + req := NewRequestOperation(OpQuerySQL) + res := NewResponseOperation(req.UID) - var res QuerySQLResult + var r QuerySQLResult + var err error - if err := o.WritePrimitives(HashCode(cache), binary, data.Table, data.Query); err != nil { - return res, errors.Wrapf(err, "failed to write request data") + // set parameters + if err = req.WriteInt(HashCode(cache)); err != nil { + return r, errors.Wrapf(err, "failed to write cache name") + } + if err = req.WriteBool(binary); err != nil { + return r, errors.Wrapf(err, "failed to write binary flag") + } + if err = req.WriteOString(data.Table); err != nil { + return r, errors.Wrapf(err, "failed to write table name") + } + if err = req.WriteOString(data.Query); err != nil { + return r, errors.Wrapf(err, "failed to write query") } var l int32 @@ -152,155 +165,150 @@ func (c *client) QuerySQL(cache string, binary bool, data QuerySQLData) (QuerySQ l = int32(len(data.QueryArgs)) } // write args - if err := o.WritePrimitives(l); err != nil { - return res, errors.Wrapf(err, "failed to write query arg count") + if err = req.WriteInt(l); err != nil { + return r, errors.Wrapf(err, "failed to write query arg count") } if l > 0 { for i, v := range data.QueryArgs { - if err := o.WriteObjects(v); err != nil { - return res, errors.Wrapf(err, "failed to write query arg with index %d", i) + if err = req.WriteObject(v); err != nil { + return r, errors.Wrapf(err, "failed to write query arg with index %d", i) } } } - if err := o.WritePrimitives(data.DistributedJoins, data.LocalQuery, data.ReplicatedOnly, - int32(data.PageSize), data.Timeout); err != nil { - return res, errors.Wrapf(err, "failed to write request data") + if err = req.WriteBool(data.DistributedJoins); err != nil { + return r, errors.Wrapf(err, "failed to write distributed joins flag") } - - // execute - r, err := c.Call(o) - if err != nil { - return res, errors.Wrapf(err, "failed to execute OP_QUERY_SQL operation") + if err = req.WriteBool(data.LocalQuery); err != nil { + return r, errors.Wrapf(err, "failed to write local query flag") } - if err = r.CheckStatus(); err != nil { - return res, err + if err = req.WriteBool(data.ReplicatedOnly); err != nil { + return r, errors.Wrapf(err, "failed to write replicated only flag") } - - var count int32 - if err = r.ReadPrimitives(&res.ID, &count); err != nil { - return res, errors.Wrapf(err, "failed to read response data") + if err = req.WriteInt(int32(data.PageSize)); err != nil { + return r, errors.Wrapf(err, "failed to write page size") } - res.Keys = make([]interface{}, 0, int(count)) - res.Values = make([]interface{}, 0, int(count)) - // read data - for i := 0; i < int(count); i++ { - pair, err := r.ReadObjects(2) - if err != nil { - return res, errors.Wrapf(err, "failed to read pair with index %d", i) - } - res.Keys = append(res.Keys, pair[0]) - res.Values = append(res.Values, pair[1]) + if err = req.WriteLong(data.Timeout); err != nil { + return r, errors.Wrapf(err, "failed to write timeout") } - if err = r.ReadPrimitives(&res.HasMore); err != nil { - return res, errors.Wrapf(err, "failed to read response data") - } - return res, nil -} -// QuerySQLCursorGetPage retrieves the next SQL query cursor page by cursor id from QuerySQL. -func (c *client) QuerySQLCursorGetPage(id int64) (QuerySQLPage, error) { - var res QuerySQLPage - - r, err := c.Exec(OpQuerySQLCursorGetPage, id) - if err != nil { - return res, errors.Wrapf(err, "failed to execute OP_QUERY_SQL_CURSOR_GET_PAGE operation") + // execute operation + if err = c.Do(req, res); err != nil { + return r, errors.Wrapf(err, "failed to execute OP_QUERY_SQL operation") } - if err = r.CheckStatus(); err != nil { - return res, err + if err = res.CheckStatus(); err != nil { + return r, err } - // read data - var count int32 - if err = r.ReadPrimitives(&count); err != nil { - return res, errors.Wrapf(err, "failed to read response data") + // process result + if r.ID, err = res.ReadLong(); err != nil { + return r, errors.Wrapf(err, "failed to read cursor ID") + } + count, err := res.ReadInt() + if err != nil { + return r, errors.Wrapf(err, "failed to read row count") } - res.Keys = make([]interface{}, 0, int(count)) - res.Values = make([]interface{}, 0, int(count)) + r.Keys = make([]interface{}, 0, int(count)) + r.Values = make([]interface{}, 0, int(count)) // read data for i := 0; i < int(count); i++ { - pair, err := r.ReadObjects(2) + key, err := res.ReadObject() + if err != nil { + return r, errors.Wrapf(err, "failed to read key with index %d", i) + } + value, err := res.ReadObject() if err != nil { - return res, errors.Wrapf(err, "failed to read pair with index %d", i) + return r, errors.Wrapf(err, "failed to read value with index %d", i) } - res.Keys = append(res.Keys, pair[0]) - res.Values = append(res.Values, pair[1]) + r.Keys = append(r.Keys, key) + r.Values = append(r.Values, value) } - if err = r.ReadPrimitives(&res.HasMore); err != nil { - return res, errors.Wrapf(err, "failed to read response data") + if r.HasMore, err = res.ReadBool(); err != nil { + return r, errors.Wrapf(err, "failed to read has more flag") } - - return res, nil + return r, nil } -// QuerySQLFields performs SQL fields query. -func (c *client) QuerySQLFields(cache string, binary bool, data QuerySQLFieldsData) (QuerySQLFieldsResult, error) { - var res QuerySQLFieldsResult +// QuerySQLCursorGetPage retrieves the next SQL query cursor page by cursor id from QuerySQL. +func (c *client) QuerySQLCursorGetPage(id int64) (QuerySQLPage, error) { + // request and response + req := NewRequestOperation(OpQuerySQLCursorGetPage) + res := NewResponseOperation(req.UID) - r, err := c.QuerySQLFieldsRaw(cache, binary, data) - if err != nil { - return res, errors.Wrapf(err, "failed to execute OP_QUERY_SQL_FIELDS operation") + var r QuerySQLPage + var err error + + // set parameters + if err = req.WriteLong(id); err != nil { + return r, errors.Wrapf(err, "failed to write cursor id") } - // read field names - var fieldCount int32 - if err = r.ReadPrimitives(&res.ID, &fieldCount); err != nil { - return res, errors.Wrapf(err, "failed to read field count") + // execute operation + if err = c.Do(req, res); err != nil { + return r, errors.Wrapf(err, "failed to execute OP_QUERY_SQL_CURSOR_GET_PAGE operation") } - res.FieldCount = int(fieldCount) - if data.IncludeFieldNames { - res.Fields = make([]string, 0, res.FieldCount) - for i := 0; i < res.FieldCount; i++ { - var s string - if err = r.ReadPrimitives(&s); err != nil { - return res, errors.Wrapf(err, "failed to read field name with index %d", i) - } - res.Fields = append(res.Fields, s) - } - } else { - res.Fields = []string{} + if err = res.CheckStatus(); err != nil { + return r, err } - // read data - var rowCount int32 - if err = r.ReadPrimitives(&rowCount); err != nil { - return res, errors.Wrapf(err, "failed to read row count") + // process result + count, err := res.ReadInt() + if err != nil { + return r, errors.Wrapf(err, "failed to read row count") } - res.Rows = make([][]interface{}, rowCount) - for i := 0; i < int(rowCount); i++ { - res.Rows[i], err = r.ReadObjects(res.FieldCount) + r.Keys = make([]interface{}, 0, int(count)) + r.Values = make([]interface{}, 0, int(count)) + // read data + for i := 0; i < int(count); i++ { + key, err := res.ReadObject() if err != nil { - return res, errors.Wrapf(err, "failed to read row with index %d", i) + return r, errors.Wrapf(err, "failed to read key with index %d", i) } + value, err := res.ReadObject() + if err != nil { + return r, errors.Wrapf(err, "failed to read value with index %d", i) + } + r.Keys = append(r.Keys, key) + r.Values = append(r.Values, value) } - if err = r.ReadPrimitives(&res.HasMore); err != nil { - return res, errors.Wrapf(err, "failed to read response data") + if r.HasMore, err = res.ReadBool(); err != nil { + return r, errors.Wrapf(err, "failed to read has more flag") } - return res, nil + return r, nil } -func (c *client) QuerySQLFieldsRaw(cache string, binary bool, data QuerySQLFieldsData) (Response, error) { - o := c.Prepare(OpQuerySQLFields) +func (c *client) QuerySQLFieldsRaw(cache string, binary bool, data QuerySQLFieldsData) (*ResponseOperation, error) { + // request and response + req := NewRequestOperation(OpQuerySQLFields) + res := NewResponseOperation(req.UID) - var r Response + var err error - if err := o.WritePrimitives(HashCode(cache), binary); err != nil { - return r, errors.Wrapf(err, "failed to write request data") + // set parameters + if err := req.WriteInt(HashCode(cache)); err != nil { + return nil, errors.Wrapf(err, "failed to write cache name") + } + if err := req.WriteBool(binary); err != nil { + return nil, errors.Wrapf(err, "failed to write binary flag") } - if len(data.Schema) > 0 { - if err := o.WriteObjects(data.Schema); err != nil { - return r, errors.Wrapf(err, "failed to write schema for the query") + if err := req.WriteOString(data.Schema); err != nil { + return nil, errors.Wrapf(err, "failed to write schema for the query") } } else { - if err := o.WriteObjects(nil); err != nil { - return r, errors.Wrapf(err, "failed to write nil for schema for the query") + if err := req.WriteNull(); err != nil { + return nil, errors.Wrapf(err, "failed to write nil for schema for the query") } } - - if err := o.WritePrimitives(int32(data.PageSize), int32(data.MaxRows), data.Query); err != nil { - return r, errors.Wrapf(err, "failed to write request data") + if err = req.WriteInt(int32(data.PageSize)); err != nil { + return nil, errors.Wrapf(err, "failed to write page size") + } + if err = req.WriteInt(int32(data.MaxRows)); err != nil { + return nil, errors.Wrapf(err, "failed to write max rows") + } + if err = req.WriteOString(data.Query); err != nil { + return nil, errors.Wrapf(err, "failed to write query") } var l int32 @@ -308,80 +316,176 @@ func (c *client) QuerySQLFieldsRaw(cache string, binary bool, data QuerySQLField l = int32(len(data.QueryArgs)) } // write args - if err := o.WritePrimitives(l); err != nil { - return r, errors.Wrapf(err, "failed to write query arg count") + if err = req.WriteInt(l); err != nil { + return nil, errors.Wrapf(err, "failed to write query arg count") } if l > 0 { for i, v := range data.QueryArgs { - if err := o.WriteObjects(v); err != nil { - return r, errors.Wrapf(err, "failed to write query arg with index %d", i) + if err = req.WriteObject(v); err != nil { + return nil, errors.Wrapf(err, "failed to write query arg with index %d", i) } } } - if err := o.WritePrimitives(data.StatementType, data.DistributedJoins, data.LocalQuery, data.ReplicatedOnly, - data.EnforceJoinOrder, data.Collocated, data.LazyQuery, data.Timeout, data.IncludeFieldNames); err != nil { - return r, errors.Wrapf(err, "failed to write request data") + if err = req.WriteByte(data.StatementType); err != nil { + return nil, errors.Wrapf(err, "failed to write statement type") + } + if err = req.WriteBool(data.DistributedJoins); err != nil { + return nil, errors.Wrapf(err, "failed to write distributed joins flag") + } + if err = req.WriteBool(data.LocalQuery); err != nil { + return nil, errors.Wrapf(err, "failed to write local query flag") + } + if err = req.WriteBool(data.ReplicatedOnly); err != nil { + return nil, errors.Wrapf(err, "failed to write replicated only flag") + } + if err = req.WriteBool(data.EnforceJoinOrder); err != nil { + return nil, errors.Wrapf(err, "failed to write enforce join order flag") + } + if err = req.WriteBool(data.Collocated); err != nil { + return nil, errors.Wrapf(err, "failed to write collocated flag") + } + if err = req.WriteBool(data.LazyQuery); err != nil { + return nil, errors.Wrapf(err, "failed to write lazy query flag") + } + if err = req.WriteLong(data.Timeout); err != nil { + return nil, errors.Wrapf(err, "failed to write timeout") + } + if err = req.WriteBool(data.IncludeFieldNames); err != nil { + return nil, errors.Wrapf(err, "failed to write include field names flag") } - // execute - r, err := c.Call(o) - if err != nil { - return r, errors.Wrapf(err, "failed to execute OP_QUERY_SQL_FIELDS operation") + // execute operation + if err = c.Do(req, res); err != nil { + return nil, errors.Wrapf(err, "failed to execute OP_QUERY_SQL_FIELDS operation") } - if err = r.CheckStatus(); err != nil { - return r, err + if err = res.CheckStatus(); err != nil { + return nil, err } - return r, nil + return res, nil } -// QuerySQLFieldsCursorGetPage retrieves the next query result page by cursor id from QuerySQLFields. -func (c *client) QuerySQLFieldsCursorGetPage(id int64, fieldCount int) (QuerySQLFieldsPage, error) { - var res QuerySQLFieldsPage +// QuerySQLFields performs SQL fields query. +func (c *client) QuerySQLFields(cache string, binary bool, data QuerySQLFieldsData) (QuerySQLFieldsResult, error) { + var r QuerySQLFieldsResult - r, err := c.QuerySQLFieldsCursorGetPageRaw(id) + res, err := c.QuerySQLFieldsRaw(cache, binary, data) + if err != nil { + return r, err + } + + // read field names + if r.ID, err = res.ReadLong(); err != nil { + return r, errors.Wrapf(err, "failed to read cursor ID") + } + fieldCount, err := res.ReadInt() if err != nil { - return res, errors.Wrapf(err, "failed to execute OP_QUERY_SQL_FIELDS_CURSOR_GET_PAGE operation") + return r, errors.Wrapf(err, "failed to read field count") + } + r.FieldCount = int(fieldCount) + if data.IncludeFieldNames { + r.Fields = make([]string, 0, fieldCount) + for i := 0; i < r.FieldCount; i++ { + var s string + if s, err = res.ReadOString(); err != nil { + return r, errors.Wrapf(err, "failed to read field name with index %d", i) + } + r.Fields = append(r.Fields, s) + } + } else { + r.Fields = []string{} } // read data - var rowCount int32 - if err = r.ReadPrimitives(&rowCount); err != nil { - return res, errors.Wrapf(err, "failed to read row count") + rowCount, err := res.ReadInt() + if err != nil { + return r, errors.Wrapf(err, "failed to read row count") } - res.Rows = make([][]interface{}, rowCount) + r.Rows = make([][]interface{}, rowCount) for i := 0; i < int(rowCount); i++ { - res.Rows[i], err = r.ReadObjects(fieldCount) - if err != nil { - return res, errors.Wrapf(err, "failed to read row with index %d", i) + r.Rows[i] = make([]interface{}, r.FieldCount) + for j := 0; j < r.FieldCount; j++ { + r.Rows[i][j], err = res.ReadObject() + if err != nil { + return r, errors.Wrapf(err, "failed to read value (row with index %d, column with index %d", i, j) + } } } - if err = r.ReadPrimitives(&res.HasMore); err != nil { - return res, errors.Wrapf(err, "failed to read response data") + if r.HasMore, err = res.ReadBool(); err != nil { + return r, errors.Wrapf(err, "failed to read has more flag") + } + + return r, nil +} + +func (c *client) QuerySQLFieldsCursorGetPageRaw(id int64) (*ResponseOperation, error) { + // request and response + req := NewRequestOperation(OpQuerySQLFieldsCursorGetPage) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteLong(id); err != nil { + return nil, errors.Wrapf(err, "failed to write cursor id") + } + + // execute operation + if err := c.Do(req, res); err != nil { + return nil, errors.Wrapf(err, "failed to execute OP_QUERY_SQL_FIELDS_CURSOR_GET_PAGE operation") + } + if err := res.CheckStatus(); err != nil { + return nil, err } return res, nil } -func (c *client) QuerySQLFieldsCursorGetPageRaw(id int64) (Response, error) { - r, err := c.Exec(OpQuerySQLFieldsCursorGetPage, id) +// QuerySQLFieldsCursorGetPage retrieves the next query result page by cursor id from QuerySQLFields. +func (c *client) QuerySQLFieldsCursorGetPage(id int64, fieldCount int) (QuerySQLFieldsPage, error) { + var r QuerySQLFieldsPage + + res, err := c.QuerySQLFieldsCursorGetPageRaw(id) if err != nil { - return r, errors.Wrapf(err, "failed to execute OP_QUERY_SQL_FIELDS_CURSOR_GET_PAGE operation") - } - if err = r.CheckStatus(); err != nil { return r, err } + // read data + rowCount, err := res.ReadInt() + if err != nil { + return r, errors.Wrapf(err, "failed to read row count") + } + r.Rows = make([][]interface{}, rowCount) + for i := 0; i < int(rowCount); i++ { + r.Rows[i] = make([]interface{}, fieldCount) + for j := 0; j < int(fieldCount); j++ { + r.Rows[i][j], err = res.ReadObject() + if err != nil { + return r, errors.Wrapf(err, "failed to read value (row with index %d, column with index %d", i, j) + } + } + } + if r.HasMore, err = res.ReadBool(); err != nil { + return r, errors.Wrapf(err, "failed to read has more flag") + } + return r, nil } // ResourceClose closes a resource, such as query cursor. func (c *client) ResourceClose(id int64) error { - r, err := c.Exec(OpResourceClose, id) - if err != nil { + // request and response + req := NewRequestOperation(OpResourceClose) + res := NewResponseOperation(req.UID) + + // set parameters + if err := req.WriteLong(id); err != nil { + return errors.Wrapf(err, "failed to write cursor id") + } + + // execute operation + if err := c.Do(req, res); err != nil { return errors.Wrapf(err, "failed to execute OP_RESOURCE_CLOSE operation") } - return r.CheckStatus() + return res.CheckStatus() } diff --git a/binary/v1/client-sql-and-scan-queries_test.go b/binary/v1/client-sql-and-scan-queries_test.go index f5c8531..f84d4ff 100644 --- a/binary/v1/client-sql-and-scan-queries_test.go +++ b/binary/v1/client-sql-and-scan-queries_test.go @@ -1,99 +1,18 @@ package ignite import ( + "context" "reflect" "testing" "time" ) -/* -import ( - "reflect" - "testing" - "time" -) - -func Test_client_QuerySQL(t *testing.T) { - // get test data - c, err := getTestClient() - if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) - } - defer c.Close() - // insert test values - tm := time.Date(2018, 4, 3, 14, 25, 32, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) - _, err = c.QuerySQLFields("TestDB1", false, QuerySQLFieldsData{ - PageSize: 10, - Query: "INSERT INTO Organization(_key, name, foundDateTime) VALUES" + - "(?, ?, ?)," + - "(?, ?, ?)," + - "(?, ?, ?)", - QueryArgs: []interface{}{ - int64(1), "Org 1", tm, - int64(2), "Org 2", tm, - int64(3), "Org 3", tm}, - }) - if err != nil { - t.Fatalf("failed to insert test data: %s", err.Error()) - } - defer c.CacheRemoveAll("TestDB1", false) - - type args struct { - cache string - binary bool - data QuerySQLData - status *int32 - } - tests := []struct { - name string - c *client - args args - want QuerySQLResult - wantErr bool - }{ - { - name: "success test 1", - c: c, - args: args{ - cache: "TestDB1", - data: QuerySQLData{ - Table: "Organization", - Query: `SELECT * FROM Organization ORDER BY name ASC`, - PageSize: 10, - Timeout: 10000, - }, - }, - want: QuerySQLResult{ - Keys: []interface{}{}, - Values: []interface{}{}, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.c.QuerySQL(tt.args.cache, tt.args.binary, tt.args.data) - if (err != nil) != tt.wantErr { - t.Errorf("client.QuerySQL() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got.Keys, tt.want.Keys) || - !reflect.DeepEqual(got.Values, tt.want.Values) || - !reflect.DeepEqual(got.HasMore, tt.want.HasMore) { - t.Errorf("client.QuerySQL() = %v, want %v", got, tt.want) - } - }) - } -} -*/ - func Test_client_QuerySQLFields(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - defer c.CacheRemoveAll("TestDB1", false) tm := time.Date(2018, 4, 3, 14, 25, 32, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) type args struct { @@ -103,16 +22,16 @@ func Test_client_QuerySQLFields(t *testing.T) { } tests := []struct { name string - c *client + c Client args args want QuerySQLFieldsResult wantErr bool }{ { - name: "success test 1", + name: "1", c: c, args: args{ - cache: "TestDB1", + cache: "QuerySQLFields", data: QuerySQLFieldsData{ PageSize: 10, Query: "INSERT INTO Organization(_key, name, foundDateTime) VALUES" + @@ -127,16 +46,17 @@ func Test_client_QuerySQLFields(t *testing.T) { }, want: QuerySQLFieldsResult{ FieldCount: 1, + Fields: []string{}, QuerySQLFieldsPage: QuerySQLFieldsPage{ Rows: [][]interface{}{[]interface{}{int64(3)}}, }, }, }, { - name: "success test 2", + name: "2", c: c, args: args{ - cache: "TestDB1", + cache: "QuerySQLFields", data: QuerySQLFieldsData{ PageSize: 10, Query: "INSERT INTO Person(_key, orgId, firstName, lastName, resume, salary) VALUES" + @@ -155,16 +75,17 @@ func Test_client_QuerySQLFields(t *testing.T) { }, want: QuerySQLFieldsResult{ FieldCount: 1, + Fields: []string{}, QuerySQLFieldsPage: QuerySQLFieldsPage{ Rows: [][]interface{}{[]interface{}{int64(5)}}, }, }, }, { - name: "success test 3", + name: "3", c: c, args: args{ - cache: "TestDB1", + cache: "QuerySQLFields", data: QuerySQLFieldsData{ PageSize: 10, Query: "SELECT " + @@ -201,27 +122,23 @@ func Test_client_QuerySQLFields(t *testing.T) { t.Errorf("client.QuerySQLFields() error = %v, wantErr %v", err, tt.wantErr) return } - if (tt.want.Fields != nil && !reflect.DeepEqual(got.Fields, tt.want.Fields)) || - (tt.want.Rows != nil && !reflect.DeepEqual(got.Rows, tt.want.Rows)) || - !reflect.DeepEqual(got.FieldCount, tt.want.FieldCount) || - !reflect.DeepEqual(got.HasMore, tt.want.HasMore) { - t.Errorf("client.QuerySQLFields() = %v, want %v", got, tt.want) + if !(reflect.DeepEqual(got.FieldCount, tt.want.FieldCount) && reflect.DeepEqual(got.Fields, tt.want.Fields) && + reflect.DeepEqual(got.QuerySQLFieldsPage, tt.want.QuerySQLFieldsPage)) { + t.Errorf("client.QuerySQLFields() = %#v, want %#v", got, tt.want) } }) } } func Test_client_QuerySQLFieldsCursorGetPage(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - // insert test values tm := time.Date(2018, 4, 3, 14, 25, 32, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) - _, err = c.QuerySQLFields("TestDB1", false, QuerySQLFieldsData{ + _, err = c.QuerySQLFields("QuerySQLFieldsCursorGetPage", false, QuerySQLFieldsData{ PageSize: 10, Query: "INSERT INTO Organization(_key, name, foundDateTime) VALUES" + "(?, ?, ?)," + @@ -233,17 +150,16 @@ func Test_client_QuerySQLFieldsCursorGetPage(t *testing.T) { int64(3), "Org 3", tm}, }) if err != nil { - t.Fatalf("failed to insert test data: %s", err.Error()) + t.Fatal(err) } - defer c.CacheRemoveAll("TestDB1", false) // select test values - res, err := c.QuerySQLFields("TestDB1", false, QuerySQLFieldsData{ + r, err := c.QuerySQLFields("QuerySQLFieldsCursorGetPage", false, QuerySQLFieldsData{ PageSize: 2, Query: "SELECT name, foundDateTime FROM Organization ORDER BY name ASC", Timeout: 10000, }) if err != nil { - t.Fatalf("failed to select test data: %s", err.Error()) + t.Fatal(err) } type args struct { @@ -252,17 +168,17 @@ func Test_client_QuerySQLFieldsCursorGetPage(t *testing.T) { } tests := []struct { name string - c *client + c Client args args want QuerySQLFieldsPage wantErr bool }{ { - name: "success test 1", + name: "1", c: c, args: args{ - id: res.ID, - fieldCount: res.FieldCount, + id: r.ID, + fieldCount: r.FieldCount, }, want: QuerySQLFieldsPage{ Rows: [][]interface{}{ @@ -287,16 +203,14 @@ func Test_client_QuerySQLFieldsCursorGetPage(t *testing.T) { } func Test_client_ResourceClose(t *testing.T) { - // get test data - c, err := getTestClient() + c, err := Connect(context.Background(), testConnInfo) if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) + t.Fatal(err) } defer c.Close() - // insert test values tm := time.Date(2018, 4, 3, 14, 25, 32, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) - _, err = c.QuerySQLFields("TestDB1", false, QuerySQLFieldsData{ + _, err = c.QuerySQLFields("ResourceClose", false, QuerySQLFieldsData{ PageSize: 10, Query: "INSERT INTO Organization(_key, name, foundDateTime) VALUES" + "(?, ?, ?)," + @@ -308,17 +222,16 @@ func Test_client_ResourceClose(t *testing.T) { int64(3), "Org 3", tm}, }) if err != nil { - t.Fatalf("failed to insert test data: %s", err.Error()) + t.Fatal(err) } - defer c.CacheRemoveAll("TestDB1", false) // select test values - res, err := c.QuerySQLFields("TestDB1", false, QuerySQLFieldsData{ + r, err := c.QuerySQLFields("ResourceClose", false, QuerySQLFieldsData{ PageSize: 2, Query: "SELECT name, foundDateTime FROM Organization ORDER BY name ASC", Timeout: 10000, }) if err != nil { - t.Fatalf("failed to select test data: %s", err.Error()) + t.Fatal(err) } type args struct { @@ -326,15 +239,15 @@ func Test_client_ResourceClose(t *testing.T) { } tests := []struct { name string - c *client + c Client args args wantErr bool }{ { - name: "success test 1", + name: "1", c: c, args: args{ - id: res.ID, + id: r.ID, }, }, } diff --git a/binary/v1/client.go b/binary/v1/client.go index 3355bff..5e5cdd0 100644 --- a/binary/v1/client.go +++ b/binary/v1/client.go @@ -1,10 +1,8 @@ package ignite import ( - "bytes" "context" - "io" - "math/rand" + "fmt" "net" "runtime" "strings" @@ -14,59 +12,21 @@ import ( "github.com/amsokol/ignite-go-client/debug" ) +// ConnInfo contains connections parameters +type ConnInfo struct { + Network, Host string + Port int + Major, Minor, Patch int +} + // Client is interface to communicate with Apache Ignite cluster. -// Client is not thread-safe. +// Client is thread safe. type Client interface { - // Exec executes request with primitives. - // code - code of operation. - // uid - request ID. - // primitives - primitives to send. - // Returns: - // Response, nil in case of success. - // Empty Response, error object in case of error. - Exec(code int16, primitives ...interface{}) (Response, error) - - // Prepare returns Operation. - // Arguments: - // code - code of operation. - // uid - request ID. - // Operation is not thread-safe. - Prepare(code int16) Operation - - // Call executes Operation - // Arguments: - // o - Operation to execute. - // Returns: - // Response, nil in case of success. - // Empty Response, error object in case of error. - Call(o Operation) (Response, error) - - // begin starts request by writing data directly to connection with server. - // Arguments: - // length - length in bytes of request message. - // code - code of operation. - // uid - request ID. - // Returns: - // nil in case of success. - // error object in case of error. - begin(length int32, code int16, uid int64) error - - // write writes primitives directly to connection with server. - // Arguments: - // primitives - primitives to write. - // Returns: - // nil in case of success. - // error object in case of error. - write(primitives ...interface{}) error + // Connected return true if connection to the cluster is active + Connected() bool - // commit finishes the request and returns response from server. - // Returns: - // Response, nil in case of success. - // Empty Response, error object in case of error. - commit() (Response, error) - - // IsConnected return true if connection to the cluster is active - IsConnected() bool + // Do sends request and receives response + Do(req Request, res Response) error // Close closes connection. // Returns: @@ -115,14 +75,6 @@ type Client interface { // See for details: // https://apacheignite.readme.io/docs/binary-client-protocol-key-value-operations - // CachePut puts a value with a given key to cache (overwriting existing value if any). - // https://apacheignite.readme.io/docs/binary-client-protocol-key-value-operations#section-op_cache_put - CachePut(cache string, binary bool, key interface{}, value interface{}) error - - // CachePutAll puts a value with a given key to cache (overwriting existing value if any). - // https://apacheignite.readme.io/docs/binary-client-protocol-key-value-operations#section-op_cache_put_all - CachePutAll(cache string, binary bool, data map[interface{}]interface{}) error - // CacheGet retrieves a value from cache by key. // https://apacheignite.readme.io/docs/binary-client-protocol-key-value-operations#section-op_cache_get CacheGet(cache string, binary bool, key interface{}) (interface{}, error) @@ -131,6 +83,14 @@ type Client interface { // https://apacheignite.readme.io/docs/binary-client-protocol-key-value-operations#section-op_cache_get_all CacheGetAll(cache string, binary bool, keys []interface{}) (map[interface{}]interface{}, error) + // CachePut puts a value with a given key to cache (overwriting existing value if any). + // https://apacheignite.readme.io/docs/binary-client-protocol-key-value-operations#section-op_cache_put + CachePut(cache string, binary bool, key interface{}, value interface{}) error + + // CachePutAll puts a value with a given key to cache (overwriting existing value if any). + // https://apacheignite.readme.io/docs/binary-client-protocol-key-value-operations#section-op_cache_put_all + CachePutAll(cache string, binary bool, data map[interface{}]interface{}) error + // CacheContainsKey returns a value indicating whether given key is present in cache. // https://apacheignite.readme.io/docs/binary-client-protocol-key-value-operations#section-op_cache_contains_key CacheContainsKey(cache string, binary bool, key interface{}) (bool, error) @@ -192,7 +152,7 @@ type Client interface { // CacheGetSize gets the number of entries in cache. // https://apacheignite.readme.io/docs/binary-client-protocol-key-value-operations#section-op_cache_get_size - CacheGetSize(cache string, binary bool, count int, modes []byte) (int64, error) + CacheGetSize(cache string, binary bool, modes []byte) (int64, error) // CacheRemoveKeys removes entries with given keys, notifying listeners and cache writers. // https://apacheignite.readme.io/docs/binary-client-protocol-key-value-operations#section-op_cache_remove_keys @@ -214,22 +174,22 @@ type Client interface { // https://apacheignite.readme.io/docs/binary-client-protocol-sql-operations#section-op_query_sql_cursor_get_page QuerySQLCursorGetPage(id int64) (QuerySQLPage, error) + // QuerySQLFieldsRaw is equal to QuerySQLFields but return raw Response object. + // Used for SQL driver to reduce memory allocations. + QuerySQLFieldsRaw(cache string, binary bool, data QuerySQLFieldsData) (*ResponseOperation, error) + // QuerySQLFields performs SQL fields query. // https://apacheignite.readme.io/docs/binary-client-protocol-sql-operations#section-op_query_sql_fields QuerySQLFields(cache string, binary bool, data QuerySQLFieldsData) (QuerySQLFieldsResult, error) - // QuerySQLFieldsRaw is equal to QuerySQLFields but return raw Response object. + // QuerySQLFieldsCursorGetPageRaw is equal to QuerySQLFieldsCursorGetPage but return raw Response object. // Used for SQL driver to reduce memory allocations. - QuerySQLFieldsRaw(cache string, binary bool, data QuerySQLFieldsData) (Response, error) + QuerySQLFieldsCursorGetPageRaw(id int64) (*ResponseOperation, error) // QuerySQLFieldsCursorGetPage retrieves the next query result page by cursor id from QuerySQLFields. // https://apacheignite.readme.io/docs/binary-client-protocol-sql-operations#section-op_query_sql_fields_cursor_get_page QuerySQLFieldsCursorGetPage(id int64, fieldCount int) (QuerySQLFieldsPage, error) - // QuerySQLFieldsCursorGetPageRaw is equal to QuerySQLFieldsCursorGetPage but return raw Response object. - // Used for SQL driver to reduce memory allocations. - QuerySQLFieldsCursorGetPageRaw(id int64) (Response, error) - // ResourceClose closes a resource, such as query cursor. // https://apacheignite.readme.io/docs/binary-client-protocol-sql-operations#section-op_resource_close ResourceClose(id int64) error @@ -238,206 +198,83 @@ type Client interface { type client struct { debugID string conn net.Conn - lock sync.Mutex + mutex *sync.Mutex Client } // IsConnected return true if connection to the cluster is active -func (c *client) IsConnected() bool { +func (c *client) Connected() bool { return c.conn != nil } -// Close closes connection. -// Returns: -// nil in case of success. -// error object in case of error. -func (c *client) Close() error { - if c.conn != nil { - defer func() { c.conn = nil }() - return c.conn.Close() - } - return nil -} - -// Exec executes request with primitives. -// code - code of operation. -// uid - request ID. -// primitives - primitives to send. -// Returns: -// Response, nil in case of success. -// Empty Response, error object in case of error. -func (c *client) Exec(code OperationCode, primitives ...interface{}) (Response, error) { - // prepare operation - o := c.Prepare(code) - - // write data - if err := o.WritePrimitives(primitives...); err != nil { - return Response{}, errors.Wrapf(err, "failed to write operation request primitives") - } - - // execute operation - return c.Call(o) -} - -// Prepare returns Operation. -// Arguments: -// code - code of operation. -// uid - request ID. -// Operation is not thread-safe. -func (c *client) Prepare(code OperationCode) Operation { - return Operation{Code: code, UID: rand.Int63(), Prefix: &bytes.Buffer{}, Data: &bytes.Buffer{}} -} +// Do sends request and receives response +func (c *client) Do(req Request, res Response) error { + c.mutex.Lock() + defer c.mutex.Unlock() -// Call executes Operation -// Arguments: -// o - Operation to execute. -// Returns: -// Response, nil in case of success. -// Empty Response, error object in case of error -func (c *client) Call(o Operation) (Response, error) { - c.lock.Lock() - defer c.lock.Unlock() - - // send request header - if err := c.begin(int32(2+8+o.Prefix.Len()+o.Data.Len()), o.Code, o.UID); err != nil { - return Response{}, errors.Wrapf(err, "failed to send request header") - } - if o.Prefix.Len() > 0 { - // send request prefix of body - if err := c.write(o.Prefix.Bytes()); err != nil { - return Response{}, errors.Wrapf(err, "failed to send request prefix of body") - } - } - if o.Data.Len() > 0 { - // send request body - if err := c.write(o.Data.Bytes()); err != nil { - return Response{}, errors.Wrapf(err, "failed to send request body") - } + // send request + if _, err := req.WriteTo(c.conn); err != nil { + return errors.Wrapf(err, "failed to send request to server") } - // receive server response - r, err := c.commit() - if err != nil { - return r, errors.Wrapf(err, "failed to receive response from server") - } - if r.UID != o.UID { - return r, errors.Errorf("invalid response id (expected %d, but received %d)", o.UID, r.UID) - } + // receive response + _, err := res.ReadFrom(c.conn) - return r, nil + return err } -// begin starts request by writing data directly to connection with server. -// Arguments: -// length - length in bytes of request message. -// code - code of operation. -// uid - request ID. -// Returns: -// nil in case of success. -// error object in case of error. -func (c *client) begin(length int32, code int16, uid int64) error { - return writePrimitives(c.conn, length, code, uid) -} - -// write writes primitives directly to connection with server. -// Arguments: -// primitives - primitives to write. +// Close closes connection. // Returns: // nil in case of success. // error object in case of error. -func (c *client) write(primitives ...interface{}) error { - return writePrimitives(c.conn, primitives...) -} - -// commit finishes the request and returns response from server. -// Returns: -// Response, nil in case of success. -// Empty Response, error object in case of error. -func (c *client) commit() (Response, error) { - var r Response - - // read response message length - if err := readPrimitives(c.conn, &r.Len); err != nil { - return r, errors.Wrapf(err, "failed to read response message length") - } - - // read response message - b := make([]byte, r.Len, r.Len) - if err := readPrimitives(c.conn, &b); err != nil { - return r, errors.Wrapf(err, "failed to read response message") - } - r.Data = bytes.NewReader(b) - - // read response header - if err := r.ReadPrimitives(&r.UID, &r.Status); err != nil { - return r, errors.Wrapf(err, "failed to read response header") - } - - if r.Status != errors.StatusSuccess { - // Response status - if err := r.ReadPrimitives(&r.Message); err != nil { - return r, errors.Wrapf(err, "failed to read error message") - } +func (c *client) Close() error { + if c.Connected() { + defer func() { c.conn = nil }() + return c.conn.Close() } - return r, nil + return nil } -// NewClient connects to the Apache Ignite cluster -func NewClient(ctx context.Context, network, address string, major, minor, patch int16) (Client, error) { +// Connect connects to the Apache Ignite cluster +// Returns: client +func Connect(ctx context.Context, ci ConnInfo) (Client, error) { + address := fmt.Sprintf("%s:%d", ci.Host, ci.Port) + + // connect d := net.Dialer{} - conn, err := d.DialContext(ctx, network, address) + conn, err := d.DialContext(ctx, ci.Network, address) if err != nil { return nil, errors.Wrapf(err, "failed to open connection") } - if err = handshake(conn, major, minor, patch); err != nil { - conn.Close() - return nil, errors.Wrapf(err, "failed to make handshake") - } - c := &client{conn: conn, debugID: strings.Join([]string{"network=", network, "', address='", address, "'"}, "")} + + c := &client{conn: conn, debugID: strings.Join([]string{"network=", ci.Network, "', address='", address, "'"}, ""), + mutex: &sync.Mutex{}} runtime.SetFinalizer(c, clientFinalizer) - return c, nil -} -// handshake - besides socket connection, the thin client protocol requires a connection handshake to ensure -// that client and server versions are compatible. Note that handshake must be the first message -// after connection establishment. -func handshake(rw io.ReadWriter, major int16, minor int16, patch int16) error { - // Send handshake request - if err := writePrimitives(rw, - // Message length - int32(8), - // Handshake operation - byte(1), - // Protocol version, e.g. 1,0,0 - major, minor, patch, - // Client code - byte(2), - ); err != nil { - return errors.Wrapf(err, "failed to send handshake request") - } + // request and response + req := NewRequestHandshake(ci.Major, ci.Minor, ci.Patch) + res := &ResponseHandshake{} - // Receive handshake response - var length int32 - var res byte - if err := readPrimitives(rw, &length, &res); err != nil { - return errors.Wrapf(err, "failed to read handshake response (length and result)") + // make handshake + if err = c.Do(req, res); err != nil { + c.Close() + return nil, errors.Wrapf(err, "failed to make handshake") } - if res != 1 { - var msg string - if err := readPrimitives(rw, &major, &minor, &patch, &msg); err != nil { - return errors.Wrapf(err, "failed to read handshake response (supported protocol version and error message)") - } - return errors.Errorf("handshake failed, code=%d, message='%s', supported protocol version is v%d.%d.%d", - res, msg, major, minor, patch) + + if !res.Success { + c.Close() + return nil, errors.Errorf("handshake failed: %s, server supported protocol version is v%d.%d.%d", + res.Message, res.Major, res.Minor, res.Patch) } - return nil + // return connected client + return c, nil } -// clientFinalizer is memory leak spy +// clientFinalizer is resource leak spy func clientFinalizer(c *client) { - if c.IsConnected() { + if c.Connected() { debug.ResourceLeakLogger.Printf("client \"%s\" is not closed", c.debugID) c.Close() } diff --git a/binary/v1/client_test.go b/binary/v1/client_test.go index 067f719..8deecab 100644 --- a/binary/v1/client_test.go +++ b/binary/v1/client_test.go @@ -2,129 +2,59 @@ package ignite import ( "context" - "io" "testing" ) -func TestNewClient(t *testing.T) { +func TestConnect(t *testing.T) { type args struct { - ctx context.Context - network string - address string - major int16 - minor int16 - patch int16 + ctx context.Context + ci ConnInfo } tests := []struct { name string args args + want Client wantErr bool }{ { - name: "success test", + name: "1", args: args{ - ctx: context.Background(), - network: "tcp", - address: "127.0.0.1:10800", - major: 1, - minor: 0, - patch: 0, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := NewClient(tt.args.ctx, tt.args.network, tt.args.address, - tt.args.major, tt.args.minor, tt.args.patch) - if (err != nil) != tt.wantErr { - t.Errorf("NewClient100() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != nil { - got.Close() - } - }) - } -} - -func Test_client_Close(t *testing.T) { - // get test data - c, err := getTestClient() - if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) - } - - tests := []struct { - name string - c *client - wantErr bool - }{ - { - name: "success test 1", - c: c, - }, - { - name: "success test 2", - c: c, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := tt.c.Close(); (err != nil) != tt.wantErr { - t.Errorf("client.Close() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - -func Test_handshake(t *testing.T) { - // get test data - c1, err := getTestConnection() - if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) - } - defer c1.Close() - c2, err := getTestConnection() - if err != nil { - t.Fatalf("failed to open test connection: %s", err.Error()) - } - defer c2.Close() - - type args struct { - rw io.ReadWriter - major int16 - minor int16 - patch int16 - } - tests := []struct { - name string - args args - wantErr bool - }{ - { - name: "success test", - args: args{ - rw: c1, - major: 1, - minor: 0, - patch: 0, + ctx: context.Background(), + ci: ConnInfo{ + Network: "tcp", + Host: "localhost", + Port: 10800, + Major: 1, + Minor: 0, + Patch: 0, + }, }, }, { - name: "failed test", + name: "2", args: args{ - rw: c2, - major: 1000, - minor: 0, - patch: 0, + ctx: context.Background(), + ci: ConnInfo{ + Network: "tcp", + Host: "localhost", + Port: 10800, + Major: 999, + Minor: 0, + Patch: 0, + }, }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := handshake(tt.args.rw, tt.args.major, tt.args.minor, tt.args.patch); (err != nil) != tt.wantErr { - t.Errorf("handshake() error = %v, wantErr %v", err, tt.wantErr) + got, err := Connect(tt.args.ctx, tt.args.ci) + if (err != nil) != tt.wantErr { + t.Errorf("Connect() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != nil { + got.Close() } }) } diff --git a/binary/v1/hash-code_test.go b/binary/v1/hash-code_test.go index 173f764..8f041db 100644 --- a/binary/v1/hash-code_test.go +++ b/binary/v1/hash-code_test.go @@ -14,9 +14,9 @@ func Test_HashCode(t *testing.T) { { name: "success test", args: args{ - s: "T", + s: "test string", }, - want: 84, + want: -318923937, }, } for _, tt := range tests { diff --git a/binary/v1/helper_test.go b/binary/v1/helper_test.go deleted file mode 100644 index 7540725..0000000 --- a/binary/v1/helper_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package ignite - -import ( - "context" - "fmt" - "net" -) - -func getTestClient() (*client, error) { - c, err := NewClient(context.Background(), "tcp", "127.0.0.1:10800", 1, 0, 0) - if err != nil { - return nil, fmt.Errorf("failed to create client: %s", err.Error()) - } - original, _ := c.(*client) - return original, nil -} - -func getTestConnection() (net.Conn, error) { - return net.Dial("tcp", "127.0.0.1:10800") -} diff --git a/binary/v1/io.go b/binary/v1/io.go deleted file mode 100644 index b2fa129..0000000 --- a/binary/v1/io.go +++ /dev/null @@ -1,620 +0,0 @@ -package ignite - -import ( - "encoding/binary" - "fmt" - "io" - "reflect" - "time" - - "github.com/google/uuid" -) - -const ( - // Supported standard types and their type codes are as follows: - typeByte byte = 1 - typeShort byte = 2 - typeInt byte = 3 - typeLong byte = 4 - typeFloat byte = 5 - typeDouble byte = 6 - typeChar byte = 7 - typeBool byte = 8 - typeString byte = 9 - typeUUID byte = 10 - /* - Apache.Ignite.Core.Impl.Binary.BinaryUtils - - bytes[0] = jBytes[4]; // a1 - bytes[1] = jBytes[5]; // a2 - bytes[2] = jBytes[6]; // a3 - bytes[3] = jBytes[7]; // a4 - - bytes[4] = jBytes[2]; // b1 - bytes[5] = jBytes[3]; // b2 - - bytes[6] = jBytes[0]; // c1 - bytes[7] = jBytes[1]; // c2 - - bytes[8] = jBytes[15]; // d - bytes[9] = jBytes[14]; // e - bytes[10] = jBytes[13]; // f - bytes[11] = jBytes[12]; // g - bytes[12] = jBytes[11]; // h - bytes[13] = jBytes[10]; // i - bytes[14] = jBytes[9]; // j - bytes[15] = jBytes[8]; // k - */ - typeDate byte = 11 - typeByteArray byte = 12 - typeShortArray byte = 13 - typeIntArray byte = 14 - typeLongArray byte = 15 - typeFloatArray byte = 16 - typeDoubleArray byte = 17 - typeCharArray byte = 18 - typeBoolArray byte = 19 - typeStringArray byte = 20 - typeUUIDArray byte = 21 - typeDateArray byte = 22 - // TODO: Object array = 23 - // TODO: Collection = 24 - // TODO: Map = 25 - // TODO: Enum = 28 - // TODO: Enum Array = 29 - // TODO: Decimal = 30 - // TODO: Decimal Array = 31 - typeTimestamp byte = 33 - // TODO: Timestamp Array = 34 - typeTime byte = 36 - // TODO: Time Array = 37 - typeNULL byte = 101 -) - -// Char is UTF32 symbol type -type Char rune - -// Date is Unix time, the number of MILLISECONDS elapsed -// since January 1, 1970 UTC. -type Date int64 - -// Time is Apache Ignite Time type -type Time int64 - -// NativeTime2Time converts Golang time.Time to Apache Ignite Time -func NativeTime2Time(t time.Time) Time { - t1 := t.UTC() - t2 := time.Date(1970, 1, 1, t1.Hour(), t1.Minute(), t1.Second(), t1.Nanosecond(), time.UTC) - t3 := t2.Unix() * 1000 - t3 += int64(t2.Nanosecond()) / int64(time.Millisecond) - return Time(t3) -} - -// Time2NativeTime converts Apache Ignite Time to Golang time.Time -func Time2NativeTime(t Time) time.Time { - return time.Unix(int64(t)/1000, (int64(t)%1000)*int64(time.Millisecond)).UTC() -} - -// NativeTime2Date converts Golang time.Time to Apache Ignite Date -func NativeTime2Date(t time.Time) Date { - t1 := t.UTC() - t2 := t1.Unix() * 1000 - t2 += int64(t1.Nanosecond()) / int64(time.Millisecond) - return Date(t2) -} - -// Date2NativeTime converts Apache Ignite Date to Golang time.Time -func Date2NativeTime(t Date) time.Time { - return time.Unix(int64(t)/1000, (int64(t)%1000)*int64(time.Millisecond)).UTC() -} - -func writePrimitives(w io.Writer, primitives ...interface{}) error { - var err error - for i, d := range primitives { - switch v := d.(type) { - case string: - err = writeObject(w, v) - default: - err = binary.Write(w, binary.LittleEndian, v) - } - if err != nil { - err = fmt.Errorf("failed to write primitive with index %d, reason: %s", i, err.Error()) - break - } - } - return err -} - -func writeObjects(w io.Writer, objects ...interface{}) error { - var err error - for i, d := range objects { - err = writeObject(w, d) - if err != nil { - err = fmt.Errorf("failed to write object with index %d, reason: %s", i, err.Error()) - break - } - } - return err -} - -func writeObject(w io.Writer, object interface{}) error { - if object == nil { - return binary.Write(w, binary.LittleEndian, typeNULL) - } - var err error - switch v := object.(type) { - case byte: - if err = binary.Write(w, binary.LittleEndian, typeByte); err == nil { - err = binary.Write(w, binary.LittleEndian, v) - } - case int16: - if err = binary.Write(w, binary.LittleEndian, typeShort); err == nil { - err = binary.Write(w, binary.LittleEndian, v) - } - case int32: - if err = binary.Write(w, binary.LittleEndian, typeInt); err == nil { - err = binary.Write(w, binary.LittleEndian, v) - } - case int64: - if err = binary.Write(w, binary.LittleEndian, typeLong); err == nil { - err = binary.Write(w, binary.LittleEndian, v) - } - case float32: - if err = binary.Write(w, binary.LittleEndian, typeFloat); err == nil { - err = binary.Write(w, binary.LittleEndian, v) - } - case float64: - if err = binary.Write(w, binary.LittleEndian, typeDouble); err == nil { - err = binary.Write(w, binary.LittleEndian, v) - } - case bool: - if err = binary.Write(w, binary.LittleEndian, typeBool); err == nil { - err = binary.Write(w, binary.LittleEndian, v) - } - case Char: - if err = binary.Write(w, binary.LittleEndian, typeChar); err == nil { - err = binary.Write(w, binary.LittleEndian, int16(v)) - } - case string: - if err = binary.Write(w, binary.LittleEndian, typeString); err == nil { - s := []byte(v) - // String data length - length := int32(len(s)) - if err = binary.Write(w, binary.LittleEndian, length); err == nil { - if length > 0 { - // String data - err = binary.Write(w, binary.LittleEndian, s) - } - } - } - case uuid.UUID: - if err = binary.Write(w, binary.LittleEndian, typeUUID); err == nil { - err = binary.Write(w, binary.LittleEndian, v) - } - case Date: - if err = binary.Write(w, binary.LittleEndian, typeDate); err == nil { - err = binary.Write(w, binary.LittleEndian, int64(v)) - } - case []byte: - if err = binary.Write(w, binary.LittleEndian, typeByteArray); err == nil { - if err = binary.Write(w, binary.LittleEndian, int32(len(v))); err == nil { - err = binary.Write(w, binary.LittleEndian, v) - } - } - case []int16: - if err = binary.Write(w, binary.LittleEndian, typeShortArray); err == nil { - if err = binary.Write(w, binary.LittleEndian, int32(len(v))); err == nil { - err = binary.Write(w, binary.LittleEndian, v) - } - } - case []int32: - if err = binary.Write(w, binary.LittleEndian, typeIntArray); err == nil { - if err = binary.Write(w, binary.LittleEndian, int32(len(v))); err == nil { - err = binary.Write(w, binary.LittleEndian, v) - } - } - case []int64: - if err = binary.Write(w, binary.LittleEndian, typeLongArray); err == nil { - if err = binary.Write(w, binary.LittleEndian, int32(len(v))); err == nil { - err = binary.Write(w, binary.LittleEndian, v) - } - } - case []float32: - if err = binary.Write(w, binary.LittleEndian, typeFloatArray); err == nil { - if err = binary.Write(w, binary.LittleEndian, int32(len(v))); err == nil { - err = binary.Write(w, binary.LittleEndian, v) - } - } - case []float64: - if err = binary.Write(w, binary.LittleEndian, typeDoubleArray); err == nil { - if err = binary.Write(w, binary.LittleEndian, int32(len(v))); err == nil { - err = binary.Write(w, binary.LittleEndian, v) - } - } - case []bool: - if err = binary.Write(w, binary.LittleEndian, typeBoolArray); err == nil { - if err = binary.Write(w, binary.LittleEndian, int32(len(v))); err == nil { - err = binary.Write(w, binary.LittleEndian, v) - } - } - case []Char: - if err = binary.Write(w, binary.LittleEndian, typeCharArray); err == nil { - if err = binary.Write(w, binary.LittleEndian, int32(len(v))); err == nil { - for _, c := range v { - if err = binary.Write(w, binary.LittleEndian, int16(c)); err != nil { - break - } - } - } - } - case []string: - if err = binary.Write(w, binary.LittleEndian, typeStringArray); err == nil { - if err = binary.Write(w, binary.LittleEndian, int32(len(v))); err == nil { - for _, s := range v { - if err = writeString(w, s); err != nil { - break - } - } - } - } - /* TODO: contact Apache Ignite team for support - case []uuid.UUID: - if err = binary.Write(w, binary.LittleEndian, typeUUIDArray); err == nil { - if err = binary.Write(w, binary.LittleEndian, int32(len(v))); err == nil { - for _, d := range v { - if err = binary.Write(w, binary.LittleEndian, typeUUID); err == nil { - break - } - if err = binary.Write(w, binary.LittleEndian, d); err != nil { - break - } - } - } - } - */ - /* TODO: contact Apache Ignite team for support - case []Date: - if err = binary.Write(w, binary.LittleEndian, typeDateArray); err == nil { - if err = binary.Write(w, binary.LittleEndian, int32(len(v))); err == nil { - for _, d := range v { - if err = binary.Write(w, binary.LittleEndian, int64(d)); err != nil { - break - } - } - } - } - */ - case time.Time: - if err = binary.Write(w, binary.LittleEndian, typeTimestamp); err == nil { - high := int64(v.Unix() * 1000) // Unix time in milliseconds - low := v.Nanosecond() - high += int64(low / int(time.Millisecond)) - low = low % int(time.Millisecond) - if err = binary.Write(w, binary.LittleEndian, int64(high)); err == nil { - err = binary.Write(w, binary.LittleEndian, int32(low)) - } - } - case Time: - if err = binary.Write(w, binary.LittleEndian, typeTime); err == nil { - err = binary.Write(w, binary.LittleEndian, int64(v)) - } - default: - err = fmt.Errorf("unsupported object type: %s", reflect.TypeOf(v).Name()) - } - return err -} - -func writeString(w io.Writer, str string) error { - var err error - // Type code - if err = binary.Write(w, binary.LittleEndian, byte(typeString)); err == nil { - s := []byte(str) - // String data length - length := int32(len(s)) - if err = binary.Write(w, binary.LittleEndian, length); err == nil { - if length > 0 { - // String data - err = binary.Write(w, binary.LittleEndian, s) - } - } - } - return err -} - -func readPrimitives(r io.Reader, data ...interface{}) error { - var err error - for i, d := range data { - switch v := d.(type) { - case *string: - err = readString(r, true, v) - default: - err = binary.Read(r, binary.LittleEndian, v) - } - if err != nil { - err = fmt.Errorf("failed to read data with index %d, reason: %s", i, err.Error()) - break - } - } - return err -} - -func readString(r io.Reader, withCode bool, data *string) error { - if withCode { - // Type code - var code byte - if err := binary.Read(r, binary.LittleEndian, &code); err != nil { - return fmt.Errorf("failed to read string code: %s", err.Error()) - } - if code == typeNULL { - *data = "" - return nil - } - if code != typeString { - return fmt.Errorf("invalid type code for 'String' expecting %d, but received %d", typeString, code) - } - } - var err error - var length int32 - if err = binary.Read(r, binary.LittleEndian, &length); err == nil { - if length > 0 { - s := make([]byte, length) - // String data - if err = binary.Read(r, binary.LittleEndian, &s); err == nil { - *data = string(s) - } - } else { - *data = "" - } - } - return err -} - -func readObjects(r io.Reader, count int) ([]interface{}, error) { - objects := make([]interface{}, 0, count) - for i := 0; i < count; i++ { - o, err := readObject(r) - if err != nil { - return nil, fmt.Errorf("failed to read object with index %d, reason: %s", i, err.Error()) - } - objects = append(objects, o) - } - return objects, nil -} - -func readObject(r io.Reader) (interface{}, error) { - var code byte - if err := readPrimitives(r, &code); err != nil { - return nil, fmt.Errorf("failed to read object type code: %s", err.Error()) - } - - switch code { - case typeByte: - var o byte - if err := readPrimitives(r, &o); err != nil { - return nil, fmt.Errorf("failed to read byte object value: %s", err.Error()) - } - return o, nil - case typeShort: - var o int16 - if err := readPrimitives(r, &o); err != nil { - return nil, fmt.Errorf("failed to read short object value: %s", err.Error()) - } - return o, nil - case typeInt: - var o int32 - if err := readPrimitives(r, &o); err != nil { - return nil, fmt.Errorf("failed to read int object value: %s", err.Error()) - } - return o, nil - case typeLong: - var o int64 - if err := readPrimitives(r, &o); err != nil { - return nil, fmt.Errorf("failed to read long object value: %s", err.Error()) - } - return o, nil - case typeFloat: - var o float32 - if err := readPrimitives(r, &o); err != nil { - return nil, fmt.Errorf("failed to read float object value: %s", err.Error()) - } - return o, nil - case typeDouble: - var o float64 - if err := readPrimitives(r, &o); err != nil { - return nil, fmt.Errorf("failed to read double object value: %s", err.Error()) - } - return o, nil - case typeChar: - var o int16 - if err := readPrimitives(r, &o); err != nil { - return nil, fmt.Errorf("failed to read char object value: %s", err.Error()) - } - return Char(o), nil - case typeBool: - var o bool - if err := readPrimitives(r, &o); err != nil { - return nil, fmt.Errorf("failed to read bool object value: %s", err.Error()) - } - return o, nil - case typeString: - var o string - if err := readString(r, false, &o); err != nil { - return nil, fmt.Errorf("failed to read string object value: %s", err.Error()) - } - return o, nil - case typeUUID: - var o uuid.UUID - if err := readPrimitives(r, &o); err != nil { - return nil, fmt.Errorf("failed to read UUID object value: %s", err.Error()) - } - return o, nil - case typeDate: - var o int64 - if err := readPrimitives(r, &o); err != nil { - return nil, fmt.Errorf("failed to read Date object value: %s", err.Error()) - } - return Date(o), nil - case typeByteArray: - var length int32 - if err := readPrimitives(r, &length); err != nil { - return nil, fmt.Errorf("failed to read byte array object length: %s", err.Error()) - } - o := make([]byte, length, length) - if length > 0 { - if err := readPrimitives(r, &o); err != nil { - return nil, fmt.Errorf("failed to read byte array object value: %s", err.Error()) - } - } - return o, nil - case typeShortArray: - var length int32 - if err := readPrimitives(r, &length); err != nil { - return nil, fmt.Errorf("failed to read short array object length: %s", err.Error()) - } - o := make([]int16, length, length) - if length > 0 { - if err := readPrimitives(r, &o); err != nil { - return nil, fmt.Errorf("failed to read short array object value: %s", err.Error()) - } - } - return o, nil - case typeIntArray: - var length int32 - if err := readPrimitives(r, &length); err != nil { - return nil, fmt.Errorf("failed to read int array object length: %s", err.Error()) - } - o := make([]int32, length, length) - if length > 0 { - if err := readPrimitives(r, &o); err != nil { - return nil, fmt.Errorf("failed to read int array object value: %s", err.Error()) - } - } - return o, nil - case typeLongArray: - var length int32 - if err := readPrimitives(r, &length); err != nil { - return nil, fmt.Errorf("failed to read long array object length: %s", err.Error()) - } - o := make([]int64, length, length) - if length > 0 { - if err := readPrimitives(r, &o); err != nil { - return nil, fmt.Errorf("failed to read long array object value: %s", err.Error()) - } - } - return o, nil - case typeFloatArray: - var length int32 - if err := readPrimitives(r, &length); err != nil { - return nil, fmt.Errorf("failed to read float array object length: %s", err.Error()) - } - o := make([]float32, length, length) - if length > 0 { - if err := readPrimitives(r, &o); err != nil { - return nil, fmt.Errorf("failed to read float array object value: %s", err.Error()) - } - } - return o, nil - case typeDoubleArray: - var length int32 - if err := readPrimitives(r, &length); err != nil { - return nil, fmt.Errorf("failed to read double array object length: %s", err.Error()) - } - o := make([]float64, length, length) - if length > 0 { - if err := readPrimitives(r, &o); err != nil { - return nil, fmt.Errorf("failed to read double array object value: %s", err.Error()) - } - } - return o, nil - case typeCharArray: - var length int32 - if err := readPrimitives(r, &length); err != nil { - return nil, fmt.Errorf("failed to read char array object length: %s", err.Error()) - } - o := make([]Char, 0, length) - for i := 0; i < int(length); i++ { - var v int16 - if err := readPrimitives(r, &v); err != nil { - return nil, fmt.Errorf("failed to read char array object value: %s", err.Error()) - } - o = append(o, Char(v)) - } - return o, nil - case typeBoolArray: - var length int32 - if err := readPrimitives(r, &length); err != nil { - return nil, fmt.Errorf("failed to read bool array object length: %s", err.Error()) - } - o := make([]bool, length, length) - if length > 0 { - if err := readPrimitives(r, &o); err != nil { - return nil, fmt.Errorf("failed to read bool array object value: %s", err.Error()) - } - } - return o, nil - case typeStringArray: - var length int32 - if err := readPrimitives(r, &length); err != nil { - return nil, fmt.Errorf("failed to read string array object length: %s", err.Error()) - } - o := make([]string, 0, length) - for i := 0; i < int(length); i++ { - var s string - if err := readPrimitives(r, &s); err != nil { - return nil, fmt.Errorf("failed to read string array object value: %s", err.Error()) - } - o = append(o, s) - } - return o, nil - case typeUUIDArray: - var length int32 - if err := readPrimitives(r, &length); err != nil { - return nil, fmt.Errorf("failed to read UUID array object length: %s", err.Error()) - } - o := make([]uuid.UUID, 0, length) - for i := 0; i < int(length); i++ { - var v uuid.UUID - if err := readPrimitives(r, &v); err != nil { - return nil, fmt.Errorf("failed to read UUID array object value: %s", err.Error()) - } - o = append(o, v) - } - return o, nil - case typeDateArray: - var length int32 - if err := readPrimitives(r, &length); err != nil { - return nil, fmt.Errorf("failed to read Date array object length: %s", err.Error()) - } - o := make([]Date, 0, length) - for i := 0; i < int(length); i++ { - var v int64 - if err := readPrimitives(r, &v); err != nil { - return nil, fmt.Errorf("failed to read UUID array object value: %s", err.Error()) - } - o = append(o, Date(v)) - } - return o, nil - case typeTimestamp: - var high int64 - if err := readPrimitives(r, &high); err != nil { - return nil, fmt.Errorf("failed to read Timestamp high value: %s", err.Error()) - } - var low int32 - if err := readPrimitives(r, &low); err != nil { - return nil, fmt.Errorf("failed to read Timestamp low value: %s", err.Error()) - } - low = int32((high%1000)*int64(time.Millisecond)) + low - high = high / 1000 - return time.Unix(high, int64(low)).UTC(), nil - case typeTime: - var o int64 - if err := readPrimitives(r, &o); err != nil { - return nil, fmt.Errorf("failed to read Time object value: %s", err.Error()) - } - return Time(o), nil - case typeNULL: - return nil, nil - default: - return nil, fmt.Errorf("type with code %d is not supported", code) - } -} diff --git a/binary/v1/operation.go b/binary/v1/operations.go similarity index 84% rename from binary/v1/operation.go rename to binary/v1/operations.go index e518a5b..3ae2fd3 100644 --- a/binary/v1/operation.go +++ b/binary/v1/operations.go @@ -1,12 +1,5 @@ package ignite -import ( - "bytes" -) - -// OperationCode is operation code type -type OperationCode = int16 - const ( // Cache Configuration @@ -92,27 +85,3 @@ const ( // OpResourceClose closes a resource, such as query cursor. OpResourceClose = 0 ) - -// Operation allows to prepare operation to execute -type Operation struct { - Code OperationCode - UID int64 - Prefix *bytes.Buffer - Data *bytes.Buffer -} - -// WritePrefix writes promitives to the prefix buffer. -// Prefix is sent to the server before data. -func (o *Operation) WritePrefix(data ...interface{}) error { - return writePrimitives(o.Prefix, data...) -} - -// WritePrimitives writes primitives to the data buffer -func (o *Operation) WritePrimitives(data ...interface{}) error { - return writePrimitives(o.Data, data...) -} - -// WriteObjects writes objects to the data buffer -func (o *Operation) WriteObjects(data ...interface{}) error { - return writeObjects(o.Data, data...) -} diff --git a/binary/v1/request-cache-create-with-configuration.go b/binary/v1/request-cache-create-with-configuration.go new file mode 100644 index 0000000..cb820e9 --- /dev/null +++ b/binary/v1/request-cache-create-with-configuration.go @@ -0,0 +1,55 @@ +package ignite + +import ( + "encoding/binary" + "io" + + "github.com/amsokol/ignite-go-client/binary/errors" +) + +// RequestCacheCreateWithConfiguration is struct to store operation request +type RequestCacheCreateWithConfiguration struct { + Count int16 + + RequestOperation +} + +// WriteTo is function to write handshake request data to io.Writer. +// Returns written bytes. +func (r *RequestCacheCreateWithConfiguration) WriteTo(w io.Writer) (int64, error) { + // write payload length + l := int32(2 + 8 + 4 + 2 + r.payload.Len()) + if err := binary.Write(w, binary.LittleEndian, &l); err != nil { + return 0, errors.Wrapf(err, "failed to write operation request length") + } + + // write operation code + if err := binary.Write(w, binary.LittleEndian, &r.Code); err != nil { + return 0, errors.Wrapf(err, "failed to write operation code") + } + + // write operation request id + if err := binary.Write(w, binary.LittleEndian, &r.UID); err != nil { + return 0, errors.Wrapf(err, "failed to write operation request id") + } + + // write data length + l = int32(r.payload.Len()) + if err := binary.Write(w, binary.LittleEndian, &l); err != nil { + return 0, errors.Wrapf(err, "failed to write data length") + } + + // write params count + if err := binary.Write(w, binary.LittleEndian, &r.Count); err != nil { + return 0, errors.Wrapf(err, "failed to write params count") + } + + // write payload + n, err := r.payload.WriteTo(w) + return 4 + 2 + 8 + 4 + 2 + n, err +} + +// NewRequestCacheCreateWithConfiguration creates new handshake request object +func NewRequestCacheCreateWithConfiguration(code int16) *RequestCacheCreateWithConfiguration { + return &RequestCacheCreateWithConfiguration{RequestOperation: *NewRequestOperation(code)} +} diff --git a/binary/v1/request-handshake.go b/binary/v1/request-handshake.go new file mode 100644 index 0000000..658dc54 --- /dev/null +++ b/binary/v1/request-handshake.go @@ -0,0 +1,50 @@ +package ignite + +import ( + "encoding/binary" + "io" + + "github.com/amsokol/ignite-go-client/binary/errors" +) + +// RequestHandshake is struct handshake request +type RequestHandshake struct { + major, minor, patch int + + request +} + +// WriteTo is function to write handshake request data to io.Writer. +// Returns written bytes. +func (r *RequestHandshake) WriteTo(w io.Writer) (int64, error) { + if err := r.WriteByte(1); err != nil { + return 0, errors.Wrapf(err, "failed to write handshake code") + } + if err := r.WriteShort(int16(r.major)); err != nil { + return 0, errors.Wrapf(err, "failed to write handshake version major") + } + if err := r.WriteShort(int16(r.minor)); err != nil { + return 0, errors.Wrapf(err, "failed to write handshake version minor") + } + if err := r.WriteShort(int16(r.patch)); err != nil { + return 0, errors.Wrapf(err, "failed to write handshake version patch") + } + if err := r.WriteByte(2); err != nil { + return 0, errors.Wrapf(err, "failed to write handshake client code") + } + + // write payload length + l := int32(r.payload.Len()) + if err := binary.Write(w, binary.LittleEndian, &l); err != nil { + return 0, errors.Wrapf(err, "failed to write handshake request length") + } + // write payload + n, err := r.payload.WriteTo(w) + return 4 + n, err +} + +// NewRequestHandshake creates new handshake request object +func NewRequestHandshake(major, minor, patch int) *RequestHandshake { + return &RequestHandshake{request: newRequest(), + major: major, minor: minor, patch: patch} +} diff --git a/binary/v1/request-handshake_test.go b/binary/v1/request-handshake_test.go new file mode 100644 index 0000000..c3d106a --- /dev/null +++ b/binary/v1/request-handshake_test.go @@ -0,0 +1,40 @@ +package ignite + +import ( + "bytes" + "reflect" + "testing" +) + +func TestRequestHandshake_WriteTo(t *testing.T) { + tests := []struct { + name string + r *RequestHandshake + want int64 + wantW []byte + wantErr bool + }{ + { + name: "1", + r: NewRequestHandshake(1, 0, 0), + want: 4 + 8, + wantW: []byte{0x8, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + w := &bytes.Buffer{} + got, err := tt.r.WriteTo(w) + if (err != nil) != tt.wantErr { + t.Errorf("RequestHandshake.WriteTo() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("RequestHandshake.WriteTo() = %v, want %v", got, tt.want) + } + if gotW := w.Bytes(); !reflect.DeepEqual(w.Bytes(), tt.wantW) { + t.Errorf("RequestHandshake.WriteTo() = %#v, want %#v", gotW, tt.wantW) + } + }) + } +} diff --git a/binary/v1/request-operation.go b/binary/v1/request-operation.go new file mode 100644 index 0000000..12d8eb0 --- /dev/null +++ b/binary/v1/request-operation.go @@ -0,0 +1,46 @@ +package ignite + +import ( + "encoding/binary" + "io" + "math/rand" + + "github.com/amsokol/ignite-go-client/binary/errors" +) + +// RequestOperation is struct to store operation request +type RequestOperation struct { + Code int16 + UID int64 + + request +} + +// WriteTo is function to write operation request data to io.Writer. +// Returns written bytes. +func (r *RequestOperation) WriteTo(w io.Writer) (int64, error) { + // write payload length + l := int32(2 + 8 + r.payload.Len()) + if err := binary.Write(w, binary.LittleEndian, &l); err != nil { + return 0, errors.Wrapf(err, "failed to write operation request length") + } + + // write operation code + if err := binary.Write(w, binary.LittleEndian, &r.Code); err != nil { + return 0, errors.Wrapf(err, "failed to write operation code") + } + + // write operation request id + if err := binary.Write(w, binary.LittleEndian, &r.UID); err != nil { + return 0, errors.Wrapf(err, "failed to write operation request id") + } + + // write payload + n, err := r.payload.WriteTo(w) + return 4 + 2 + 8 + n, err +} + +// NewRequestOperation creates new handshake request object +func NewRequestOperation(code int16) *RequestOperation { + return &RequestOperation{request: newRequest(), Code: code, UID: rand.Int63()} +} diff --git a/binary/v1/request-operation_test.go b/binary/v1/request-operation_test.go new file mode 100644 index 0000000..7f5aa51 --- /dev/null +++ b/binary/v1/request-operation_test.go @@ -0,0 +1,34 @@ +package ignite + +import ( + "bytes" + "testing" +) + +func TestRequestOperation_WriteTo(t *testing.T) { + tests := []struct { + name string + r *RequestOperation + want int64 + wantErr bool + }{ + { + name: "1", + r: NewRequestOperation(0), + want: 4 + 2 + 8, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + w := &bytes.Buffer{} + got, err := tt.r.WriteTo(w) + if (err != nil) != tt.wantErr { + t.Errorf("RequestOperation.WriteTo() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("RequestOperation.WriteTo() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/binary/v1/request.go b/binary/v1/request.go new file mode 100644 index 0000000..fb1f21a --- /dev/null +++ b/binary/v1/request.go @@ -0,0 +1,438 @@ +package ignite + +import ( + "bytes" + "encoding/binary" + "io" + "reflect" + "time" + + "github.com/google/uuid" + + "github.com/amsokol/ignite-go-client/binary/errors" +) + +// Request is interface of base message request functionality +type Request interface { + // WriteTo is function to write request data to io.Writer. + // Returns written bytes. + WriteTo(w io.Writer) (int64, error) +} + +// request is struct is implementing base message request functionality +type request struct { + payload *bytes.Buffer + + Request +} + +// WriteByte writes "byte" value +func (r *request) WriteByte(v byte) error { + return binary.Write(r.payload, binary.LittleEndian, v) +} + +// WriteOByte writes "byte" object value +func (r *request) WriteOByte(v byte) error { + if err := r.WriteByte(typeByte); err != nil { + return err + } + return r.WriteByte(v) +} + +// WriteShort writes "short" value +func (r *request) WriteShort(v int16) error { + return binary.Write(r.payload, binary.LittleEndian, v) +} + +// WriteOShort writes "short" object value +func (r *request) WriteOShort(v int16) error { + if err := r.WriteByte(typeShort); err != nil { + return err + } + return r.WriteShort(v) +} + +// WriteInt writes "int" value +func (r *request) WriteInt(v int32) error { + return binary.Write(r.payload, binary.LittleEndian, v) +} + +// WriteOInt writes "int" object value +func (r *request) WriteOInt(v int32) error { + if err := r.WriteByte(typeInt); err != nil { + return err + } + return r.WriteInt(v) +} + +// WriteLong writes "long" value +func (r *request) WriteLong(v int64) error { + return binary.Write(r.payload, binary.LittleEndian, v) +} + +// WriteOLong writes "long" object value +func (r *request) WriteOLong(v int64) error { + if err := r.WriteByte(typeLong); err != nil { + return err + } + return r.WriteLong(v) +} + +// WriteFloat writes "float" value +func (r *request) WriteFloat(v float32) error { + return binary.Write(r.payload, binary.LittleEndian, v) +} + +// WriteOFloat writes "float" object value +func (r *request) WriteOFloat(v float32) error { + if err := r.WriteByte(typeFloat); err != nil { + return err + } + return r.WriteFloat(v) +} + +// WriteDouble writes "double" value +func (r *request) WriteDouble(v float64) error { + return binary.Write(r.payload, binary.LittleEndian, v) +} + +// WriteODouble writes "double" object value +func (r *request) WriteODouble(v float64) error { + if err := r.WriteByte(typeDouble); err != nil { + return err + } + return r.WriteDouble(v) +} + +// WriteChar writes "char" value +func (r *request) WriteChar(v Char) error { + return binary.Write(r.payload, binary.LittleEndian, int16(v)) +} + +// WriteOChar writes "char" object value +func (r *request) WriteOChar(v Char) error { + if err := r.WriteByte(typeChar); err != nil { + return err + } + return r.WriteChar(v) +} + +// WriteBool writes "bool" value +func (r *request) WriteBool(v bool) error { + return binary.Write(r.payload, binary.LittleEndian, v) +} + +// WriteOBool writes "bool" object value +func (r *request) WriteOBool(v bool) error { + if err := r.WriteByte(typeBool); err != nil { + return err + } + return r.WriteBool(v) +} + +// WriteOString writes "string" object value +// String is marshalling as object in all cases. +func (r *request) WriteOString(v string) error { + if err := r.WriteByte(typeString); err != nil { + return err + } + s := []byte(v) + if err := r.WriteInt(int32(len(s))); err != nil { + return err + } + return binary.Write(r.payload, binary.LittleEndian, s) +} + +// WriteOUUID writes "UUID" object value +// UUID is marshaled as object in all cases. +func (r *request) WriteOUUID(v uuid.UUID) error { + if err := r.WriteByte(typeUUID); err != nil { + return err + } + return binary.Write(r.payload, binary.LittleEndian, v) +} + +// WriteODate writes "Date" object value +func (r *request) WriteODate(v Date) error { + if err := r.WriteByte(typeDate); err != nil { + return err + } + return r.WriteLong(int64(v)) +} + +// WriteOArrayBytes writes "byte" array object value +func (r *request) WriteOArrayBytes(v []byte) error { + if err := r.WriteByte(typeByteArray); err != nil { + return err + } + if err := r.WriteInt(int32(len(v))); err != nil { + return err + } + return binary.Write(r.payload, binary.LittleEndian, v) +} + +// WriteOArrayShorts writes "short" array object value +func (r *request) WriteOArrayShorts(v []int16) error { + if err := r.WriteByte(typeShortArray); err != nil { + return err + } + if err := r.WriteInt(int32(len(v))); err != nil { + return err + } + return binary.Write(r.payload, binary.LittleEndian, v) +} + +// WriteOArrayInts writes "int" array object value +func (r *request) WriteOArrayInts(v []int32) error { + if err := r.WriteByte(typeIntArray); err != nil { + return err + } + if err := r.WriteInt(int32(len(v))); err != nil { + return err + } + return binary.Write(r.payload, binary.LittleEndian, v) +} + +// WriteOArrayLongs writes "long" array object value +func (r *request) WriteOArrayLongs(v []int64) error { + if err := r.WriteByte(typeLongArray); err != nil { + return err + } + if err := r.WriteInt(int32(len(v))); err != nil { + return err + } + return binary.Write(r.payload, binary.LittleEndian, v) +} + +// WriteOArrayFloats writes "float" array object value +func (r *request) WriteOArrayFloats(v []float32) error { + if err := r.WriteByte(typeFloatArray); err != nil { + return err + } + if err := r.WriteInt(int32(len(v))); err != nil { + return err + } + return binary.Write(r.payload, binary.LittleEndian, v) +} + +// WriteOArrayDoubles writes "double" array object value +func (r *request) WriteOArrayDoubles(v []float64) error { + if err := r.WriteByte(typeDoubleArray); err != nil { + return err + } + if err := r.WriteInt(int32(len(v))); err != nil { + return err + } + return binary.Write(r.payload, binary.LittleEndian, v) +} + +// WriteOArrayChars writes "char" array object value +func (r *request) WriteOArrayChars(v []Char) error { + if err := r.WriteByte(typeCharArray); err != nil { + return err + } + if err := r.WriteInt(int32(len(v))); err != nil { + return err + } + for _, c := range v { + if err := r.WriteChar(c); err != nil { + return err + } + } + return nil +} + +// WriteOArrayBools writes "Bool" array object value +func (r *request) WriteOArrayBools(v []bool) error { + if err := r.WriteByte(typeBoolArray); err != nil { + return err + } + if err := r.WriteInt(int32(len(v))); err != nil { + return err + } + return binary.Write(r.payload, binary.LittleEndian, v) +} + +// WriteOArrayOStrings writes "String" array object value +func (r *request) WriteOArrayOStrings(v []string) error { + if err := r.WriteByte(typeStringArray); err != nil { + return err + } + if err := r.WriteInt(int32(len(v))); err != nil { + return err + } + for _, s := range v { + if err := r.WriteOString(s); err != nil { + return err + } + } + return nil +} + +// WriteOArrayOUUIDs writes "UUID" array object value +func (r *request) WriteOArrayOUUIDs(v []uuid.UUID) error { + if err := r.WriteByte(typeUUIDArray); err != nil { + return err + } + if err := r.WriteInt(int32(len(v))); err != nil { + return err + } + for _, d := range v { + if err := r.WriteOUUID(d); err != nil { + return err + } + } + return nil +} + +// WriteOArrayODates writes "Date" array object value +func (r *request) WriteOArrayODates(v []Date) error { + if err := r.WriteByte(typeDateArray); err != nil { + return err + } + if err := r.WriteInt(int32(len(v))); err != nil { + return err + } + for _, d := range v { + if err := r.WriteODate(d); err != nil { + return err + } + } + return nil +} + +// WriteOTimestamp writes "Timestamp" object value +// Timestamp is marshaled as object in all cases. +func (r *request) WriteOTimestamp(v time.Time) error { + if err := r.WriteByte(typeTimestamp); err != nil { + return err + } + high := int64(v.Unix() * 1000) // Unix time in milliseconds + low := v.Nanosecond() + high += int64(low / int(time.Millisecond)) + low = low % int(time.Millisecond) + if err := r.WriteLong(high); err != nil { + return err + } + return r.WriteInt(int32(low)) +} + +// WriteOArrayOTimestamps writes "Timestamp" array object value +func (r *request) WriteOArrayOTimestamps(v []time.Time) error { + if err := r.WriteByte(typeTimestampArray); err != nil { + return err + } + if err := r.WriteInt(int32(len(v))); err != nil { + return err + } + for _, d := range v { + if err := r.WriteOTimestamp(d); err != nil { + return err + } + } + return nil +} + +// WriteOTime writes "Time" object value +// Time is marshaled as object in all cases. +func (r *request) WriteOTime(v Time) error { + if err := r.WriteByte(typeTime); err != nil { + return err + } + return r.WriteLong(int64(v)) +} + +// WriteOArrayOTimes writes "Time" array object value +func (r *request) WriteOArrayOTimes(v []Time) error { + if err := r.WriteByte(typeTimeArray); err != nil { + return err + } + if err := r.WriteInt(int32(len(v))); err != nil { + return err + } + for _, d := range v { + if err := r.WriteOTime(d); err != nil { + return err + } + } + return nil +} + +// WriteNull writes NULL +func (r *request) WriteNull() error { + return r.WriteByte(typeNULL) +} + +func (r *request) WriteObject(o interface{}) error { + if o == nil { + return r.WriteNull() + } + + switch v := o.(type) { + case byte: + return r.WriteOByte(v) + case int16: + return r.WriteOShort(v) + case int32: + return r.WriteOInt(v) + case int64: + return r.WriteOLong(v) + case float32: + return r.WriteOFloat(v) + case float64: + return r.WriteODouble(v) + case Char: + return r.WriteOChar(v) + case bool: + return r.WriteOBool(v) + case string: + return r.WriteOString(v) + case uuid.UUID: + return r.WriteOUUID(v) + case Date: + return r.WriteODate(v) + case []byte: + return r.WriteOArrayBytes(v) + case []int16: + return r.WriteOArrayShorts(v) + case []int32: + return r.WriteOArrayInts(v) + case []int64: + return r.WriteOArrayLongs(v) + case []float32: + return r.WriteOArrayFloats(v) + case []float64: + return r.WriteOArrayDoubles(v) + case []Char: + return r.WriteOArrayChars(v) + case []bool: + return r.WriteOArrayBools(v) + case []string: + return r.WriteOArrayOStrings(v) + case []Date: + return r.WriteOArrayODates(v) + case []uuid.UUID: + return r.WriteOArrayOUUIDs(v) + case time.Time: + return r.WriteOTimestamp(v) + case []time.Time: + return r.WriteOArrayOTimestamps(v) + case Time: + return r.WriteOTime(v) + case []Time: + return r.WriteOArrayOTimes(v) + default: + return errors.Errorf("unsupported object type: %s", reflect.TypeOf(v).Name()) + } +} + +// WriteTo is function to write request data to io.Writer. +// Returns written bytes. +func (r *request) WriteTo(w io.Writer) (int64, error) { + return r.payload.WriteTo(w) +} + +// newRequest is private constructor for request +func newRequest() request { + return request{payload: &bytes.Buffer{}} +} diff --git a/binary/v1/request_test.go b/binary/v1/request_test.go new file mode 100644 index 0000000..c04f463 --- /dev/null +++ b/binary/v1/request_test.go @@ -0,0 +1,1589 @@ +package ignite + +import ( + "bytes" + "reflect" + "testing" + "time" + + "github.com/google/uuid" +) + +func Test_request_WriteByte(t *testing.T) { + r := &request{payload: &bytes.Buffer{}} + + type args struct { + v byte + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r, + args: args{ + v: 123, + }, + want: []byte{123}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteByte(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteByte() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteByte() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOByte(t *testing.T) { + r := &request{payload: &bytes.Buffer{}} + + type args struct { + v byte + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r, + args: args{ + v: 123, + }, + want: []byte{1, 123}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOByte(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOByte() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOByte() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteShort(t *testing.T) { + r := &request{payload: &bytes.Buffer{}} + + type args struct { + v int16 + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r, + args: args{ + v: 12345, + }, + want: []byte{0x39, 0x30}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteShort(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteShort() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteShort() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOShort(t *testing.T) { + r := &request{payload: &bytes.Buffer{}} + + type args struct { + v int16 + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r, + args: args{ + v: 12345, + }, + want: []byte{2, 0x39, 0x30}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOShort(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOShort() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOShort() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteInt(t *testing.T) { + r := &request{payload: &bytes.Buffer{}} + + type args struct { + v int32 + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r, + args: args{ + v: 1234567890, + }, + want: []byte{0xD2, 0x02, 0x96, 0x49}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteInt(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteInt() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteInt() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOInt(t *testing.T) { + r := &request{payload: &bytes.Buffer{}} + + type args struct { + v int32 + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r, + args: args{ + v: 1234567890, + }, + want: []byte{3, 0xD2, 0x02, 0x96, 0x49}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOInt(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOInt() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOInt() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteLong(t *testing.T) { + r := &request{payload: &bytes.Buffer{}} + + type args struct { + v int64 + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r, + args: args{ + v: 1234567890123456789, + }, + want: []byte{0x15, 0x81, 0xE9, 0x7D, 0xF4, 0x10, 0x22, 0x11}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteLong(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteLong() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteLong() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOLong(t *testing.T) { + r := &request{payload: &bytes.Buffer{}} + + type args struct { + v int64 + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r, + args: args{ + v: 1234567890123456789, + }, + want: []byte{4, 0x15, 0x81, 0xE9, 0x7D, 0xF4, 0x10, 0x22, 0x11}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOLong(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOLong() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOLong() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteFloat(t *testing.T) { + r := &request{payload: &bytes.Buffer{}} + + type args struct { + v float32 + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r, + args: args{ + v: 123456.789, + }, + want: []byte{0x65, 0x20, 0xf1, 0x47}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteFloat(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteFloat() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteFloat() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOFloat(t *testing.T) { + r := &request{payload: &bytes.Buffer{}} + + type args struct { + v float32 + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r, + args: args{ + v: 123456.789, + }, + want: []byte{5, 0x65, 0x20, 0xf1, 0x47}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOFloat(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOFloat() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOFloat() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteDouble(t *testing.T) { + r := &request{payload: &bytes.Buffer{}} + + type args struct { + v float64 + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r, + args: args{ + v: 123456789.12345, + }, + want: []byte{0xad, 0x69, 0x7e, 0x54, 0x34, 0x6f, 0x9d, 0x41}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteDouble(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteDouble() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteDouble() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteODouble(t *testing.T) { + r := &request{payload: &bytes.Buffer{}} + + type args struct { + v float64 + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r, + args: args{ + v: 123456789.12345, + }, + want: []byte{6, 0xad, 0x69, 0x7e, 0x54, 0x34, 0x6f, 0x9d, 0x41}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteODouble(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteODouble() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteODouble() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteChar(t *testing.T) { + r := &request{payload: &bytes.Buffer{}} + + type args struct { + v Char + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r, + args: args{ + v: 'A', + }, + want: []byte{0x41, 0x0}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteChar(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteChar() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteODouble() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOChar(t *testing.T) { + r := &request{payload: &bytes.Buffer{}} + + type args struct { + v Char + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r, + args: args{ + v: 'A', + }, + want: []byte{7, 0x41, 0x0}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOChar(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOChar() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteODouble() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteBool(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + r2 := &request{payload: &bytes.Buffer{}} + + type args struct { + v bool + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + v: true, + }, + want: []byte{1}[:], + }, + { + name: "2", + r: r2, + args: args{ + v: false, + }, + want: []byte{0}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteBool(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteBool() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteByte() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOBool(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + r2 := &request{payload: &bytes.Buffer{}} + + type args struct { + v bool + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + v: true, + }, + want: []byte{8, 1}[:], + }, + { + name: "2", + r: r2, + args: args{ + v: false, + }, + want: []byte{8, 0}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOBool(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOBool() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteByte() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOString(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + r2 := &request{payload: &bytes.Buffer{}} + + type args struct { + v string + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + v: "test string", + }, + want: []byte{9, 0x0B, 0, 0, 0, 0x74, 0x65, 0x73, 0x74, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67}[:], + }, + { + name: "2", + r: r2, + args: args{ + v: "", + }, + want: []byte{9, 0, 0, 0, 0}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOString(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOString() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOString() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOUUID(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + v, _ := uuid.Parse("d6589da7-f8b1-4687-b5bd-2ddc7362a4a4") + + type args struct { + v uuid.UUID + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + v: v, + }, + want: []byte{10, 0xd6, 0x58, 0x9d, 0xa7, 0xf8, 0xb1, 0x46, 0x87, 0xb5, + 0xbd, 0x2d, 0xdc, 0x73, 0x62, 0xa4, 0xa4}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOUUID(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOUUID() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOUUID() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteODate(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + dm := time.Date(2018, 4, 3, 0, 0, 0, 0, time.UTC) + + type args struct { + v Date + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + v: ToDate(dm), + }, + want: []byte{11, 0x0, 0xa0, 0xcd, 0x88, 0x62, 0x1, 0x0, 0x0}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteODate(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteODate() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteODate() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOArrayBytes(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + + type args struct { + v []byte + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + v: []byte{1, 2, 3}, + }, + want: []byte{12, 3, 0, 0, 0, 1, 2, 3}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOArrayBytes(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOArrayBytes() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOArrayBytes() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOArrayShorts(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + + type args struct { + v []int16 + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + v: []int16{1, 2, 3}, + }, + want: []byte{13, 3, 0, 0, 0, 1, 0, 2, 0, 3, 0}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOArrayShorts(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOArrayShorts() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOArrayShorts() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOArrayInts(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + + type args struct { + v []int32 + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + v: []int32{1, 2, 3}, + }, + want: []byte{14, 3, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOArrayInts(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOArrayInts() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOArrayInts() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOArrayLongs(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + + type args struct { + v []int64 + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + v: []int64{1, 2, 3}, + }, + want: []byte{15, 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOArrayLongs(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOArrayLongs() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOArrayLongs() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOArrayFloats(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + + type args struct { + v []float32 + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + v: []float32{1.1, 2.2, 3.3}, + }, + want: []byte{16, 0x3, 0x0, 0x0, 0x0, 0xcd, 0xcc, 0x8c, 0x3f, 0xcd, 0xcc, 0xc, 0x40, 0x33, 0x33, 0x53, 0x40}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOArrayFloats(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOArrayFloats() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOArrayFloats() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOArrayDoubles(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + + type args struct { + v []float64 + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + v: []float64{1.1, 2.2, 3.3}, + }, + want: []byte{17, 0x3, 0x0, 0x0, 0x0, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99, 0xf1, 0x3f, 0x9a, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x1, 0x40, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0xa, 0x40}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOArrayDoubles(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOArrayDoubles() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOArrayDoubles() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOArrayChars(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + + type args struct { + v []Char + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + v: []Char{'A', 'B', 'Я'}, + }, + want: []byte{18, 3, 0, 0, 0, 0x41, 0x0, 0x42, 0x0, 0x2f, 0x4}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOArrayChars(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOArrayChars() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOArrayChars() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOArrayBools(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + + type args struct { + v []bool + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + v: []bool{true, false, true}, + }, + want: []byte{19, 3, 0, 0, 0, 1, 0, 1}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOArrayBools(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOArrayBools() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOArrayBools() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOArrayOStrings(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + + type args struct { + v []string + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + v: []string{"one", "two", "три"}, + }, + want: []byte{20, 3, 0, 0, 0, + 0x9, 3, 0, 0, 0, 0x6f, 0x6e, 0x65, + 0x9, 3, 0, 0, 0, 0x74, 0x77, 0x6f, + 0x9, 6, 0, 0, 0, 0xd1, 0x82, 0xd1, 0x80, 0xd0, 0xb8}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOArrayOStrings(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOArrayOStrings() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOArrayOStrings() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOArrayOUUIDs(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + uid1, _ := uuid.Parse("a0c07c4c-7e2e-43d3-8eda-176881477c81") + uid2, _ := uuid.Parse("4015b55f-72f0-48a4-8d01-64168d50f627") + uid3, _ := uuid.Parse("827d1bf0-c5d4-4443-8708-d8b5de31fe74") + + type args struct { + v []uuid.UUID + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + v: []uuid.UUID{uid1, uid2, uid3}, + }, + want: []byte{21, 3, 0, 0, 0, + 10, 0xa0, 0xc0, 0x7c, 0x4c, 0x7e, 0x2e, 0x43, 0xd3, 0x8e, 0xda, 0x17, 0x68, 0x81, 0x47, 0x7c, 0x81, + 10, 0x40, 0x15, 0xb5, 0x5f, 0x72, 0xf0, 0x48, 0xa4, 0x8d, 0x1, 0x64, 0x16, 0x8d, 0x50, 0xf6, 0x27, + 10, 0x82, 0x7d, 0x1b, 0xf0, 0xc5, 0xd4, 0x44, 0x43, 0x87, 0x8, 0xd8, 0xb5, 0xde, 0x31, 0xfe, 0x74}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOArrayOUUIDs(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOArrayOUUIDs() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOArrayOUUIDs() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOArrayODates(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + dm1 := time.Date(2018, 4, 3, 0, 0, 0, 0, time.UTC) + dm2 := time.Date(2019, 5, 4, 0, 0, 0, 0, time.UTC) + dm3 := time.Date(2020, 6, 5, 0, 0, 0, 0, time.UTC) + + type args struct { + v []Date + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + v: []Date{ToDate(dm1), ToDate(dm2), ToDate(dm3)}, + }, + want: []byte{22, 3, 0, 0, 0, + 11, 0x0, 0xa0, 0xcd, 0x88, 0x62, 0x1, 0x0, 0x0, + 11, 0x0, 0xf0, 0x23, 0x80, 0x6a, 0x1, 0x0, 0x0, + 11, 0x0, 0xf8, 0xc6, 0x81, 0x72, 0x1, 0x0, 0x0}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOArrayODates(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOArrayODates() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOArrayOStrings() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOTimestamp(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + tm := time.Date(2018, 4, 3, 14, 25, 32, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + + type args struct { + v time.Time + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + v: tm, + }, + want: []byte{33, 0xdb, 0xb, 0xe6, 0x8b, 0x62, 0x1, 0x0, 0x0, 0x55, 0xf8, 0x6, 0x0}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOTimestamp(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOTimestamp() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOString() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOArrayOTimestamps(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + tm1 := time.Date(2018, 4, 3, 14, 25, 32, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + tm2 := time.Date(2019, 5, 4, 15, 26, 33, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + tm3 := time.Date(2020, 6, 5, 16, 27, 34, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + + type args struct { + v []time.Time + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + v: []time.Time{tm1, tm2, tm3}, + }, + want: []byte{34, 3, 0, 0, 0, + 33, 0xdb, 0xb, 0xe6, 0x8b, 0x62, 0x1, 0x0, 0x0, 0x55, 0xf8, 0x6, 0x0, + 33, 0xa3, 0x38, 0x74, 0x83, 0x6a, 0x1, 0x0, 0x0, 0x55, 0xf8, 0x6, 0x0, + 33, 0x6b, 0x1d, 0x4f, 0x85, 0x72, 0x1, 0x0, 0x0, 0x55, 0xf8, 0x6, 0x0}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOArrayOTimestamps(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOArrayOTimestamps() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOArrayOTimestamps() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOTime(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + tm := time.Date(1, 1, 1, 14, 25, 32, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + + type args struct { + v Time + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + v: ToTime(tm), + }, + want: []byte{36, 0xdb, 0x6b, 0x18, 0x3, 0x0, 0x0, 0x0, 0x0}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOTime(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOTime() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOTime() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteOArrayOTimes(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + tm1 := time.Date(1, 1, 1, 14, 25, 32, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + tm2 := time.Date(1, 1, 1, 15, 26, 33, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + tm3 := time.Date(1, 1, 1, 16, 27, 34, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + + type args struct { + v []Time + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + v: []Time{ToTime(tm1), ToTime(tm2), ToTime(tm3)}, + }, + want: []byte{37, 3, 0, 0, 0, + 36, 0xdb, 0x6b, 0x18, 0x3, 0x0, 0x0, 0x0, 0x0, + 36, 0xa3, 0x48, 0x50, 0x3, 0x0, 0x0, 0x0, 0x0, + 36, 0x6b, 0x25, 0x88, 0x3, 0x0, 0x0, 0x0, 0x0}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteOArrayOTimes(tt.args.v); (err != nil) != tt.wantErr { + t.Errorf("request.WriteOArrayOTimes() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteOArrayOTimes() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteNull(t *testing.T) { + r := &request{payload: &bytes.Buffer{}} + + tests := []struct { + name string + r *request + want []byte + wantErr bool + }{ + { + name: "1", + r: r, + want: []byte{101}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteNull(); (err != nil) != tt.wantErr { + t.Errorf("request.WriteNull() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteNull() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteObject(t *testing.T) { + r1 := &request{payload: &bytes.Buffer{}} + r2 := &request{payload: &bytes.Buffer{}} + r3 := &request{payload: &bytes.Buffer{}} + r4 := &request{payload: &bytes.Buffer{}} + r5 := &request{payload: &bytes.Buffer{}} + r6 := &request{payload: &bytes.Buffer{}} + r7 := &request{payload: &bytes.Buffer{}} + r8 := &request{payload: &bytes.Buffer{}} + r9 := &request{payload: &bytes.Buffer{}} + r10 := &request{payload: &bytes.Buffer{}} + uid, _ := uuid.Parse("d6589da7-f8b1-4687-b5bd-2ddc7362a4a4") + r11 := &request{payload: &bytes.Buffer{}} + dm := time.Date(2018, 4, 3, 0, 0, 0, 0, time.UTC) + r12 := &request{payload: &bytes.Buffer{}} + r13 := &request{payload: &bytes.Buffer{}} + r14 := &request{payload: &bytes.Buffer{}} + r15 := &request{payload: &bytes.Buffer{}} + r16 := &request{payload: &bytes.Buffer{}} + r17 := &request{payload: &bytes.Buffer{}} + r18 := &request{payload: &bytes.Buffer{}} + r19 := &request{payload: &bytes.Buffer{}} + r20 := &request{payload: &bytes.Buffer{}} + r21 := &request{payload: &bytes.Buffer{}} + uid1, _ := uuid.Parse("a0c07c4c-7e2e-43d3-8eda-176881477c81") + uid2, _ := uuid.Parse("4015b55f-72f0-48a4-8d01-64168d50f627") + uid3, _ := uuid.Parse("827d1bf0-c5d4-4443-8708-d8b5de31fe74") + r22 := &request{payload: &bytes.Buffer{}} + dm1 := time.Date(2018, 4, 3, 0, 0, 0, 0, time.UTC) + dm2 := time.Date(2019, 5, 4, 0, 0, 0, 0, time.UTC) + dm3 := time.Date(2020, 6, 5, 0, 0, 0, 0, time.UTC) + r33 := &request{payload: &bytes.Buffer{}} + r34 := &request{payload: &bytes.Buffer{}} + tm1 := time.Date(2018, 4, 3, 14, 25, 32, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + tm2 := time.Date(2019, 5, 4, 15, 26, 33, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + tm3 := time.Date(2020, 6, 5, 16, 27, 34, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + r36 := &request{payload: &bytes.Buffer{}} + tm := time.Date(2018, 4, 3, 14, 25, 32, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + r37 := &request{payload: &bytes.Buffer{}} + tm4 := time.Date(1, 1, 1, 14, 25, 32, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + tm5 := time.Date(1, 1, 1, 15, 26, 33, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + tm6 := time.Date(1, 1, 1, 16, 27, 34, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + r101 := &request{payload: &bytes.Buffer{}} + + type args struct { + o interface{} + } + tests := []struct { + name string + r *request + args args + want []byte + wantErr bool + }{ + { + name: "byte", + r: r1, + args: args{ + byte(123), + }, + want: []byte{1, 123}[:], + }, + { + name: "short", + r: r2, + args: args{ + int16(12345), + }, + want: []byte{2, 0x39, 0x30}[:], + }, + { + name: "int", + r: r3, + args: args{ + int32(1234567890), + }, + want: []byte{3, 0xD2, 0x02, 0x96, 0x49}[:], + }, + { + name: "long", + r: r4, + args: args{ + int64(1234567890123456789), + }, + want: []byte{4, 0x15, 0x81, 0xE9, 0x7D, 0xF4, 0x10, 0x22, 0x11}[:], + }, + { + name: "float", + r: r5, + args: args{ + float32(123456.789), + }, + want: []byte{5, 0x65, 0x20, 0xf1, 0x47}[:], + }, + { + name: "double", + r: r6, + args: args{ + float64(123456789.12345), + }, + want: []byte{6, 0xad, 0x69, 0x7e, 0x54, 0x34, 0x6f, 0x9d, 0x41}[:], + }, + { + name: "char", + r: r7, + args: args{ + Char('A'), + }, + want: []byte{7, 0x41, 0x0}[:], + }, + { + name: "bool", + r: r8, + args: args{ + true, + }, + want: []byte{8, 0x1}[:], + }, + { + name: "String", + r: r9, + args: args{ + "test string", + }, + want: []byte{9, 0x0B, 0, 0, 0, 0x74, 0x65, 0x73, 0x74, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67}[:], + }, + { + name: "UUID", + r: r10, + args: args{ + uid, + }, + want: []byte{10, 0xd6, 0x58, 0x9d, 0xa7, 0xf8, 0xb1, 0x46, 0x87, 0xb5, + 0xbd, 0x2d, 0xdc, 0x73, 0x62, 0xa4, 0xa4}[:], + }, + { + name: "Date", + r: r11, + args: args{ + ToDate(dm), + }, + want: []byte{11, 0x0, 0xa0, 0xcd, 0x88, 0x62, 0x1, 0x0, 0x0}, + }, + { + name: "byte array", + r: r12, + args: args{ + []byte{1, 2, 3}, + }, + want: []byte{12, 3, 0, 0, 0, 1, 2, 3}, + }, + { + name: "short array", + r: r13, + args: args{ + []int16{1, 2, 3}, + }, + want: []byte{13, 3, 0, 0, 0, 1, 0, 2, 0, 3, 0}, + }, + { + name: "int array", + r: r14, + args: args{ + []int32{1, 2, 3}, + }, + want: []byte{14, 3, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0}, + }, + { + name: "long array", + r: r15, + args: args{ + []int64{1, 2, 3}, + }, + want: []byte{15, 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "float array", + r: r16, + args: args{ + []float32{1.1, 2.2, 3.3}, + }, + want: []byte{16, 0x3, 0x0, 0x0, 0x0, 0xcd, 0xcc, 0x8c, 0x3f, 0xcd, 0xcc, 0xc, 0x40, 0x33, 0x33, 0x53, 0x40}, + }, + { + name: "double array", + r: r17, + args: args{ + []float64{1.1, 2.2, 3.3}, + }, + want: []byte{17, 3, 0, 0, 0, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99, 0xf1, 0x3f, 0x9a, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x1, 0x40, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0xa, 0x40}, + }, + { + name: "char array", + r: r18, + args: args{ + []Char{'A', 'B', 'Я'}, + }, + want: []byte{18, 3, 0, 0, 0, 0x41, 0x0, 0x42, 0x0, 0x2f, 0x4}, + }, + { + name: "bool array", + r: r19, + args: args{ + []bool{true, false, true}, + }, + want: []byte{19, 3, 0, 0, 0, 1, 0, 1}, + }, + { + name: "string array", + r: r20, + args: args{ + []string{"one", "two", "три"}, + }, + want: []byte{20, 3, 0, 0, 0, + 0x9, 3, 0, 0, 0, 0x6f, 0x6e, 0x65, + 0x9, 3, 0, 0, 0, 0x74, 0x77, 0x6f, + 0x9, 6, 0, 0, 0, 0xd1, 0x82, 0xd1, 0x80, 0xd0, 0xb8}, + }, + { + name: "UUID array", + r: r21, + args: args{ + []uuid.UUID{uid1, uid2, uid3}, + }, + want: []byte{21, 3, 0, 0, 0, + 10, 0xa0, 0xc0, 0x7c, 0x4c, 0x7e, 0x2e, 0x43, 0xd3, 0x8e, 0xda, 0x17, 0x68, 0x81, 0x47, 0x7c, 0x81, + 10, 0x40, 0x15, 0xb5, 0x5f, 0x72, 0xf0, 0x48, 0xa4, 0x8d, 0x1, 0x64, 0x16, 0x8d, 0x50, 0xf6, 0x27, + 10, 0x82, 0x7d, 0x1b, 0xf0, 0xc5, 0xd4, 0x44, 0x43, 0x87, 0x8, 0xd8, 0xb5, 0xde, 0x31, 0xfe, 0x74}, + }, + { + name: "date array", + r: r22, + args: args{ + []Date{ToDate(dm1), ToDate(dm2), ToDate(dm3)}, + }, + want: []byte{22, 3, 0, 0, 0, + 11, 0x0, 0xa0, 0xcd, 0x88, 0x62, 0x1, 0x0, 0x0, + 11, 0x0, 0xf0, 0x23, 0x80, 0x6a, 0x1, 0x0, 0x0, + 11, 0x0, 0xf8, 0xc6, 0x81, 0x72, 0x1, 0x0, 0x0}, + }, + { + name: "Timestamp", + r: r33, + args: args{ + tm, + }, + want: []byte{33, 0xdb, 0xb, 0xe6, 0x8b, 0x62, 0x1, 0x0, 0x0, 0x55, 0xf8, 0x6, 0x0}[:], + }, + { + name: "Timestamp array", + r: r34, + args: args{ + []time.Time{tm1, tm2, tm3}, + }, + want: []byte{34, 3, 0, 0, 0, + 33, 0xdb, 0xb, 0xe6, 0x8b, 0x62, 0x1, 0x0, 0x0, 0x55, 0xf8, 0x6, 0x0, + 33, 0xa3, 0x38, 0x74, 0x83, 0x6a, 0x1, 0x0, 0x0, 0x55, 0xf8, 0x6, 0x0, + 33, 0x6b, 0x1d, 0x4f, 0x85, 0x72, 0x1, 0x0, 0x0, 0x55, 0xf8, 0x6, 0x0}, + }, + { + name: "Time", + r: r36, + args: args{ + ToTime(tm), + }, + want: []byte{36, 0xdb, 0x6b, 0x18, 0x3, 0x0, 0x0, 0x0, 0x0}[:], + }, + { + name: "Time array", + r: r37, + args: args{ + []Time{ToTime(tm4), ToTime(tm5), ToTime(tm6)}, + }, + want: []byte{37, 3, 0, 0, 0, + 36, 0xdb, 0x6b, 0x18, 0x3, 0x0, 0x0, 0x0, 0x0, + 36, 0xa3, 0x48, 0x50, 0x3, 0x0, 0x0, 0x0, 0x0, + 36, 0x6b, 0x25, 0x88, 0x3, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "NULL", + r: r101, + args: args{ + nil, + }, + want: []byte{101}[:], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.r.WriteObject(tt.args.o); (err != nil) != tt.wantErr { + t.Errorf("request.WriteObject() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.r.payload.Bytes(), tt.want) { + t.Errorf("request.WriteObject() = %#v, want %#v", tt.r.payload.Bytes(), tt.want) + } + }) + } +} + +func Test_request_WriteTo(t *testing.T) { + r := &request{payload: &bytes.Buffer{}} + _ = r.WriteInt(1234567890) + + tests := []struct { + name string + r *request + want int64 + wantW []byte + wantErr bool + }{ + { + name: "1", + r: r, + want: 4, + wantW: []byte{0xD2, 0x02, 0x96, 0x49}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + w := &bytes.Buffer{} + got, err := tt.r.WriteTo(w) + if (err != nil) != tt.wantErr { + t.Errorf("request.WriteTo() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("request.WriteTo() = %v, want %v", got, tt.want) + } + if gotW := w.Bytes(); !reflect.DeepEqual(w.Bytes(), tt.wantW) { + t.Errorf("request.WriteTo() = %#v, want %#v", gotW, tt.wantW) + } + }) + } +} diff --git a/binary/v1/response-handshake.go b/binary/v1/response-handshake.go new file mode 100644 index 0000000..bd0e199 --- /dev/null +++ b/binary/v1/response-handshake.go @@ -0,0 +1,61 @@ +package ignite + +import ( + "io" + + "github.com/amsokol/ignite-go-client/binary/errors" +) + +// ResponseHandshake is struct handshake response +type ResponseHandshake struct { + // Success flag + Success bool + // Server version major, minor, patch + Major, Minor, Patch int + // Error message + Message string + + response +} + +// ReadFrom is function to read request data from io.Reader. +// Returns read bytes. +func (r *ResponseHandshake) ReadFrom(rr io.Reader) (int64, error) { + // read response + n, err := r.response.ReadFrom(rr) + if err != nil { + return 0, errors.Wrapf(err, "failed to read handshare response") + } + + r.Success, err = r.ReadBool() + if err != nil { + return 0, errors.Wrapf(err, "failed to read success flag") + } + + if !r.Success { + v, err := r.ReadShort() + if err != nil { + return 0, errors.Wrapf(err, "failed to read server version major") + } + r.Major = int(v) + + v, err = r.ReadShort() + if err != nil { + return 0, errors.Wrapf(err, "failed to read server version minor") + } + r.Minor = int(v) + + v, err = r.ReadShort() + if err != nil { + return 0, errors.Wrapf(err, "failed to read server version patch") + } + r.Patch = int(v) + + r.Message, err = r.ReadOString() + if err != nil { + return 0, errors.Wrapf(err, "failed to read error message") + } + } + + return n, nil +} diff --git a/binary/v1/response-handshake_test.go b/binary/v1/response-handshake_test.go new file mode 100644 index 0000000..42d4760 --- /dev/null +++ b/binary/v1/response-handshake_test.go @@ -0,0 +1,82 @@ +package ignite + +import ( + "bytes" + "io" + "testing" +) + +func TestResponseHandshake_ReadFrom(t *testing.T) { + rr1 := bytes.NewBuffer( + []byte{1, 0, 0, 0, 1}) + rr2 := bytes.NewBuffer( + []byte{23, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 9, 0x0B, 0, 0, 0, 0x74, 0x65, 0x73, 0x74, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67}) + + r1 := &ResponseHandshake{} + r2 := &ResponseHandshake{} + + type args struct { + rr io.Reader + } + tests := []struct { + name string + r *ResponseHandshake + args args + want int64 + wantSuccess bool + wantMajor, wantMinor, wantPatch int + wantMessage string + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + rr: rr1, + }, + want: 4 + 1, + wantSuccess: true, + }, + { + name: "2", + r: r2, + args: args{ + rr: rr2, + }, + want: 4 + 23, + wantSuccess: false, + wantMajor: 1, + wantMinor: 0, + wantPatch: 0, + wantMessage: "test string", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadFrom(tt.args.rr) + if (err != nil) != tt.wantErr { + t.Errorf("ResponseHandshake.ReadFrom() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("ResponseHandshake.ReadFrom() = %v, want %v", got, tt.want) + } + if tt.r.Success != tt.wantSuccess { + t.Errorf("ResponseHandshake.ReadFrom() success = %v, want %v", tt.r.Success, tt.wantSuccess) + } + if tt.r.Major != tt.wantMajor { + t.Errorf("ResponseHandshake.ReadFrom() major = %v, want %v", tt.r.Major, tt.wantMajor) + } + if tt.r.Minor != tt.wantMinor { + t.Errorf("ResponseHandshake.ReadFrom() minor = %v, want %v", tt.r.Minor, tt.wantMinor) + } + if tt.r.Patch != tt.wantPatch { + t.Errorf("ResponseHandshake.ReadFrom() patch = %v, want %v", tt.r.Patch, tt.wantPatch) + } + if tt.r.Message != tt.wantMessage { + t.Errorf("ResponseHandshake.ReadFrom() message = %v, want %v", tt.r.Message, tt.wantMessage) + } + }) + } +} diff --git a/binary/v1/response-operation.go b/binary/v1/response-operation.go new file mode 100644 index 0000000..7d08f3f --- /dev/null +++ b/binary/v1/response-operation.go @@ -0,0 +1,73 @@ +package ignite + +import ( + "io" + + "github.com/amsokol/ignite-go-client/binary/errors" +) + +const ( + // OperationStatusSuccess means success + OperationStatusSuccess = 0 +) + +// ResponseOperation is struct operation response +type ResponseOperation struct { + // Request id + UID int64 + // Status code (0 for success, otherwise error code) + Status int32 + // Error message (present only when status is not 0) + Message string + + response +} + +// ReadFrom is function to read request data from io.Reader. +// Returns read bytes. +func (r *ResponseOperation) ReadFrom(rr io.Reader) (int64, error) { + // read response + n, err := r.response.ReadFrom(rr) + if err != nil { + return 0, errors.Wrapf(err, "failed to read operation response") + } + + uid, err := r.ReadLong() + if err != nil { + return 0, errors.Wrapf(err, "failed to read operation request id") + } + + r.Status, err = r.ReadInt() + if err != nil { + return 0, errors.Wrapf(err, "failed to read status code") + } + + if r.Status != OperationStatusSuccess { + r.Message, err = r.ReadOString() + if err != nil { + return 0, errors.Wrapf(err, "failed to read error message") + } + } + + if uid != r.UID { + return n, errors.Errorf("invalid request ID: got %d, but expected %d", uid, r.UID) + } + + return n, nil +} + +// CheckStatus checks status of operation execution. +// Returns: +// nil in case of success. +// error object in case of operation failed. +func (r *ResponseOperation) CheckStatus() error { + if r.Status != OperationStatusSuccess { + return errors.NewError(r.Status, r.Message) + } + return nil +} + +// NewResponseOperation is ResponseOperation constructor +func NewResponseOperation(uid int64) *ResponseOperation { + return &ResponseOperation{UID: uid} +} diff --git a/binary/v1/response-operation_test.go b/binary/v1/response-operation_test.go new file mode 100644 index 0000000..8723513 --- /dev/null +++ b/binary/v1/response-operation_test.go @@ -0,0 +1,90 @@ +package ignite + +import ( + "bytes" + "io" + "testing" +) + +func TestResponseOperation_ReadFrom(t *testing.T) { + rr1 := bytes.NewBuffer( + []byte{12, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) + rr2 := bytes.NewBuffer( + []byte{28, 0, 0, 0, + 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 9, 0x0B, 0, 0, 0, 0x74, 0x65, 0x73, 0x74, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67}) + rr3 := bytes.NewBuffer( + []byte{12, 0, 0, 0, + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) + + r1 := NewResponseOperation(1) + r2 := NewResponseOperation(2) + r3 := NewResponseOperation(0) + + type args struct { + rr io.Reader + } + tests := []struct { + name string + r *ResponseOperation + args args + want int64 + wantUID int64 + wantStatus int32 + wantMessage string + wantErr bool + }{ + { + name: "1", + r: r1, + args: args{ + rr: rr1, + }, + want: 4 + 12, + wantUID: 1, + wantStatus: 0, + }, + { + name: "2", + r: r2, + args: args{ + rr: rr2, + }, + want: 4 + 28, + wantUID: 2, + wantStatus: 1, + wantMessage: "test string", + }, + { + name: "3", + r: r3, + args: args{ + rr: rr3, + }, + want: 4 + 12, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadFrom(tt.args.rr) + if (err != nil) != tt.wantErr { + t.Errorf("ResponseOperation.ReadFrom() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("ResponseOperation.ReadFrom() = %v, want %v", got, tt.want) + } + if tt.r.UID != tt.wantUID { + t.Errorf("ResponseOperation.ReadFrom() UID = %v, want %v", tt.r.UID, tt.wantUID) + } + if tt.r.Status != tt.wantStatus { + t.Errorf("ResponseOperation.ReadFrom() Status = %v, want %v", tt.r.Status, tt.wantStatus) + } + if tt.r.Message != tt.wantMessage { + t.Errorf("ResponseOperation.ReadFrom() Message = %v, want %v", tt.r.Message, tt.wantMessage) + } + }) + } +} diff --git a/binary/v1/response.go b/binary/v1/response.go index 747d07a..7926a9a 100644 --- a/binary/v1/response.go +++ b/binary/v1/response.go @@ -2,42 +2,438 @@ package ignite import ( "bytes" + "encoding/binary" + "io" + "time" + + "github.com/google/uuid" "github.com/amsokol/ignite-go-client/binary/errors" ) -// Response is struct of response data -type Response struct { - Len int32 - UID int64 - Status int32 - Message string +// Response is interface of base message response functionality +type Response interface { + // ReadFrom is function to read request data from io.Reader. + // Returns read bytes. + ReadFrom(r io.Reader) (int64, error) +} + +// response is struct is implementing base message response functionality +type response struct { + message io.Reader + + Response +} + +// ReadByte reads "byte" value +func (r *response) ReadByte() (byte, error) { + var v byte + err := binary.Read(r.message, binary.LittleEndian, &v) + return v, err +} + +// ReadShort reads "short" value +func (r *response) ReadShort() (int16, error) { + var v int16 + err := binary.Read(r.message, binary.LittleEndian, &v) + return v, err +} + +// ReadInt reads "int" value +func (r *response) ReadInt() (int32, error) { + var v int32 + err := binary.Read(r.message, binary.LittleEndian, &v) + return v, err +} + +// ReadLong reads "long" value +func (r *response) ReadLong() (int64, error) { + var v int64 + err := binary.Read(r.message, binary.LittleEndian, &v) + return v, err +} + +// ReadFloat reads "float" value +func (r *response) ReadFloat() (float32, error) { + var v float32 + err := binary.Read(r.message, binary.LittleEndian, &v) + return v, err +} + +// ReadDouble reads "Double" value +func (r *response) ReadDouble() (float64, error) { + var v float64 + err := binary.Read(r.message, binary.LittleEndian, &v) + return v, err +} + +// ReadChar reads "char" value +func (r *response) ReadChar() (Char, error) { + var v int16 + err := binary.Read(r.message, binary.LittleEndian, &v) + return Char(v), err +} + +// ReadBool reads "bool" value +func (r *response) ReadBool() (bool, error) { + v, err := r.ReadByte() + if err != nil { + return false, err + } + switch v { + case 1: + return true, nil + case 0: + return false, nil + default: + return false, errors.Errorf("invalid bool value: %d", v) + } +} + +// ReadString reads "string" value +func (r *response) ReadString() (string, error) { + l, err := r.ReadInt() + if err != nil { + return "", err + } + if l > 0 { + s := make([]byte, l) + if err = binary.Read(r.message, binary.LittleEndian, &s); err != nil { + return "", err + } + return string(s), nil + } + return "", nil +} + +// ReadOString reads "string" object value or NULL (returns "") +func (r *response) ReadOString() (string, error) { + t, err := r.ReadByte() + if err != nil { + return "", err + } + switch t { + case typeNULL: + return "", nil + case typeString: + v, err := r.ReadString() + return v, err + default: + return "", errors.Errorf("invalid type (expected %d, but got %d)", typeString, t) + } +} + +// ReadUUID reads "UUID" object value +func (r *response) ReadUUID() (uuid.UUID, error) { + var o uuid.UUID + err := binary.Read(r.message, binary.LittleEndian, &o) + return o, err +} + +// ReadDate reads "Date" object value +func (r *response) ReadDate() (time.Time, error) { + v, err := r.ReadLong() + if err != nil { + return time.Time{}, err + } + return time.Unix(int64(v)/1000, (int64(v)%1000)*int64(time.Millisecond)).UTC(), nil +} - Data *bytes.Reader +// ReadArrayBytes reads "byte" array value +func (r *response) ReadArrayBytes() ([]byte, error) { + l, err := r.ReadInt() + if err != nil { + return nil, err + } + b := make([]byte, l) + if l > 0 { + err = binary.Read(r.message, binary.LittleEndian, &b) + } + return b, err +} + +// ReadArrayShorts reads "short" array value +func (r *response) ReadArrayShorts() ([]int16, error) { + l, err := r.ReadInt() + if err != nil { + return nil, err + } + b := make([]int16, l) + if l > 0 { + err = binary.Read(r.message, binary.LittleEndian, &b) + } + return b, err } -// CheckStatus checks status of operation execution. -// Returns: -// nil in case of success. -// error object in case of operation failed. -func (r *Response) CheckStatus() error { - if r.Status != errors.StatusSuccess { - return errors.NewError(r.Status, r.Message) +// ReadArrayInts reads "int" array value +func (r *response) ReadArrayInts() ([]int32, error) { + l, err := r.ReadInt() + if err != nil { + return nil, err } - return nil + b := make([]int32, l) + if l > 0 { + err = binary.Read(r.message, binary.LittleEndian, &b) + } + return b, err } -// ReadPrimitives reads primitives from buffered response data -func (r *Response) ReadPrimitives(data ...interface{}) error { - return readPrimitives(r.Data, data...) +// ReadArrayLongs reads "long" array value +func (r *response) ReadArrayLongs() ([]int64, error) { + l, err := r.ReadInt() + if err != nil { + return nil, err + } + b := make([]int64, l) + if l > 0 { + err = binary.Read(r.message, binary.LittleEndian, &b) + } + return b, err } -// ReadObject reads object from buffered response data -func (r *Response) ReadObject() (interface{}, error) { - return readObject(r.Data) +// ReadArrayFloats reads "float" array value +func (r *response) ReadArrayFloats() ([]float32, error) { + l, err := r.ReadInt() + if err != nil { + return nil, err + } + b := make([]float32, l) + if l > 0 { + err = binary.Read(r.message, binary.LittleEndian, &b) + } + return b, err } -// ReadObjects reads objects from buffered response data -func (r *Response) ReadObjects(count int) ([]interface{}, error) { - return readObjects(r.Data, count) +// ReadArrayDoubles reads "double" array value +func (r *response) ReadArrayDoubles() ([]float64, error) { + l, err := r.ReadInt() + if err != nil { + return nil, err + } + b := make([]float64, l) + if l > 0 { + err = binary.Read(r.message, binary.LittleEndian, &b) + } + return b, err +} + +// ReadArrayChars reads "char" array value +func (r *response) ReadArrayChars() ([]Char, error) { + l, err := r.ReadInt() + if err != nil { + return nil, err + } + b := make([]Char, l) + for i := 0; i < int(l); i++ { + if b[i], err = r.ReadChar(); err != nil { + return nil, err + } + } + return b, nil +} + +// ReadArrayBools reads "bool" array value +func (r *response) ReadArrayBools() ([]bool, error) { + l, err := r.ReadInt() + if err != nil { + return nil, err + } + b := make([]bool, l) + if l > 0 { + err = binary.Read(r.message, binary.LittleEndian, &b) + } + return b, err +} + +// ReadArrayOStrings reads "String" array value +func (r *response) ReadArrayOStrings() ([]string, error) { + l, err := r.ReadInt() + if err != nil { + return nil, err + } + b := make([]string, l) + for i := 0; i < int(l); i++ { + if b[i], err = r.ReadOString(); err != nil { + return nil, err + } + } + return b, nil +} + +// ReadArrayOUUIDs reads "UUID" array value +func (r *response) ReadArrayOUUIDs() ([]uuid.UUID, error) { + l, err := r.ReadInt() + if err != nil { + return nil, err + } + b := make([]uuid.UUID, l) + for i := 0; i < int(l); i++ { + o, err := r.ReadObject() + if err != nil { + return nil, err + } + b[i] = o.(uuid.UUID) + } + return b, nil +} + +// ReadArrayODates reads "Date" array value +func (r *response) ReadArrayODates() ([]time.Time, error) { + l, err := r.ReadInt() + if err != nil { + return nil, err + } + b := make([]time.Time, l) + for i := 0; i < int(l); i++ { + o, err := r.ReadObject() + if err != nil { + return nil, err + } + b[i] = o.(time.Time) + } + return b, nil +} + +// ReadTimestamp reads "Timestamp" object value +func (r *response) ReadTimestamp() (time.Time, error) { + high, err := r.ReadLong() + if err != nil { + return time.Time{}, err + } + low, err := r.ReadInt() + if err != nil { + return time.Time{}, err + } + low = int32((high%1000)*int64(time.Millisecond)) + low + high = high / 1000 + return time.Unix(high, int64(low)).UTC(), nil +} + +// ReadArrayOTimestamps reads "Timestamp" array value +func (r *response) ReadArrayOTimestamps() ([]time.Time, error) { + l, err := r.ReadInt() + if err != nil { + return nil, err + } + b := make([]time.Time, l) + for i := 0; i < int(l); i++ { + o, err := r.ReadObject() + if err != nil { + return nil, err + } + b[i] = o.(time.Time) + } + return b, nil +} + +// ReadTime reads "Time" object value +func (r *response) ReadTime() (time.Time, error) { + v, err := r.ReadLong() + if err != nil { + return time.Time{}, err + } + t := time.Unix(int64(v)/1000, (int64(v)%1000)*int64(time.Millisecond)).UTC() + return time.Date(1, 1, 1, t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), time.UTC), nil +} + +// ReadArrayOTimes reads "Time" array value +func (r *response) ReadArrayOTimes() ([]time.Time, error) { + l, err := r.ReadInt() + if err != nil { + return nil, err + } + b := make([]time.Time, l) + for i := 0; i < int(l); i++ { + o, err := r.ReadObject() + if err != nil { + return nil, err + } + b[i] = o.(time.Time) + } + return b, nil +} + +func (r *response) ReadObject() (interface{}, error) { + t, err := r.ReadByte() + if err != nil { + return nil, err + } + + switch t { + case typeByte: + return r.ReadByte() + case typeShort: + return r.ReadShort() + case typeInt: + return r.ReadInt() + case typeLong: + return r.ReadLong() + case typeFloat: + return r.ReadFloat() + case typeDouble: + return r.ReadDouble() + case typeChar: + return r.ReadChar() + case typeBool: + return r.ReadBool() + case typeString: + return r.ReadString() + case typeUUID: + return r.ReadUUID() + case typeDate: + return r.ReadDate() + case typeByteArray: + return r.ReadArrayBytes() + case typeShortArray: + return r.ReadArrayShorts() + case typeIntArray: + return r.ReadArrayInts() + case typeLongArray: + return r.ReadArrayLongs() + case typeFloatArray: + return r.ReadArrayFloats() + case typeDoubleArray: + return r.ReadArrayDoubles() + case typeCharArray: + return r.ReadArrayChars() + case typeBoolArray: + return r.ReadArrayBools() + case typeStringArray: + return r.ReadArrayOStrings() + case typeDateArray: + return r.ReadArrayODates() + case typeUUIDArray: + return r.ReadArrayOUUIDs() + case typeTimestamp: + return r.ReadTimestamp() + case typeTimestampArray: + return r.ReadArrayOTimestamps() + case typeTime: + return r.ReadTime() + case typeTimeArray: + return r.ReadArrayOTimes() + case typeNULL: + return nil, nil + default: + return nil, errors.Errorf("unsupported object type: %d", t) + } +} + +// ReadFrom is function to read request data from io.Reader. +// Returns read bytes. +func (r *response) ReadFrom(rr io.Reader) (int64, error) { + // read response length + var l int32 + if err := binary.Read(rr, binary.LittleEndian, &l); err != nil { + return 0, errors.Wrapf(err, "failed to read response length") + } + + // read response message + b := make([]byte, int(l)) + if err := binary.Read(rr, binary.LittleEndian, &b); err != nil { + return 0, errors.Wrapf(err, "failed to read response data") + } + r.message = bytes.NewReader(b) + + return 4 + int64(l), nil } diff --git a/binary/v1/response_test.go b/binary/v1/response_test.go new file mode 100644 index 0000000..7b6a73b --- /dev/null +++ b/binary/v1/response_test.go @@ -0,0 +1,1098 @@ +package ignite + +import ( + "bytes" + "io" + "reflect" + "testing" + "time" + + "github.com/google/uuid" +) + +func Test_response_ReadByte(t *testing.T) { + r := &response{message: bytes.NewBuffer([]byte{123})} + + tests := []struct { + name string + r *response + want byte + wantErr bool + }{ + { + name: "1", + r: r, + want: 123, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadByte() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadByte() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("response.ReadByte() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadShort(t *testing.T) { + r := &response{message: bytes.NewBuffer([]byte{0x39, 0x30})} + + tests := []struct { + name string + r *response + want int16 + wantErr bool + }{ + { + name: "1", + r: r, + want: 12345, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadShort() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadShort() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("response.ReadShort() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadInt(t *testing.T) { + r := &response{message: bytes.NewBuffer([]byte{0xD2, 0x02, 0x96, 0x49})} + + tests := []struct { + name string + r *response + want int32 + wantErr bool + }{ + { + name: "1", + r: r, + want: 1234567890, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadInt() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadInt() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("response.ReadInt() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadLong(t *testing.T) { + r := &response{message: bytes.NewBuffer([]byte{0x15, 0x81, 0xE9, 0x7D, 0xF4, 0x10, 0x22, 0x11})} + + tests := []struct { + name string + r *response + want int64 + wantErr bool + }{ + { + name: "1", + r: r, + want: 1234567890123456789, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadLong() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadLong() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("response.ReadLong() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadFloat(t *testing.T) { + r := &response{message: bytes.NewBuffer([]byte{0x65, 0x20, 0xf1, 0x47})} + + tests := []struct { + name string + r *response + want float32 + wantErr bool + }{ + { + name: "1", + r: r, + want: 123456.789, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadFloat() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadFloat() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("response.ReadFloat() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadDouble(t *testing.T) { + r := &response{message: bytes.NewBuffer([]byte{0xad, 0x69, 0x7e, 0x54, 0x34, 0x6f, 0x9d, 0x41})} + + tests := []struct { + name string + r *response + want float64 + wantErr bool + }{ + { + name: "1", + r: r, + want: 123456789.12345, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadDouble() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadDouble() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("response.ReadDouble() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadChar(t *testing.T) { + r := &response{message: bytes.NewBuffer([]byte{0x41, 0x0})} + + tests := []struct { + name string + r *response + want Char + wantErr bool + }{ + { + name: "1", + r: r, + want: Char('A'), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadChar() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadChar() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("response.ReadChar() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadBool(t *testing.T) { + r1 := &response{message: bytes.NewBuffer([]byte{1})} + r2 := &response{message: bytes.NewBuffer([]byte{0})} + r3 := &response{message: bytes.NewBuffer([]byte{2})} + + tests := []struct { + name string + r *response + want bool + wantErr bool + }{ + { + name: "1", + r: r1, + want: true, + }, + { + name: "2", + r: r2, + want: false, + }, + { + name: "3", + r: r3, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadBool() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadBool() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("response.ReadBool() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadOString(t *testing.T) { + r1 := &response{message: bytes.NewBuffer( + []byte{9, 0x0B, 0, 0, 0, 0x74, 0x65, 0x73, 0x74, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67})} + r2 := &response{message: bytes.NewBuffer( + []byte{9, 0, 0, 0, 0})} + r3 := &response{message: bytes.NewBuffer( + []byte{101})} + r4 := &response{message: bytes.NewBuffer( + []byte{0, 0x0B, 0, 0, 0, 0x74, 0x65, 0x73, 0x74, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67})} + + tests := []struct { + name string + r *response + want string + wantErr bool + }{ + { + name: "1", + r: r1, + want: "test string", + }, + { + name: "2", + r: r2, + want: "", + }, + { + name: "3", + r: r3, + }, + { + name: "4", + r: r4, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadOString() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadOString() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("response.ReadOString() got = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadUUID(t *testing.T) { + r1 := &response{message: bytes.NewBuffer( + []byte{0xd6, 0x58, 0x9d, 0xa7, 0xf8, 0xb1, 0x46, 0x87, 0xb5, + 0xbd, 0x2d, 0xdc, 0x73, 0x62, 0xa4, 0xa4}[:])} + v, _ := uuid.Parse("d6589da7-f8b1-4687-b5bd-2ddc7362a4a4") + + tests := []struct { + name string + r *response + want uuid.UUID + wantErr bool + }{ + { + name: "1", + r: r1, + want: v, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadUUID() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadUUID() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("response.ReadUUID() = %#v, want %#v", got, tt.want) + } + }) + } +} + +func Test_response_ReadDate(t *testing.T) { + r1 := &response{message: bytes.NewBuffer([]byte{0x0, 0xa0, 0xcd, 0x88, 0x62, 0x1, 0x0, 0x0})} + dm := time.Date(2018, 4, 3, 0, 0, 0, 0, time.UTC) + + tests := []struct { + name string + r *response + want time.Time + wantErr bool + }{ + { + name: "1", + r: r1, + want: dm, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadDate() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadDate() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("response.ReadDate() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadArrayBytes(t *testing.T) { + r1 := &response{message: bytes.NewBuffer([]byte{3, 0, 0, 0, 1, 2, 3})} + + tests := []struct { + name string + r *response + want []byte + wantErr bool + }{ + { + name: "1", + r: r1, + want: []byte{1, 2, 3}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadArrayBytes() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadArrayBytes() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("response.ReadArrayBytes() = %#v, want %#v", got, tt.want) + } + }) + } +} + +func Test_response_ReadArrayShorts(t *testing.T) { + r1 := &response{message: bytes.NewBuffer([]byte{3, 0, 0, 0, 1, 0, 2, 0, 3, 0})} + + tests := []struct { + name string + r *response + want []int16 + wantErr bool + }{ + { + name: "1", + r: r1, + want: []int16{1, 2, 3}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadArrayShorts() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadArrayShorts() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("response.ReadArrayShorts() = %#v, want %#v", got, tt.want) + } + }) + } +} + +func Test_response_ReadArrayInts(t *testing.T) { + r1 := &response{message: bytes.NewBuffer( + []byte{3, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0})} + + tests := []struct { + name string + r *response + want []int32 + wantErr bool + }{ + { + name: "1", + r: r1, + want: []int32{1, 2, 3}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadArrayInts() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadArrayInts() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("response.ReadArrayInts() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadArrayLongs(t *testing.T) { + r1 := &response{message: bytes.NewBuffer( + []byte{3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0})} + + tests := []struct { + name string + r *response + want []int64 + wantErr bool + }{ + { + name: "1", + r: r1, + want: []int64{1, 2, 3}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadArrayLongs() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadArrayLongs() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("response.ReadArrayLongs() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadArrayFloats(t *testing.T) { + r1 := &response{message: bytes.NewBuffer( + []byte{3, 0x0, 0x0, 0x0, 0xcd, 0xcc, 0x8c, 0x3f, 0xcd, 0xcc, 0xc, 0x40, 0x33, 0x33, 0x53, 0x40})} + + tests := []struct { + name string + r *response + want []float32 + wantErr bool + }{ + { + name: "1", + r: r1, + want: []float32{1.1, 2.2, 3.3}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadArrayFloats() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadArrayFloats() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("response.ReadArrayFloats() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadArrayDoubles(t *testing.T) { + r1 := &response{message: bytes.NewBuffer( + []byte{3, 0, 0, 0, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99, 0xf1, 0x3f, 0x9a, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x1, 0x40, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0xa, 0x40})} + + tests := []struct { + name string + r *response + want []float64 + wantErr bool + }{ + { + name: "1", + r: r1, + want: []float64{1.1, 2.2, 3.3}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadArrayDoubles() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadArrayDoubles() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("response.ReadArrayDoubles() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadArrayChars(t *testing.T) { + r1 := &response{message: bytes.NewBuffer([]byte{3, 0, 0, 0, 0x41, 0x0, 0x42, 0x0, 0x2f, 0x4})} + + tests := []struct { + name string + r *response + want []Char + wantErr bool + }{ + { + name: "1", + r: r1, + want: []Char{'A', 'B', 'Я'}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadArrayChars() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadArrayChars() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("response.ReadArrayChars() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadArrayBools(t *testing.T) { + r1 := &response{message: bytes.NewBuffer([]byte{3, 0, 0, 0, 1, 0, 1})} + + tests := []struct { + name string + r *response + want []bool + wantErr bool + }{ + { + name: "1", + r: r1, + want: []bool{true, false, true}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadArrayBools() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadArrayBools() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("response.ReadArrayBools() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadArrayOStrings(t *testing.T) { + r1 := &response{message: bytes.NewBuffer([]byte{3, 0, 0, 0, + 0x9, 3, 0, 0, 0, 0x6f, 0x6e, 0x65, + 0x9, 3, 0, 0, 0, 0x74, 0x77, 0x6f, + 0x9, 6, 0, 0, 0, 0xd1, 0x82, 0xd1, 0x80, 0xd0, 0xb8})} + + tests := []struct { + name string + r *response + want []string + wantErr bool + }{ + { + name: "1", + r: r1, + want: []string{"one", "two", "три"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadArrayOStrings() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadArrayOStrings() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("response.ReadArrayOStrings() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadArrayOUUIDs(t *testing.T) { + r1 := &response{message: bytes.NewBuffer([]byte{3, 0, 0, 0, + 10, 0xa0, 0xc0, 0x7c, 0x4c, 0x7e, 0x2e, 0x43, 0xd3, 0x8e, 0xda, 0x17, 0x68, 0x81, 0x47, 0x7c, 0x81, + 10, 0x40, 0x15, 0xb5, 0x5f, 0x72, 0xf0, 0x48, 0xa4, 0x8d, 0x1, 0x64, 0x16, 0x8d, 0x50, 0xf6, 0x27, + 10, 0x82, 0x7d, 0x1b, 0xf0, 0xc5, 0xd4, 0x44, 0x43, 0x87, 0x8, 0xd8, 0xb5, 0xde, 0x31, 0xfe, 0x74})} + uid1, _ := uuid.Parse("a0c07c4c-7e2e-43d3-8eda-176881477c81") + uid2, _ := uuid.Parse("4015b55f-72f0-48a4-8d01-64168d50f627") + uid3, _ := uuid.Parse("827d1bf0-c5d4-4443-8708-d8b5de31fe74") + + tests := []struct { + name string + r *response + want []uuid.UUID + wantErr bool + }{ + { + name: "1", + r: r1, + want: []uuid.UUID{uid1, uid2, uid3}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadArrayOUUIDs() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadArrayOUUIDs() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("response.ReadArrayOUUIDs() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadArrayODates(t *testing.T) { + r1 := &response{message: bytes.NewBuffer([]byte{3, 0, 0, 0, + 11, 0x0, 0xa0, 0xcd, 0x88, 0x62, 0x1, 0x0, 0x0, + 11, 0x0, 0xf0, 0x23, 0x80, 0x6a, 0x1, 0x0, 0x0, + 11, 0x0, 0xf8, 0xc6, 0x81, 0x72, 0x1, 0x0, 0x0})} + dm1 := time.Date(2018, 4, 3, 0, 0, 0, 0, time.UTC) + dm2 := time.Date(2019, 5, 4, 0, 0, 0, 0, time.UTC) + dm3 := time.Date(2020, 6, 5, 0, 0, 0, 0, time.UTC) + + tests := []struct { + name string + r *response + want []time.Time + wantErr bool + }{ + { + name: "1", + r: r1, + want: []time.Time{dm1, dm2, dm3}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadArrayODates() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadArrayODates() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("response.ReadArrayODates() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadTimestamp(t *testing.T) { + r1 := &response{message: bytes.NewBuffer( + []byte{0xdb, 0xb, 0xe6, 0x8b, 0x62, 0x1, 0x0, 0x0, 0x55, 0xf8, 0x6, 0x0})} + tm := time.Date(2018, 4, 3, 14, 25, 32, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + + tests := []struct { + name string + r *response + want time.Time + wantErr bool + }{ + { + name: "1", + r: r1, + want: tm, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadTimestamp() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadTimestamp() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("response.ReadTimestamp() = %#v, want %#v", got, tt.want) + } + }) + } +} + +func Test_response_ReadArrayOTimestamps(t *testing.T) { + r1 := &response{message: bytes.NewBuffer([]byte{3, 0, 0, 0, + 33, 0xdb, 0xb, 0xe6, 0x8b, 0x62, 0x1, 0x0, 0x0, 0x55, 0xf8, 0x6, 0x0, + 33, 0xa3, 0x38, 0x74, 0x83, 0x6a, 0x1, 0x0, 0x0, 0x55, 0xf8, 0x6, 0x0, + 33, 0x6b, 0x1d, 0x4f, 0x85, 0x72, 0x1, 0x0, 0x0, 0x55, 0xf8, 0x6, 0x0})} + tm1 := time.Date(2018, 4, 3, 14, 25, 32, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + tm2 := time.Date(2019, 5, 4, 15, 26, 33, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + tm3 := time.Date(2020, 6, 5, 16, 27, 34, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + + tests := []struct { + name string + r *response + want []time.Time + wantErr bool + }{ + { + name: "1", + r: r1, + want: []time.Time{tm1, tm2, tm3}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadArrayOTimestamps() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadArrayOTimestamps() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("response.ReadArrayOTimestamps() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadTime(t *testing.T) { + r1 := &response{message: bytes.NewBuffer([]byte{0xdb, 0x6b, 0x18, 0x3, 0x0, 0x0, 0x0, 0x0})} + tm := time.Date(1, 1, 1, 14, 25, 32, int(time.Millisecond*123), time.UTC) + + tests := []struct { + name string + r *response + want time.Time + wantErr bool + }{ + { + name: "1", + r: r1, + want: tm, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadTime() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadTime() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("response.ReadTime() = %s, want %s", got.String(), tt.want.String()) + // t.Errorf("response.ReadTime() = %#v, want %#v", got, tt.want) + } + }) + } +} + +func Test_response_ReadArrayOTimes(t *testing.T) { + r1 := &response{message: bytes.NewBuffer([]byte{3, 0, 0, 0, + 36, 0xdb, 0x6b, 0x18, 0x3, 0x0, 0x0, 0x0, 0x0, + 36, 0xa3, 0x48, 0x50, 0x3, 0x0, 0x0, 0x0, 0x0, + 36, 0x6b, 0x25, 0x88, 0x3, 0x0, 0x0, 0x0, 0x0})} + tm4 := time.Date(1, 1, 1, 14, 25, 32, int(time.Millisecond*123), time.UTC) + tm5 := time.Date(1, 1, 1, 15, 26, 33, int(time.Millisecond*123), time.UTC) + tm6 := time.Date(1, 1, 1, 16, 27, 34, int(time.Millisecond*123), time.UTC) + + tests := []struct { + name string + r *response + want []time.Time + wantErr bool + }{ + { + name: "1", + r: r1, + want: []time.Time{tm4, tm5, tm6}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadArrayOTimes() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadArrayOTimes() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("response.ReadArrayOTimes() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_response_ReadObject(t *testing.T) { + r1 := &response{message: bytes.NewBuffer([]byte{1, 123})} + r2 := &response{message: bytes.NewBuffer([]byte{2, 0x39, 0x30})} + r3 := &response{message: bytes.NewBuffer([]byte{3, 0xD2, 0x02, 0x96, 0x49})} + r4 := &response{message: bytes.NewBuffer([]byte{4, 0x15, 0x81, 0xE9, 0x7D, 0xF4, 0x10, 0x22, 0x11})} + r5 := &response{message: bytes.NewBuffer([]byte{5, 0x65, 0x20, 0xf1, 0x47})} + r6 := &response{message: bytes.NewBuffer([]byte{6, 0xad, 0x69, 0x7e, 0x54, 0x34, 0x6f, 0x9d, 0x41})} + r7 := &response{message: bytes.NewBuffer([]byte{7, 0x41, 0x0})} + r8 := &response{message: bytes.NewBuffer([]byte{8, 1})} + r9 := &response{message: bytes.NewBuffer( + []byte{9, 0x0B, 0, 0, 0, 0x74, 0x65, 0x73, 0x74, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67})} + r10 := &response{message: bytes.NewBuffer([]byte{10, 0xd6, 0x58, 0x9d, 0xa7, 0xf8, 0xb1, 0x46, 0x87, 0xb5, + 0xbd, 0x2d, 0xdc, 0x73, 0x62, 0xa4, 0xa4})} + uid, _ := uuid.Parse("d6589da7-f8b1-4687-b5bd-2ddc7362a4a4") + r11 := &response{message: bytes.NewBuffer([]byte{11, 0x0, 0xa0, 0xcd, 0x88, 0x62, 0x1, 0x0, 0x0})} + dm := time.Date(2018, 4, 3, 0, 0, 0, 0, time.UTC) + r12 := &response{message: bytes.NewBuffer([]byte{12, 3, 0, 0, 0, 1, 2, 3})} + r13 := &response{message: bytes.NewBuffer([]byte{13, 3, 0, 0, 0, 1, 0, 2, 0, 3, 0})} + r14 := &response{message: bytes.NewBuffer( + []byte{14, 3, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0})} + r15 := &response{message: bytes.NewBuffer( + []byte{15, 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0})} + r16 := &response{message: bytes.NewBuffer( + []byte{16, 0x3, 0x0, 0x0, 0x0, 0xcd, 0xcc, 0x8c, 0x3f, 0xcd, 0xcc, 0xc, 0x40, 0x33, 0x33, 0x53, 0x40})} + r17 := &response{message: bytes.NewBuffer( + []byte{17, 3, 0, 0, 0, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99, 0xf1, 0x3f, 0x9a, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x1, 0x40, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0xa, 0x40})} + r18 := &response{message: bytes.NewBuffer([]byte{18, 3, 0, 0, 0, 0x41, 0x0, 0x42, 0x0, 0x2f, 0x4})} + r19 := &response{message: bytes.NewBuffer([]byte{19, 3, 0, 0, 0, 1, 0, 1})} + r20 := &response{message: bytes.NewBuffer([]byte{20, 3, 0, 0, 0, + 0x9, 3, 0, 0, 0, 0x6f, 0x6e, 0x65, + 0x9, 3, 0, 0, 0, 0x74, 0x77, 0x6f, + 0x9, 6, 0, 0, 0, 0xd1, 0x82, 0xd1, 0x80, 0xd0, 0xb8})} + r21 := &response{message: bytes.NewBuffer([]byte{21, 3, 0, 0, 0, + 10, 0xa0, 0xc0, 0x7c, 0x4c, 0x7e, 0x2e, 0x43, 0xd3, 0x8e, 0xda, 0x17, 0x68, 0x81, 0x47, 0x7c, 0x81, + 10, 0x40, 0x15, 0xb5, 0x5f, 0x72, 0xf0, 0x48, 0xa4, 0x8d, 0x1, 0x64, 0x16, 0x8d, 0x50, 0xf6, 0x27, + 10, 0x82, 0x7d, 0x1b, 0xf0, 0xc5, 0xd4, 0x44, 0x43, 0x87, 0x8, 0xd8, 0xb5, 0xde, 0x31, 0xfe, 0x74})} + uid1, _ := uuid.Parse("a0c07c4c-7e2e-43d3-8eda-176881477c81") + uid2, _ := uuid.Parse("4015b55f-72f0-48a4-8d01-64168d50f627") + uid3, _ := uuid.Parse("827d1bf0-c5d4-4443-8708-d8b5de31fe74") + r22 := &response{message: bytes.NewBuffer([]byte{22, 3, 0, 0, 0, + 11, 0x0, 0xa0, 0xcd, 0x88, 0x62, 0x1, 0x0, 0x0, + 11, 0x0, 0xf0, 0x23, 0x80, 0x6a, 0x1, 0x0, 0x0, + 11, 0x0, 0xf8, 0xc6, 0x81, 0x72, 0x1, 0x0, 0x0})} + dm1 := time.Date(2018, 4, 3, 0, 0, 0, 0, time.UTC) + dm2 := time.Date(2019, 5, 4, 0, 0, 0, 0, time.UTC) + dm3 := time.Date(2020, 6, 5, 0, 0, 0, 0, time.UTC) + r33 := &response{message: bytes.NewBuffer([]byte{33, 0xdb, 0xb, 0xe6, 0x8b, 0x62, 0x1, 0x0, 0x0, + 0x55, 0xf8, 0x6, 0x0})} + tm := time.Date(2018, 4, 3, 14, 25, 32, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + r34 := &response{message: bytes.NewBuffer([]byte{34, 3, 0, 0, 0, + 33, 0xdb, 0xb, 0xe6, 0x8b, 0x62, 0x1, 0x0, 0x0, 0x55, 0xf8, 0x6, 0x0, + 33, 0xa3, 0x38, 0x74, 0x83, 0x6a, 0x1, 0x0, 0x0, 0x55, 0xf8, 0x6, 0x0, + 33, 0x6b, 0x1d, 0x4f, 0x85, 0x72, 0x1, 0x0, 0x0, 0x55, 0xf8, 0x6, 0x0})} + tm1 := time.Date(2018, 4, 3, 14, 25, 32, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + tm2 := time.Date(2019, 5, 4, 15, 26, 33, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + tm3 := time.Date(2020, 6, 5, 16, 27, 34, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) + r36 := &response{message: bytes.NewBuffer([]byte{36, 0xdb, 0x6b, 0x18, 0x3, 0x0, 0x0, 0x0, 0x0})} + tm4 := time.Date(1, 1, 1, 14, 25, 32, int(time.Millisecond*123), time.UTC) + r37 := &response{message: bytes.NewBuffer([]byte{37, 3, 0, 0, 0, + 36, 0xdb, 0x6b, 0x18, 0x3, 0x0, 0x0, 0x0, 0x0, + 36, 0xa3, 0x48, 0x50, 0x3, 0x0, 0x0, 0x0, 0x0, + 36, 0x6b, 0x25, 0x88, 0x3, 0x0, 0x0, 0x0, 0x0})} + tm5 := time.Date(1, 1, 1, 14, 25, 32, int(time.Millisecond*123), time.UTC) + tm6 := time.Date(1, 1, 1, 15, 26, 33, int(time.Millisecond*123), time.UTC) + tm7 := time.Date(1, 1, 1, 16, 27, 34, int(time.Millisecond*123), time.UTC) + r101 := &response{message: bytes.NewBuffer([]byte{101})} + + tests := []struct { + name string + r *response + want interface{} + wantErr bool + }{ + { + name: "byte", + r: r1, + want: byte(123), + }, + { + name: "short", + r: r2, + want: int16(12345), + }, + { + name: "int", + r: r3, + want: int32(1234567890), + }, + { + name: "long", + r: r4, + want: int64(1234567890123456789), + }, + { + name: "float", + r: r5, + want: float32(123456.789), + }, + { + name: "double", + r: r6, + want: float64(123456789.12345), + }, + { + name: "char", + r: r7, + want: Char('A'), + }, + { + name: "bool", + r: r8, + want: true, + }, + { + name: "string", + r: r9, + want: "test string", + }, + { + name: "UUID", + r: r10, + want: uid, + }, + { + name: "Date", + r: r11, + want: dm, + }, + { + name: "byte array", + r: r12, + want: []byte{1, 2, 3}, + }, + { + name: "short array", + r: r13, + want: []int16{1, 2, 3}, + }, + { + name: "int array", + r: r14, + want: []int32{1, 2, 3}, + }, + { + name: "long array", + r: r15, + want: []int64{1, 2, 3}, + }, + { + name: "float array", + r: r16, + want: []float32{1.1, 2.2, 3.3}, + }, + { + name: "double array", + r: r17, + want: []float64{1.1, 2.2, 3.3}, + }, + { + name: "char array", + r: r18, + want: []Char{'A', 'B', 'Я'}, + }, + { + name: "bool array", + r: r19, + want: []bool{true, false, true}, + }, + { + name: "string array", + r: r20, + want: []string{"one", "two", "три"}, + }, + { + name: "UUID array", + r: r21, + want: []uuid.UUID{uid1, uid2, uid3}, + }, + { + name: "date array", + r: r22, + want: []time.Time{dm1, dm2, dm3}, + }, + { + name: "Timestamp", + r: r33, + want: tm, + }, + { + name: "Timestamp array", + r: r34, + want: []time.Time{tm1, tm2, tm3}, + }, + { + name: "Time", + r: r36, + want: tm4, + }, + { + name: "Time array", + r: r37, + want: []time.Time{tm5, tm6, tm7}, + }, + { + name: "NULL", + r: r101, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadObject() + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadObject() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("response.ReadObject() = %#v, want %#v", got, tt.want) + } + }) + } +} + +func Test_response_ReadFrom(t *testing.T) { + rr := bytes.NewBuffer([]byte{1, 0, 0, 0, 1}) + + type args struct { + rr io.Reader + } + tests := []struct { + name string + r *response + args args + want int64 + wantErr bool + }{ + { + name: "1", + r: &response{}, + args: args{ + rr: rr, + }, + want: 4 + 1, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.r.ReadFrom(tt.args.rr) + if (err != nil) != tt.wantErr { + t.Errorf("response.ReadFrom() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("response.ReadFrom() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/binary/v1/types.go b/binary/v1/types.go new file mode 100644 index 0000000..044ccb9 --- /dev/null +++ b/binary/v1/types.go @@ -0,0 +1,70 @@ +package ignite + +import ( + "time" +) + +const ( + // Supported standard types and their type codes are as follows: + typeByte = 1 + typeShort = 2 + typeInt = 3 + typeLong = 4 + typeFloat = 5 + typeDouble = 6 + typeChar = 7 + typeBool = 8 + typeString = 9 + typeUUID = 10 + typeDate = 11 + typeByteArray = 12 + typeShortArray = 13 + typeIntArray = 14 + typeLongArray = 15 + typeFloatArray = 16 + typeDoubleArray = 17 + typeCharArray = 18 + typeBoolArray = 19 + typeStringArray = 20 + typeUUIDArray = 21 + typeDateArray = 22 + // TODO: Object array = 23 + // TODO: Collection = 24 + // TODO: Map = 25 + // TODO: Enum = 28 + // TODO: Enum Array = 29 + // TODO: Decimal = 30 + // TODO: Decimal Array = 31 + typeTimestamp = 33 + typeTimestampArray = 34 + typeTime = 36 + typeTimeArray = 37 + typeNULL = 101 +) + +// Char is Apache Ignite "char" type +type Char rune + +// Date is Unix time, the number of MILLISECONDS elapsed +// since January 1, 1970 UTC. +type Date int64 + +// ToDate converts Golang time.Time to Apache Ignite Date +func ToDate(t time.Time) Date { + t1 := t.UTC() + t2 := t1.Unix() * 1000 + t2 += int64(t1.Nanosecond()) / int64(time.Millisecond) + return Date(t2) +} + +// Time is Apache Ignite Time type +type Time int64 + +// ToTime converts Golang time.Time to Apache Ignite Time +func ToTime(t time.Time) Time { + t1 := t.UTC() + t2 := time.Date(1970, 1, 1, t1.Hour(), t1.Minute(), t1.Second(), t1.Nanosecond(), time.UTC) + t3 := t2.Unix() * 1000 + t3 += int64(t2.Nanosecond()) / int64(time.Millisecond) + return Time(t3) +} diff --git a/examples_test.go b/examples_test.go index f4ad3b7..6c4a642 100644 --- a/examples_test.go +++ b/examples_test.go @@ -15,7 +15,7 @@ func Test_SQL_Driver(t *testing.T) { ctx := context.Background() // open connection - db, err := sql.Open("ignite", "tcp://localhost:10800/TestDB3?version=1.0.0&&page-size=10000&timeout=5000") + db, err := sql.Open("ignite", "tcp://localhost:10800/ExampleDB?version=1.0.0&&page-size=10000&timeout=5000") if err != nil { t.Fatalf("failed to open connection: %v", err) } @@ -98,7 +98,14 @@ func Test_Key_Value(t *testing.T) { ctx := context.Background() // connect - c, err := ignite.NewClient(ctx, "tcp", "localhost:10800", 1, 0, 0) + c, err := ignite.Connect(ctx, ignite.ConnInfo{ + Network: "tcp", + Host: "localhost", + Port: 10800, + Major: 1, + Minor: 0, + Patch: 0, + }) if err != nil { t.Fatalf("failed connect to server: %v", err) } diff --git a/sql/common/conn.go b/sql/common/conn.go index 65fcc35..b34bd67 100644 --- a/sql/common/conn.go +++ b/sql/common/conn.go @@ -1,21 +1,18 @@ package common import ( - "github.com/Masterminds/semver" + "github.com/amsokol/ignite-go-client/binary/v1" ) // ConnInfo contains Apache Ignite cluster connection and query execution parameters type ConnInfo struct { URL string - Network string - - Address string + // Apache Ignite client connection information + ignite.ConnInfo Cache string - Version *semver.Version - // Schema for the query; can be empty, in which case default PUBLIC schema will be used. Schema string diff --git a/sql/connector.go b/sql/connector.go index e755ed9..3a71e01 100644 --- a/sql/connector.go +++ b/sql/connector.go @@ -29,11 +29,11 @@ type connector struct { // The returned connection is only used by one goroutine at a // time. func (c *connector) Connect(ctx context.Context) (driver.Conn, error) { - switch c.info.Version.Major() { + switch c.info.Major { case 1: return v1.Connect(ctx, c.info) default: - return nil, errors.Errorf("unsupported protocol version: %v", c.info.Version) + return nil, errors.Errorf("unsupported protocol version: v%d.%d.%d", c.info.Major, c.info.Minor, c.info.Patch) } } diff --git a/sql/connector_test.go b/sql/connector_test.go index 6c9a285..8375ac6 100644 --- a/sql/connector_test.go +++ b/sql/connector_test.go @@ -5,13 +5,12 @@ package ignitesql import ( "context" "database/sql/driver" - //"reflect" "testing" ) func Test_connector_Connect(t *testing.T) { d := &Driver{} - ci, err := d.OpenConnector("tcp://localhost:10800/TestDB2") + ci, err := d.OpenConnector("tcp://localhost:10800/DriverOpen") if err != nil { t.Errorf("failed to open connector: %v", err) return @@ -43,12 +42,9 @@ func Test_connector_Connect(t *testing.T) { t.Errorf("connector.Connect() error = %v, wantErr %v", err, tt.wantErr) return } - /* - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("connector.Connect() = %v, want %v", got, tt.want) - } - */ - _ = got.Close() + if got != nil { + _ = got.Close() + } }) } } diff --git a/sql/driver-go1.10_test.go b/sql/driver-go1.10_test.go index ac88baf..b28e2d8 100644 --- a/sql/driver-go1.10_test.go +++ b/sql/driver-go1.10_test.go @@ -22,30 +22,25 @@ func TestDriver_OpenConnector(t *testing.T) { name: "success test 1", d: &Driver{}, args: args{ - name: "tcp://localhost:10800/TestDB2", + name: "tcp://localhost:10800/OpenConnector", }, }, { name: "failed test 2", d: &Driver{}, args: args{ - name: "tcp://localhost:10800/TestDB2?invalid-param=true", + name: "tcp://localhost:10800/OpenConnector?invalid-param=true", }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - /*got*/ _, err := tt.d.OpenConnector(tt.args.name) + _, err := tt.d.OpenConnector(tt.args.name) if (err != nil) != tt.wantErr { t.Errorf("Driver.OpenConnector() error = %v, wantErr %v", err, tt.wantErr) return } - /* - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Driver.OpenConnector() = %v, want %v", got, tt.want) - } - */ }) } } diff --git a/sql/driver.go b/sql/driver.go index 6af29ad..599dd42 100644 --- a/sql/driver.go +++ b/sql/driver.go @@ -29,11 +29,11 @@ func (d *Driver) Open(name string) (driver.Conn, error) { if err != nil { return nil, errors.Wrapf(err, "failed to parse connection name") } - switch ci.Version.Major() { + switch ci.Major { case 1: return v1.Connect(ctx, ci) default: - return nil, errors.Errorf("unsupported protocol version: %v", ci.Version) + return nil, errors.Errorf("unsupported protocol version: v%d.%d.%d", ci.Major, ci.Minor, ci.Patch) } } @@ -51,7 +51,7 @@ func (d *Driver) Open(name string) (driver.Conn, error) { // URL parameters (param1,...paramN): // | Name | Mandatory | Description | Default value | // |--------------------|-----------|---------------------------------------------------------------|-----------------------------------| -// | schema | no | Database schema | "" (PUBLIC schema will be used) | +// | schema | no | Database schema | "" (PUBLIC schema will be used) | // | version | no | Binary protocol version in Semantic Version format | 1.0.0 | // | page-size | no | Query cursor page size | 10000 | // | max-rows | no | Max rows to return by query | 0 (looks like it means unlimited) | @@ -76,19 +76,18 @@ func (d *Driver) parseURL(name string) (common.ConnInfo, error) { ci.Network = "tcp" } - if ci.Address = u.Hostname(); len(ci.Address) == 0 { - ci.Address = "127.0.0.1" + if ci.Host = u.Hostname(); len(ci.Host) == 0 { + ci.Host = "127.0.0.1" } - if len(u.Port()) == 0 { - ci.Address += ":10800" - } else { - ci.Address += ":" + u.Port() + ci.Port, _ = strconv.Atoi(u.Port()) + if ci.Port == 0 { + ci.Port = 10800 } ci.Cache = strings.Trim(u.Path, "/") // default values - ci.Version, _ = semver.NewVersion("1.0.0") + ver, _ := semver.NewVersion("1.0.0") ci.PageSize = 10000 for k, v := range u.Query() { @@ -101,7 +100,7 @@ func (d *Driver) parseURL(name string) (common.ConnInfo, error) { ci.Schema = val case "version": if len(val) > 0 { - ci.Version, err = semver.NewVersion(val) + ver, err = semver.NewVersion(val) } case "page-size": if len(val) > 0 { @@ -136,6 +135,10 @@ func (d *Driver) parseURL(name string) (common.ConnInfo, error) { } ci.URL = name + ci.ConnInfo.Major = int(ver.Major()) + ci.ConnInfo.Minor = int(ver.Minor()) + ci.ConnInfo.Patch = int(ver.Patch()) + return ci, nil } diff --git a/sql/driver_test.go b/sql/driver_test.go index 8ca7e84..c0e6a18 100644 --- a/sql/driver_test.go +++ b/sql/driver_test.go @@ -5,8 +5,7 @@ import ( "reflect" "testing" - "github.com/Masterminds/semver" - + "github.com/amsokol/ignite-go-client/binary/v1" "github.com/amsokol/ignite-go-client/sql/common" ) @@ -77,9 +76,6 @@ func TestDriver_parseYesNo(t *testing.T) { } func TestDriver_parseURL(t *testing.T) { - ver1, _ := semver.NewVersion("1.1.1") - ver2, _ := semver.NewVersion("1.0.0") - type args struct { name string } @@ -120,10 +116,15 @@ func TestDriver_parseURL(t *testing.T) { "&enforce-join-order=yes" + "&collocated=yes" + "&lazy-query=yes", - Network: "tcp", - Address: "localhost:10800", + ConnInfo: ignite.ConnInfo{ + Network: "tcp", + Host: "localhost", + Port: 10800, + Major: 1, + Minor: 1, + Patch: 1, + }, Cache: "TestDB2", - Version: ver1, Schema: "SCHEMA", PageSize: 100, MaxRows: 99, @@ -143,11 +144,16 @@ func TestDriver_parseURL(t *testing.T) { name: "tcp://localhost/TestDB2", }, want: common.ConnInfo{ - URL: "tcp://localhost/TestDB2", - Network: "tcp", - Address: "localhost:10800", + URL: "tcp://localhost/TestDB2", + ConnInfo: ignite.ConnInfo{ + Network: "tcp", + Host: "localhost", + Port: 10800, + Major: 1, + Minor: 0, + Patch: 0, + }, Cache: "TestDB2", - Version: ver2, PageSize: 10000, }, }, @@ -158,11 +164,16 @@ func TestDriver_parseURL(t *testing.T) { name: "tcp://localhost/TestDB2?invalid-param=true", }, want: common.ConnInfo{ - URL: "tcp://localhost/TestDB2", - Network: "tcp", - Address: "localhost:10800", + URL: "tcp://localhost/TestDB2", + ConnInfo: ignite.ConnInfo{ + Network: "tcp", + Host: "localhost", + Port: 10800, + Major: 1, + Minor: 0, + Patch: 0, + }, Cache: "TestDB2", - Version: ver2, PageSize: 10000, }, wantErr: true, @@ -197,30 +208,28 @@ func TestDriver_Open(t *testing.T) { name: "success test 1", d: &Driver{}, args: args{ - name: "tcp://localhost:10800/TestDB2", + name: "tcp://localhost:10800/DriverOpen", }, }, { name: "failed test 2", d: &Driver{}, args: args{ - name: "tcp://localhost:10800/TestDB2?invalid-param=true", + name: "tcp://localhost:10800/DriverOpen?invalid-param=true", }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - /*got*/ _, err := tt.d.Open(tt.args.name) + got, err := tt.d.Open(tt.args.name) if (err != nil) != tt.wantErr { t.Errorf("Driver.Open() error = %v, wantErr %v", err, tt.wantErr) return } - /* - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Driver.Open() = %v, want %v", got, tt.want) - } - */ + if got != nil { + _ = got.Close() + } }) } } diff --git a/sql/v1/conn.go b/sql/v1/conn.go index 7958782..d71642d 100644 --- a/sql/v1/conn.go +++ b/sql/v1/conn.go @@ -4,7 +4,6 @@ import ( "context" "database/sql/driver" "fmt" - // "fmt" "runtime" "sort" "time" @@ -29,7 +28,7 @@ type conn struct { // isConnected return true if connection to the cluster is active func (c *conn) isConnected() bool { - return c.client != nil && c.client.IsConnected() + return c.client != nil && c.client.Connected() } // resourceClose closes a resource, such as query cursor. @@ -216,9 +215,9 @@ func (c *conn) QueryContext(ctx context.Context, query string, args []driver.Nam // -func (c *conn) QueryNexPageContext(ctx context.Context, cursorID int64) (ignite.Response, error) { +func (c *conn) QueryNexPageContext(ctx context.Context, cursorID int64) (*ignite.ResponseOperation, error) { if !c.isConnected() { - return ignite.Response{}, driver.ErrBadConn + return nil, driver.ErrBadConn } return c.client.QuerySQLFieldsCursorGetPageRaw(cursorID) } @@ -231,8 +230,7 @@ func Connect(ctx context.Context, ci common.ConnInfo) (driver.Conn, error) { defer cancel() } - client, err := ignite.NewClient(ctx, ci.Network, ci.Address, - int16(ci.Version.Major()), int16(ci.Version.Minor()), int16(ci.Version.Patch())) + client, err := ignite.Connect(ctx, ci.ConnInfo) if err != nil { return nil, fmt.Errorf("failed to create client: %v", err) } diff --git a/sql/v1/conn_test.go b/sql/v1/conn_test.go index 04adaa1..4cc8868 100644 --- a/sql/v1/conn_test.go +++ b/sql/v1/conn_test.go @@ -7,13 +7,11 @@ import ( "testing" "time" - "github.com/Masterminds/semver" + "github.com/amsokol/ignite-go-client/binary/v1" "github.com/amsokol/ignite-go-client/sql/common" ) func TestConnect(t *testing.T) { - ver, _ := semver.NewVersion("1.0.0") - type args struct { ctx context.Context ci common.ConnInfo @@ -29,11 +27,16 @@ func TestConnect(t *testing.T) { args: args{ ctx: context.Background(), ci: common.ConnInfo{ - URL: "tcp://localhost:10800/TestDB2", - Network: "tcp", - Address: "localhost:10800", - Cache: "TestDB2", - Version: ver, + URL: "tcp://localhost:10800/DriverOpen", + ConnInfo: ignite.ConnInfo{ + Network: "tcp", + Host: "localhost", + Port: 10800, + Major: 1, + Minor: 0, + Patch: 0, + }, + Cache: "DriverOpen", PageSize: 10000, }, }, @@ -46,24 +49,26 @@ func TestConnect(t *testing.T) { t.Errorf("Connect() error = %v, wantErr %v", err, tt.wantErr) return } - /* - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Connect() = %v, want %v", got, tt.want) - } - */ - _ = got.Close() + + if got != nil { + _ = got.Close() + } }) } } func Test_conn_Close(t *testing.T) { - ver, _ := semver.NewVersion("1.0.0") ci, err := Connect(context.Background(), common.ConnInfo{ - URL: "tcp://localhost:10800/TestDB2", - Network: "tcp", - Address: "localhost:10800", - Cache: "TestDB2", - Version: ver, + URL: "tcp://localhost:10800/DriverOpen", + ConnInfo: ignite.ConnInfo{ + Network: "tcp", + Host: "localhost", + Port: 10800, + Major: 1, + Minor: 0, + Patch: 0, + }, + Cache: "DriverOpen", PageSize: 10000, }) if err != nil { @@ -92,13 +97,17 @@ func Test_conn_Close(t *testing.T) { } func Test_conn_ExecContext(t *testing.T) { - ver, _ := semver.NewVersion("1.0.0") ci, err := Connect(context.Background(), common.ConnInfo{ - URL: "tcp://localhost:10800/TestDB2", - Network: "tcp", - Address: "localhost:10800", - Cache: "TestDB2", - Version: ver, + URL: "tcp://localhost:10800/ConnExecContext", + ConnInfo: ignite.ConnInfo{ + Network: "tcp", + Host: "localhost", + Port: 10800, + Major: 1, + Minor: 0, + Patch: 0, + }, + Cache: "ConnExecContext", PageSize: 10000, }) if err != nil { @@ -107,8 +116,8 @@ func Test_conn_ExecContext(t *testing.T) { } c, _ := ci.(*conn) defer c.Close() - defer c.client.CacheRemoveAll("TestDB2", false) - _ = c.client.CacheRemoveAll("TestDB2", false) + defer c.client.CacheRemoveAll("ConnExecContext", false) + _ = c.client.CacheRemoveAll("ConnExecContext", false) tm := time.Date(2018, 4, 3, 14, 25, 32, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) type args struct { @@ -193,13 +202,17 @@ func Test_conn_ExecContext(t *testing.T) { } func Test_conn_QueryContext(t *testing.T) { - ver, _ := semver.NewVersion("1.0.0") ci, err := Connect(context.Background(), common.ConnInfo{ - URL: "tcp://localhost:10800/TestDB2", - Network: "tcp", - Address: "localhost:10800", - Cache: "TestDB2", - Version: ver, + URL: "tcp://localhost:10800/ConnQueryContext", + ConnInfo: ignite.ConnInfo{ + Network: "tcp", + Host: "localhost", + Port: 10800, + Major: 1, + Minor: 0, + Patch: 0, + }, + Cache: "ConnQueryContext", PageSize: 2, /* test server cursor */ }) if err != nil { @@ -208,8 +221,8 @@ func Test_conn_QueryContext(t *testing.T) { } c, _ := ci.(*conn) defer c.Close() - defer c.client.CacheRemoveAll("TestDB2", false) - _ = c.client.CacheRemoveAll("TestDB2", false) + defer c.client.CacheRemoveAll("ConnQueryContext", false) + _ = c.client.CacheRemoveAll("ConnQueryContext", false) tm := time.Date(2018, 4, 3, 14, 25, 32, int(time.Millisecond*123+time.Microsecond*456+789), time.UTC) _, err = c.ExecContext(context.Background(), "INSERT INTO Organization(_key, name, foundDateTime) VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?), (?, ?, ?)", @@ -296,13 +309,17 @@ func Test_conn_QueryContext(t *testing.T) { } func Test_conn_Ping(t *testing.T) { - ver, _ := semver.NewVersion("1.0.0") ci, err := Connect(context.Background(), common.ConnInfo{ - URL: "tcp://localhost:10800/TestDB2", - Network: "tcp", - Address: "localhost:10800", - Cache: "TestDB2", - Version: ver, + URL: "tcp://localhost:10800/DriverOpen", + ConnInfo: ignite.ConnInfo{ + Network: "tcp", + Host: "localhost", + Port: 10800, + Major: 1, + Minor: 0, + Patch: 0, + }, + Cache: "DriverOpen", PageSize: 10000, }) if err != nil { diff --git a/sql/v1/rows.go b/sql/v1/rows.go index 701bd39..38fe97c 100644 --- a/sql/v1/rows.go +++ b/sql/v1/rows.go @@ -19,7 +19,7 @@ type Rows interface { type rows struct { conn *conn - response ignite.Response + response *ignite.ResponseOperation id int64 fields []string rowsLeft int @@ -56,7 +56,7 @@ func (r *rows) Next(dest []driver.Value) error { var err error if r.rowsLeft == 0 { var hasMore bool - if err = r.response.ReadPrimitives(&hasMore); err != nil { + if hasMore, err = r.response.ReadBool(); err != nil { // prevent resource leak on server _ = r.Close() return errors.Wrapf(err, "failed to read more records flag") @@ -71,7 +71,7 @@ func (r *rows) Next(dest []driver.Value) error { } // read data var rowCount int32 - if err = r.response.ReadPrimitives(&rowCount); err != nil { + if rowCount, err = r.response.ReadInt(); err != nil { // prevent resource leak on server _ = r.Close() return errors.Wrapf(err, "failed to read row count") @@ -91,18 +91,22 @@ func (r *rows) Next(dest []driver.Value) error { } // newRows creates new Rows object -func newRows(conn *conn, r ignite.Response) (driver.Rows, error) { +func newRows(conn *conn, r *ignite.ResponseOperation) (driver.Rows, error) { + var err error // read field names var id int64 var fieldCount int32 - if err := r.ReadPrimitives(&id, &fieldCount); err != nil { + if id, err = r.ReadLong(); err != nil { + return nil, errors.Wrapf(err, "failed to read request ID") + } + if fieldCount, err = r.ReadInt(); err != nil { return nil, errors.Wrapf(err, "failed to read field count") } // response MUST return field names fields := make([]string, 0, fieldCount) for i := 0; i < int(fieldCount); i++ { var s string - if err := r.ReadPrimitives(&s); err != nil { + if s, err = r.ReadOString(); err != nil { return nil, errors.Wrapf(err, "failed to read field name with index %d", i) } fields = append(fields, s) @@ -110,7 +114,7 @@ func newRows(conn *conn, r ignite.Response) (driver.Rows, error) { // read row count var rowCount int32 - if err := r.ReadPrimitives(&rowCount); err != nil { + if rowCount, err = r.ReadInt(); err != nil { return nil, errors.Wrapf(err, "failed to read row count") } diff --git a/testdata/configuration-for-tests.xml b/testdata/configuration-for-tests.xml index 211c653..cee82d7 100644 --- a/testdata/configuration-for-tests.xml +++ b/testdata/configuration-for-tests.xml @@ -30,7 +30,347 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -82,7 +422,7 @@ - + @@ -134,7 +474,7 @@ - + diff --git a/testdata/run.cmd b/testdata/run.cmd new file mode 100644 index 0000000..ddecb56 --- /dev/null +++ b/testdata/run.cmd @@ -0,0 +1 @@ +@ignite.bat .\configuration-for-tests.xml \ No newline at end of file