diff --git a/CHANGELOG.md b/CHANGELOG.md index 19fe823e3..d9869bc91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ * Added `internal/xsync.{OnceFunc,OnceValue}` * Updated `google.golang.org/protobuf` from `v1.31.0` to `v.33.0` * Added `ydb.ParamsBuilder().Pg().{Value,Int4,Int8,Unknown}` for postgres arguments +* Added `Tuple` support for `ydb.ParamsBuilder()` ## v3.57.4 * Added client pid to each gRPC requests to YDB over header `x-ydb-client-pid` diff --git a/internal/params/parameters.go b/internal/params/parameters.go index 756946093..18210cc16 100644 --- a/internal/params/parameters.go +++ b/internal/params/parameters.go @@ -127,6 +127,13 @@ func (p *Parameter) BeginDict() *dict { } } +func (p *Parameter) BeginTuple() *tuple { + return &tuple{ + parent: p.parent, + name: p.name, + } +} + func (p *Parameter) Text(v string) Builder { p.value = value.TextValue(v) p.parent.params = append(p.parent.params, p) diff --git a/internal/params/tuple.go b/internal/params/tuple.go new file mode 100644 index 000000000..0762cdb05 --- /dev/null +++ b/internal/params/tuple.go @@ -0,0 +1,172 @@ +package params + +import ( + "time" + + "github.com/ydb-platform/ydb-go-sdk/v3/internal/value" +) + +type ( + tuple struct { + parent Builder + name string + values []value.Value + } + tupleItem struct { + parent *tuple + } +) + +func (t *tuple) Add() *tupleItem { + return &tupleItem{ + parent: t, + } +} + +func (t *tuple) AddItems(items ...value.Value) *tuple { + t.values = append(t.values, items...) + + return t +} + +func (t *tuple) EndTuple() Builder { + t.parent.params = append(t.parent.params, &Parameter{ + parent: t.parent, + name: t.name, + value: value.TupleValue(t.values...), + }) + + return t.parent +} + +func (t *tupleItem) Text(v string) *tuple { + t.parent.values = append(t.parent.values, value.TextValue(v)) + + return t.parent +} + +func (t *tupleItem) Bytes(v []byte) *tuple { + t.parent.values = append(t.parent.values, value.BytesValue(v)) + + return t.parent +} + +func (t *tupleItem) Bool(v bool) *tuple { + t.parent.values = append(t.parent.values, value.BoolValue(v)) + + return t.parent +} + +func (t *tupleItem) Uint64(v uint64) *tuple { + t.parent.values = append(t.parent.values, value.Uint64Value(v)) + + return t.parent +} + +func (t *tupleItem) Int64(v int64) *tuple { + t.parent.values = append(t.parent.values, value.Int64Value(v)) + + return t.parent +} + +func (t *tupleItem) Uint32(v uint32) *tuple { + t.parent.values = append(t.parent.values, value.Uint32Value(v)) + + return t.parent +} + +func (t *tupleItem) Int32(v int32) *tuple { + t.parent.values = append(t.parent.values, value.Int32Value(v)) + + return t.parent +} + +func (t *tupleItem) Uint16(v uint16) *tuple { + t.parent.values = append(t.parent.values, value.Uint16Value(v)) + + return t.parent +} + +func (t *tupleItem) Int16(v int16) *tuple { + t.parent.values = append(t.parent.values, value.Int16Value(v)) + + return t.parent +} + +func (t *tupleItem) Uint8(v uint8) *tuple { + t.parent.values = append(t.parent.values, value.Uint8Value(v)) + + return t.parent +} + +func (t *tupleItem) Int8(v int8) *tuple { + t.parent.values = append(t.parent.values, value.Int8Value(v)) + + return t.parent +} + +func (t *tupleItem) Float(v float32) *tuple { + t.parent.values = append(t.parent.values, value.FloatValue(v)) + + return t.parent +} + +func (t *tupleItem) Double(v float64) *tuple { + t.parent.values = append(t.parent.values, value.DoubleValue(v)) + + return t.parent +} + +func (t *tupleItem) Decimal(v [16]byte, precision, scale uint32) *tuple { + t.parent.values = append(t.parent.values, value.DecimalValue(v, precision, scale)) + + return t.parent +} + +func (t *tupleItem) Timestamp(v time.Time) *tuple { + t.parent.values = append(t.parent.values, value.TimestampValueFromTime(v)) + + return t.parent +} + +func (t *tupleItem) Date(v time.Time) *tuple { + t.parent.values = append(t.parent.values, value.DateValueFromTime(v)) + + return t.parent +} + +func (t *tupleItem) Datetime(v time.Time) *tuple { + t.parent.values = append(t.parent.values, value.DatetimeValueFromTime(v)) + + return t.parent +} + +func (t *tupleItem) Interval(v time.Duration) *tuple { + t.parent.values = append(t.parent.values, value.IntervalValueFromDuration(v)) + + return t.parent +} + +func (t *tupleItem) JSON(v string) *tuple { + t.parent.values = append(t.parent.values, value.JSONValue(v)) + + return t.parent +} + +func (t *tupleItem) JSONDocument(v string) *tuple { + t.parent.values = append(t.parent.values, value.JSONDocumentValue(v)) + + return t.parent +} + +func (t *tupleItem) YSON(v []byte) *tuple { + t.parent.values = append(t.parent.values, value.YSONValue(v)) + + return t.parent +} + +func (t *tupleItem) UUID(v [16]byte) *tuple { + t.parent.values = append(t.parent.values, value.UUIDValue(v)) + + return t.parent +} diff --git a/internal/params/tuple_test.go b/internal/params/tuple_test.go new file mode 100644 index 000000000..9938fae0c --- /dev/null +++ b/internal/params/tuple_test.go @@ -0,0 +1,735 @@ +package params + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + "github.com/ydb-platform/ydb-go-genproto/protos/Ydb" + + "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator" + "github.com/ydb-platform/ydb-go-sdk/v3/internal/value" + "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest" +) + +func TestTuple(t *testing.T) { + for _, tt := range []struct { + name string + builder Builder + params map[string]*Ydb.TypedValue + }{ + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Uint64(123).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_UINT64, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Uint64Value{ + Uint64Value: 123, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Int64(123).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_INT64, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Int64Value{ + Int64Value: 123, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Uint32(123).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_UINT32, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Uint32Value{ + Uint32Value: 123, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Int32(123).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_INT32, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Int32Value{ + Int32Value: 123, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Uint16(123).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_UINT16, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Uint32Value{ + Uint32Value: 123, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Int16(123).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_INT16, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Int32Value{ + Int32Value: 123, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Uint8(123).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_UINT8, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Uint32Value{ + Uint32Value: 123, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Int8(123).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_INT8, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Int32Value{ + Int32Value: 123, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Bool(true).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_BOOL, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_BoolValue{ + BoolValue: true, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Text("test").EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_UTF8, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_TextValue{ + TextValue: "test", + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Bytes([]byte("test")).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_STRING, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_BytesValue{ + BytesValue: []byte("test"), + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Float(123).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_FLOAT, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_FloatValue{ + FloatValue: 123, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Double(123).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_DOUBLE, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_DoubleValue{ + DoubleValue: 123, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Interval(time.Second).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_INTERVAL, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Int64Value{ + Int64Value: 1000000, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Datetime(time.Unix(123456789, 456)).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_DATETIME, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Uint32Value{ + Uint32Value: 123456789, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Date(time.Unix(123456789, 456)).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_DATE, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Uint32Value{ + Uint32Value: 1428, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Timestamp(time.Unix(123456789, 456)).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_TIMESTAMP, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Uint64Value{ + Uint64Value: 123456789000000, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Decimal([...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}, 22, 9).EndTuple(), //nolint:lll + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_DecimalType{ + DecimalType: &Ydb.DecimalType{ + Precision: 22, + Scale: 9, + }, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + High_128: 72623859790382856, + Value: &Ydb.Value_Low_128{ + Low_128: 648519454493508870, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().JSON(`{"a": 1,"b": "B"}`).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_JSON, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_TextValue{ + TextValue: `{"a": 1,"b": "B"}`, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().JSONDocument(`{"a": 1,"b": "B"}`).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_JSON_DOCUMENT, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_TextValue{ + TextValue: `{"a": 1,"b": "B"}`, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().YSON([]byte(`[ 1; 2; 3; 4; 5 ]`)).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_YSON, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_BytesValue{ + BytesValue: []byte(`[ 1; 2; 3; 4; 5 ]`), + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add(). + UUID([...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_UUID, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Low_128{ + Low_128: 651345242494996240, + }, + High_128: 72623859790382856, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().AddItems(value.Uint64Value(123), value.Uint64Value(321)).EndTuple(), + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*Ydb.Type{ + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_UINT64, + }, + }, + { + Type: &Ydb.Type_TypeId{ + TypeId: Ydb.Type_UINT64, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Uint64Value{ + Uint64Value: 123, + }, + }, + { + Value: &Ydb.Value_Uint64Value{ + Uint64Value: 321, + }, + }, + }, + }, + }, + }, + }, + } { + t.Run(tt.name, func(t *testing.T) { + a := allocator.New() + defer a.Free() + params := tt.builder.Build().ToYDB(a) + require.Equal(t, paramsToJSON(tt.params), paramsToJSON(params)) + }) + } +}