diff --git a/schema/bool.go b/schema/bool.go index 6c79e6967c..a801f2a0f3 100644 --- a/schema/bool.go +++ b/schema/bool.go @@ -23,6 +23,10 @@ func (*Bool) Type() ValueType { return TypeBool } +func (*Bool) Size() int { + return 1 +} + func (dst *Bool) Equal(src CQType) bool { if src == nil { return false diff --git a/schema/bytea.go b/schema/bytea.go index c9235d1a68..27f9d98bdd 100644 --- a/schema/bytea.go +++ b/schema/bytea.go @@ -20,6 +20,10 @@ func (*Bytea) Type() ValueType { return TypeByteArray } +func (dst *Bytea) Size() int { + return len(dst.Bytes) +} + func (dst *Bytea) GetStatus() Status { return dst.Status } diff --git a/schema/bytea_test.go b/schema/bytea_test.go index acc385dcb7..6f6a001d49 100644 --- a/schema/bytea_test.go +++ b/schema/bytea_test.go @@ -28,3 +28,29 @@ func TestByteaSet(t *testing.T) { } } } + +func TestBytea_Size(t *testing.T) { + tests := []struct { + name string + b Bytea + want int + }{ + { + name: "present", + b: Bytea{Bytes: []byte{1, 2, 3}, Status: Present}, + want: 3, + }, + { + name: "null", + b: Bytea{Status: Null}, + want: 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.b.Size(); got != tt.want { + t.Errorf("Bytea.Size() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/schema/cidr.go b/schema/cidr.go index 0b6ae1ac42..80bb347640 100644 --- a/schema/cidr.go +++ b/schema/cidr.go @@ -16,6 +16,10 @@ func (*CIDR) Type() ValueType { return TypeCIDR } +func (dst *CIDR) Size() int { + return len(dst.IPNet.IP) + len(dst.IPNet.Mask) +} + func (dst *CIDR) String() string { if dst.Status == Present { return dst.IPNet.String() diff --git a/schema/cidr_array.go b/schema/cidr_array.go index 407d194251..c95684062c 100644 --- a/schema/cidr_array.go +++ b/schema/cidr_array.go @@ -26,6 +26,14 @@ func (*CIDRArray) Type() ValueType { return TypeCIDRArray } +func (dst *CIDRArray) Size() int { + totalSize := 0 + for _, element := range dst.Elements { + totalSize += element.Size() + } + return totalSize +} + func (dst *CIDRArray) Equal(src CQType) bool { if src == nil { return false diff --git a/schema/cidr_array_test.go b/schema/cidr_array_test.go index 11fd18a892..9d06066708 100644 --- a/schema/cidr_array_test.go +++ b/schema/cidr_array_test.go @@ -115,3 +115,14 @@ func TestCIDRArraySet(t *testing.T) { } } } + +func TestCIDRArray_Size(t *testing.T) { + a := CIDRArray{ + Elements: []CIDR{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: Present}}, + Dimensions: []ArrayDimension{{LowerBound: 1, Length: 1}}, + Status: Present, + } + if a.Size() != 8 { + t.Errorf("Size() = %d, want 8", a.Size()) + } +} diff --git a/schema/float8.go b/schema/float8.go index ca44383163..f89490e786 100644 --- a/schema/float8.go +++ b/schema/float8.go @@ -26,6 +26,10 @@ func (*Float8) Type() ValueType { return TypeFloat } +func (dst *Float8) Size() int { + return 8 +} + func (dst *Float8) String() string { if dst.Status == Present { return strconv.FormatFloat(dst.Float, 'f', -1, 64) diff --git a/schema/float8_test.go b/schema/float8_test.go index b3520d2850..6769a27056 100644 --- a/schema/float8_test.go +++ b/schema/float8_test.go @@ -39,3 +39,29 @@ func TestFloat8Set(t *testing.T) { } } } + +func TestFloat8_Size(t *testing.T) { + tests := []struct { + name string + f Float8 + want int + }{ + { + name: "present", + f: Float8{Float: 1, Status: Present}, + want: 8, + }, + { + name: "null", + f: Float8{Status: Null}, + want: 8, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.f.Size(); got != tt.want { + t.Errorf("Float8.Size() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/schema/inet.go b/schema/inet.go index 7204c76b15..7c9cc151f3 100644 --- a/schema/inet.go +++ b/schema/inet.go @@ -29,6 +29,9 @@ func (*Inet) Type() ValueType { return TypeInet } +func (dst *Inet) Size() int { + return len(dst.IPNet.IP) + len(dst.IPNet.Mask) +} func (dst *Inet) GetStatus() Status { return dst.Status } diff --git a/schema/inet_array.go b/schema/inet_array.go index f271ce89a1..b4d1f18619 100644 --- a/schema/inet_array.go +++ b/schema/inet_array.go @@ -26,6 +26,14 @@ func (*InetArray) Type() ValueType { return TypeInetArray } +func (dst *InetArray) Size() int { + totalSize := 0 + for _, element := range dst.Elements { + totalSize += element.Size() + } + return totalSize +} + func (dst *InetArray) Equal(src CQType) bool { if src == nil { return false diff --git a/schema/inet_array_test.go b/schema/inet_array_test.go index 1893155d07..1ff3242197 100644 --- a/schema/inet_array_test.go +++ b/schema/inet_array_test.go @@ -115,3 +115,29 @@ func TestInetArraySet(t *testing.T) { } } } + +func TestInetArray_Size(t *testing.T) { + tests := []struct { + source InetArray + want int + }{ + { + source: InetArray{ + Elements: []Inet{ + {IPNet: mustParseCIDR(t, "127.0.0.1/24"), Status: Present}, + {IPNet: mustParseCIDR(t, "10.0.0.1/24"), Status: Present}, + }, + Dimensions: []ArrayDimension{ + {LowerBound: 1, Length: 2}, + {LowerBound: 1, Length: 1}}, + Status: Present, + }, + want: 16, + }, + } + for _, tt := range tests { + if tt.source.Size() != tt.want { + t.Errorf("%v.Size() = %d, want %v", tt.source, tt.source.Size(), tt.want) + } + } +} diff --git a/schema/int8.go b/schema/int8.go index 475b17d69c..1b3782043f 100644 --- a/schema/int8.go +++ b/schema/int8.go @@ -24,6 +24,10 @@ func (*Int8) Type() ValueType { return TypeInt } +func (*Int8) Size() int { + return 8 +} + func (dst *Int8) Equal(src CQType) bool { if src == nil { return false diff --git a/schema/int8_array.go b/schema/int8_array.go index eb2f2202b0..b7bcd571c7 100644 --- a/schema/int8_array.go +++ b/schema/int8_array.go @@ -25,6 +25,10 @@ func (*Int8Array) Type() ValueType { return TypeIntArray } +func (dst *Int8Array) Size() int { + return 8 * len(dst.Elements) +} + func (dst *Int8Array) Equal(src CQType) bool { if src == nil { return false diff --git a/schema/int8_array_test.go b/schema/int8_array_test.go index 2c0520b174..c5a97b8a58 100644 --- a/schema/int8_array_test.go +++ b/schema/int8_array_test.go @@ -131,3 +131,19 @@ func TestInt8ArraySet(t *testing.T) { } } } + +func TestInt8Array_Size(t *testing.T) { + var r Int8Array + var sz int + + sz = r.Size() + if sz != 0 { + t.Errorf("%v.Size() = %d, want %d", r, sz, 0) + } + + _ = r.Set([]int64{1, 2, 3}) + sz = r.Size() + if sz != 24 { + t.Errorf("%v.Size() = %d, want %d", r, sz, 24) + } +} diff --git a/schema/json.go b/schema/json.go index 9922b51898..0ee9f0b5f2 100644 --- a/schema/json.go +++ b/schema/json.go @@ -24,6 +24,10 @@ func (*JSON) Type() ValueType { return TypeJSON } +func (dst *JSON) Size() int { + return len(dst.Bytes) +} + // JSONBytesEqual compares the JSON in two byte slices. func jsonBytesEqual(a, b []byte) (bool, error) { var j, j2 any diff --git a/schema/json_test.go b/schema/json_test.go index b1b34ce2e6..3cbd53fc7e 100644 --- a/schema/json_test.go +++ b/schema/json_test.go @@ -57,3 +57,29 @@ func TestJSONSet(t *testing.T) { } } } + +func TestJSON_Size(t *testing.T) { + tests := []struct { + name string + j JSON + want int + }{ + { + name: "empty", + j: JSON{Status: Null}, + want: 0, + }, + { + name: "present", + j: JSON{Bytes: []byte(`{"foo":"bar"}`), Status: Present}, + want: 13, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.j.Size(); got != tt.want { + t.Errorf("JSON.Size() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/schema/macaddr.go b/schema/macaddr.go index a0d5752ff1..caf16e22dc 100644 --- a/schema/macaddr.go +++ b/schema/macaddr.go @@ -23,6 +23,10 @@ func (*Macaddr) Type() ValueType { return TypeMacAddr } +func (dst *Macaddr) Size() int { + return len(dst.Addr) +} + func (dst *Macaddr) Equal(src CQType) bool { if src == nil { return false diff --git a/schema/macaddr_array.go b/schema/macaddr_array.go index decfaa7d60..e5ab27bbc3 100644 --- a/schema/macaddr_array.go +++ b/schema/macaddr_array.go @@ -26,6 +26,14 @@ func (*MacaddrArray) Type() ValueType { return TypeMacAddrArray } +func (dst *MacaddrArray) Size() int { + totalSize := 0 + for _, element := range dst.Elements { + totalSize += element.Size() + } + return totalSize +} + func (dst *MacaddrArray) Equal(src CQType) bool { if src == nil { return false diff --git a/schema/macaddr_test.go b/schema/macaddr_test.go index fc1879b188..5d95d92c0f 100644 --- a/schema/macaddr_test.go +++ b/schema/macaddr_test.go @@ -31,3 +31,29 @@ func TestMacaddrSet(t *testing.T) { } } } + +func TestMacaddr_Size(t *testing.T) { + tests := []struct { + name string + b Macaddr + want int + }{ + { + name: "present", + b: Macaddr{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: Present}, + want: 6, + }, + { + name: "null", + b: Macaddr{Status: Null}, + want: 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.b.Size(); got != tt.want { + t.Errorf("Macaddr.Size() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/schema/text.go b/schema/text.go index 87f2725f22..28673f9cff 100644 --- a/schema/text.go +++ b/schema/text.go @@ -23,6 +23,10 @@ func (*Text) Type() ValueType { return TypeString } +func (dst *Text) Size() int { + return len(dst.Str) +} + func (dst *Text) Equal(src CQType) bool { if src == nil { return false diff --git a/schema/text_array.go b/schema/text_array.go index adc4dc699e..810eb0ef1f 100644 --- a/schema/text_array.go +++ b/schema/text_array.go @@ -25,6 +25,14 @@ func (*TextArray) Type() ValueType { return TypeStringArray } +func (dst *TextArray) Size() int { + totalSize := 0 + for _, element := range dst.Elements { + totalSize += element.Size() + } + return totalSize +} + func (dst *TextArray) Equal(src CQType) bool { if src == nil { return false diff --git a/schema/text_array_test.go b/schema/text_array_test.go index 23f208ffb7..1962dba185 100644 --- a/schema/text_array_test.go +++ b/schema/text_array_test.go @@ -82,3 +82,50 @@ func TestTextArraySet(t *testing.T) { } } } + +func TestTextArray_Size(t *testing.T) { + cases := []struct { + source TextArray + result int + }{ + { + source: TextArray{ + Elements: []Text{{Str: "foo", Status: Present}}, + Dimensions: []ArrayDimension{{LowerBound: 1, Length: 1}}, + Status: Present}, + result: 3, + }, + { + source: TextArray{Status: Null}, + result: 0, + }, + { + source: TextArray{ + Elements: []Text{{Str: "foo", Status: Present}, {Str: "bar", Status: Present}}, + Dimensions: []ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, + Status: Present}, + result: 6, + }, + { + source: TextArray{ + Elements: []Text{ + {Str: "foo", Status: Present}, + {Str: "bar", Status: Present}, + {Str: "baz", Status: Present}}, + Dimensions: []ArrayDimension{ + {LowerBound: 1, Length: 2}, + {LowerBound: 1, Length: 1}, + {LowerBound: 1, Length: 1}, + {LowerBound: 1, Length: 3}}, + Status: Present}, + result: 9, + }, + } + + for i, tt := range cases { + result := tt.source.Size() + if result != tt.result { + t.Errorf("%d: %v != %v", i, result, tt.result) + } + } +} diff --git a/schema/timestamptz.go b/schema/timestamptz.go index fef699c9d3..162fc54657 100644 --- a/schema/timestamptz.go +++ b/schema/timestamptz.go @@ -39,6 +39,10 @@ func (*Timestamptz) Type() ValueType { return TypeTimestamp } +func (dst *Timestamptz) Size() int { + return 24 +} + func (dst *Timestamptz) Equal(src CQType) bool { if src == nil { return false diff --git a/schema/types.go b/schema/types.go index 2dd445462b..5a2fcc0ddd 100644 --- a/schema/types.go +++ b/schema/types.go @@ -212,11 +212,23 @@ type CQType interface { String() string Equal(CQType) bool Type() ValueType + Size() int GetStatus() Status } type CQTypes []CQType +// Size returns total number of bytes occupied by all values +// this useful to understand how much data is being transferred, rather than just number +// of resources. +func (c CQTypes) Size() int { + var size int + for _, v := range c { + size += v.Size() + } + return size +} + func (c CQTypes) MarshalJSON() ([]byte, error) { res := make([]map[string]any, len(c)) for i, v := range c { diff --git a/schema/uuid.go b/schema/uuid.go index ee637104df..ac8829a344 100644 --- a/schema/uuid.go +++ b/schema/uuid.go @@ -19,6 +19,10 @@ func (*UUID) Type() ValueType { return TypeUUID } +func (dst *UUID) Size() int { + return 16 +} + func (dst *UUID) GetStatus() Status { return dst.Status } diff --git a/schema/uuid_array.go b/schema/uuid_array.go index 086b33ea36..0eb62d0422 100644 --- a/schema/uuid_array.go +++ b/schema/uuid_array.go @@ -25,6 +25,14 @@ func (*UUIDArray) Type() ValueType { return TypeUUIDArray } +func (dst *UUIDArray) Size() int { + totalSize := 0 + for _, element := range dst.Elements { + totalSize += element.Size() + } + return totalSize +} + func (dst *UUIDArray) Equal(src CQType) bool { if src == nil { return false diff --git a/schema/uuid_array_test.go b/schema/uuid_array_test.go index 6064e114e5..63f5098f6d 100644 --- a/schema/uuid_array_test.go +++ b/schema/uuid_array_test.go @@ -161,3 +161,24 @@ func TestUUIDArraySet(t *testing.T) { } } } + +func TestUUIDArray_Size(t *testing.T) { + var r UUIDArray + if r.Size() != 0 { + t.Errorf("bad size: %d", r.Size()) + } + + _ = r.Set([2][1][1][3]string{ + {{{ + "00010203-0405-0607-0809-0a0b0c0d0e0f", + "10111213-1415-1617-1819-1a1b1c1d1e1f", + "20212223-2425-2627-2829-2a2b2c2d2e2f"}}}, + {{{ + "30313233-3435-3637-3839-3a3b3c3d3e3f", + "40414243-4445-4647-4849-4a4b4c4d4e4f", + "50515253-5455-5657-5859-5a5b5c5d5e5f"}}}, + }) + if r.Size() != 96 { + t.Errorf("bad size: %d", r.Size()) + } +}