Skip to content

Commit b8da9f5

Browse files
Merge pull request #1589 from ClickHouse/json_serialization_version_2
`JSON` serialization version 3
2 parents b8caae7 + e064bff commit b8da9f5

File tree

14 files changed

+116
-96
lines changed

14 files changed

+116
-96
lines changed

examples/clickhouse_api/dynamic.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,14 @@ func DynamicExample() error {
2727
ctx := context.Background()
2828

2929
conn, err := GetNativeConnection(clickhouse.Settings{
30-
"allow_experimental_dynamic_type": true,
30+
"allow_experimental_dynamic_type": true,
31+
"output_format_native_use_flattened_dynamic_and_json_serialization": true,
3132
}, nil, nil)
3233
if err != nil {
3334
return err
3435
}
3536

36-
if !CheckMinServerVersion(conn, 24, 8, 0) {
37+
if !CheckMinServerVersion(conn, 25, 6, 0) {
3738
fmt.Print("unsupported clickhouse version for Dynamic type")
3839
return nil
3940
}

examples/clickhouse_api/json_fast_structs.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,14 @@ func JSONFastStructExample() error {
9191
ctx := context.Background()
9292

9393
conn, err := GetNativeConnection(clickhouse.Settings{
94-
"allow_experimental_json_type": true,
94+
"allow_experimental_json_type": true,
95+
"output_format_native_use_flattened_dynamic_and_json_serialization": true,
9596
}, nil, nil)
9697
if err != nil {
9798
return err
9899
}
99100

100-
if !CheckMinServerVersion(conn, 24, 9, 0) {
101+
if !CheckMinServerVersion(conn, 25, 6, 0) {
101102
fmt.Print("unsupported clickhouse version for JSON type")
102103
return nil
103104
}

examples/clickhouse_api/json_paths.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,14 @@ func JSONPathsExample() error {
2828
ctx := context.Background()
2929

3030
conn, err := GetNativeConnection(clickhouse.Settings{
31-
"allow_experimental_json_type": true,
31+
"allow_experimental_json_type": true,
32+
"output_format_native_use_flattened_dynamic_and_json_serialization": true,
3233
}, nil, nil)
3334
if err != nil {
3435
return err
3536
}
3637

37-
if !CheckMinServerVersion(conn, 24, 9, 0) {
38+
if !CheckMinServerVersion(conn, 25, 6, 0) {
3839
fmt.Print("unsupported clickhouse version for JSON type")
3940
return nil
4041
}

examples/clickhouse_api/json_strings.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,15 @@ func JSONStringExample() error {
2727
ctx := context.Background()
2828

2929
conn, err := GetNativeConnection(clickhouse.Settings{
30-
"allow_experimental_json_type": true,
31-
"output_format_native_write_json_as_string": true,
30+
"allow_experimental_json_type": true,
31+
"output_format_native_write_json_as_string": true,
32+
"output_format_native_use_flattened_dynamic_and_json_serialization": true,
3233
}, nil, nil)
3334
if err != nil {
3435
return err
3536
}
3637

37-
if !CheckMinServerVersion(conn, 24, 9, 0) {
38+
if !CheckMinServerVersion(conn, 25, 6, 0) {
3839
fmt.Print("unsupported clickhouse version for JSON type")
3940
return nil
4041
}

examples/clickhouse_api/json_structs.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,14 @@ func JSONStructExample() error {
6161
ctx := context.Background()
6262

6363
conn, err := GetNativeConnection(clickhouse.Settings{
64-
"allow_experimental_json_type": true,
64+
"allow_experimental_json_type": true,
65+
"output_format_native_use_flattened_dynamic_and_json_serialization": true,
6566
}, nil, nil)
6667
if err != nil {
6768
return err
6869
}
6970

70-
if !CheckMinServerVersion(conn, 24, 9, 0) {
71+
if !CheckMinServerVersion(conn, 25, 6, 0) {
7172
fmt.Print("unsupported clickhouse version for JSON type")
7273
return nil
7374
}

examples/clickhouse_api/main_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,5 @@ func TestJSONFastStructExample(t *testing.T) {
234234

235235
func TestJSONStringExample(t *testing.T) {
236236
clickhouse_tests.SkipOnCloud(t, "cannot modify JSON settings on cloud")
237-
t.Skip("client cannot receive JSON strings")
238237
require.NoError(t, JSONStringExample())
239238
}

examples/std/json_paths.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func JSONPathsExample() error {
3232
return err
3333
}
3434

35-
if !CheckMinServerVersion(conn, 24, 9, 0) {
35+
if !CheckMinServerVersion(conn, 25, 6, 0) {
3636
fmt.Print("unsupported clickhouse version for JSON type")
3737
return nil
3838
}
@@ -41,6 +41,10 @@ func JSONPathsExample() error {
4141
if err != nil {
4242
return err
4343
}
44+
_, err = conn.ExecContext(ctx, "SET output_format_native_use_flattened_dynamic_and_json_serialization = 1")
45+
if err != nil {
46+
return err
47+
}
4448

4549
defer func() {
4650
conn.Exec("DROP TABLE go_json_example")

examples/std/main_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,5 @@ func TestJSONPathsExample(t *testing.T) {
166166

167167
func TestJSONStringExample(t *testing.T) {
168168
clickhouse_tests.SkipOnCloud(t, "cannot modify JSON settings on cloud")
169-
t.Skip("client cannot receive JSON strings")
170169
require.NoError(t, JSONStringExample())
171170
}

lib/column/dynamic.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ func (c *Dynamic) decodeHeader(reader *proto.Reader) error {
321321
}
322322

323323
if dynamicSerializationVersion == DeprecatedSupportedDynamicSerializationVersion {
324-
return fmt.Errorf("deprecated dynamic serialization version: %d, enable \"output_format_native_use_flattened_dynamic_and_json_serialization\" in your settings.", dynamicSerializationVersion)
324+
return fmt.Errorf("deprecated dynamic serialization version: %d, enable \"output_format_native_use_flattened_dynamic_and_json_serialization\" in your settings", dynamicSerializationVersion)
325325
} else if dynamicSerializationVersion != SupportedDynamicSerializationVersion {
326326
return fmt.Errorf("unsupported dynamic serialization version: %d", dynamicSerializationVersion)
327327
}

lib/column/json.go

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ import (
2929
"github.com/ClickHouse/ch-go/proto"
3030
)
3131

32-
const JSONObjectSerializationVersion uint64 = 0
32+
const JSONDeprecatedObjectSerializationVersion uint64 = 0
3333
const JSONStringSerializationVersion uint64 = 1
34+
const JSONObjectSerializationVersion uint64 = 3
3435
const JSONUnsetSerializationVersion uint64 = math.MaxUint64
3536
const DefaultMaxDynamicPaths = 1024
3637

@@ -51,13 +52,13 @@ type JSON struct {
5152
skipPaths []string
5253
skipPathsIndex map[string]int
5354

55+
totalDynamicPaths int
5456
dynamicPaths []string
5557
dynamicPathsIndex map[string]int
5658
dynamicColumns []*Dynamic
5759

58-
maxDynamicPaths int
59-
maxDynamicTypes int
60-
totalDynamicPaths int
60+
maxDynamicPaths int
61+
maxDynamicTypes int
6162
}
6263

6364
func (c *JSON) parse(t Type, tz *time.Location) (_ *JSON, err error) {
@@ -544,17 +545,29 @@ func (c *JSON) appendRowString(v any) error {
544545
return nil
545546
}
546547

547-
func (c *JSON) encodeObjectHeader(buffer *proto.Buffer) {
548-
buffer.PutUVarInt(uint64(c.maxDynamicPaths))
548+
func (c *JSON) encodeObjectHeader(buffer *proto.Buffer) error {
549549
buffer.PutUVarInt(uint64(c.totalDynamicPaths))
550550

551551
for _, dynamicPath := range c.dynamicPaths {
552552
buffer.PutString(dynamicPath)
553553
}
554554

555-
for _, col := range c.dynamicColumns {
556-
col.encodeHeader(buffer)
555+
for i, col := range c.typedColumns {
556+
if serialize, ok := col.(CustomSerialization); ok {
557+
if err := serialize.WriteStatePrefix(buffer); err != nil {
558+
return fmt.Errorf("failed to write prefix for typed path \"%s\" in json with type %s: %w", c.typedPaths[i], string(col.Type()), err)
559+
}
560+
}
561+
}
562+
563+
for i, col := range c.dynamicColumns {
564+
err := col.encodeHeader(buffer)
565+
if err != nil {
566+
return fmt.Errorf("failed to encode header for json dynamic path \"%s\": %w", c.dynamicPaths[i], err)
567+
}
557568
}
569+
570+
return nil
558571
}
559572

560573
func (c *JSON) encodeObjectData(buffer *proto.Buffer) {
@@ -565,11 +578,6 @@ func (c *JSON) encodeObjectData(buffer *proto.Buffer) {
565578
for _, col := range c.dynamicColumns {
566579
col.encodeData(buffer)
567580
}
568-
569-
// SharedData per row, empty for now.
570-
for i := 0; i < c.rows; i++ {
571-
buffer.PutUInt64(0)
572-
}
573581
}
574582

575583
func (c *JSON) encodeStringData(buffer *proto.Buffer) {
@@ -580,9 +588,7 @@ func (c *JSON) WriteStatePrefix(buffer *proto.Buffer) error {
580588
switch c.serializationVersion {
581589
case JSONObjectSerializationVersion:
582590
buffer.PutUInt64(JSONObjectSerializationVersion)
583-
c.encodeObjectHeader(buffer)
584-
585-
return nil
591+
return c.encodeObjectHeader(buffer)
586592
case JSONStringSerializationVersion:
587593
buffer.PutUInt64(JSONStringSerializationVersion)
588594

@@ -632,20 +638,14 @@ func (c *JSON) Reset() {
632638
}
633639

634640
func (c *JSON) decodeObjectHeader(reader *proto.Reader) error {
635-
maxDynamicPaths, err := reader.UVarInt()
636-
if err != nil {
637-
return fmt.Errorf("failed to read max dynamic paths for json column: %w", err)
638-
}
639-
c.maxDynamicPaths = int(maxDynamicPaths)
640-
641641
totalDynamicPaths, err := reader.UVarInt()
642642
if err != nil {
643643
return fmt.Errorf("failed to read total dynamic paths for json column: %w", err)
644644
}
645645
c.totalDynamicPaths = int(totalDynamicPaths)
646646

647-
c.dynamicPaths = make([]string, 0, totalDynamicPaths)
648-
for i := 0; i < int(totalDynamicPaths); i++ {
647+
c.dynamicPaths = make([]string, 0, c.totalDynamicPaths)
648+
for i := 0; i < c.totalDynamicPaths; i++ {
649649
dynamicPath, err := reader.Str()
650650
if err != nil {
651651
return fmt.Errorf("failed to read dynamic path name bytes at index %d for json column: %w", i, err)
@@ -655,14 +655,22 @@ func (c *JSON) decodeObjectHeader(reader *proto.Reader) error {
655655
c.dynamicPathsIndex[dynamicPath] = len(c.dynamicPaths) - 1
656656
}
657657

658-
c.dynamicColumns = make([]*Dynamic, 0, totalDynamicPaths)
658+
for i, col := range c.typedColumns {
659+
if serialize, ok := col.(CustomSerialization); ok {
660+
if err := serialize.ReadStatePrefix(reader); err != nil {
661+
return fmt.Errorf("failed to read prefix for typed path \"%s\" with type %s in json: %w", c.typedPaths[i], string(col.Type()), err)
662+
}
663+
}
664+
}
665+
666+
c.dynamicColumns = make([]*Dynamic, 0, c.totalDynamicPaths)
659667
for _, dynamicPath := range c.dynamicPaths {
660668
parsedColDynamic, _ := Type("Dynamic").Column("", c.tz)
661669
colDynamic := parsedColDynamic.(*Dynamic)
662670

663671
err := colDynamic.decodeHeader(reader)
664672
if err != nil {
665-
return fmt.Errorf("failed to decode dynamic header at path %s for json column: %w", dynamicPath, err)
673+
return fmt.Errorf("failed to decode dynamic header at path \"%s\" for json column: %w", dynamicPath, err)
666674
}
667675

668676
c.dynamicColumns = append(c.dynamicColumns, colDynamic)
@@ -677,7 +685,7 @@ func (c *JSON) decodeObjectData(reader *proto.Reader, rows int) error {
677685

678686
err := col.Decode(reader, rows)
679687
if err != nil {
680-
return fmt.Errorf("failed to decode %s typed path %s for json column: %w", col.Type(), typedPath, err)
688+
return fmt.Errorf("failed to decode %s typed path \"%s\" for json column: %w", col.Type(), typedPath, err)
681689
}
682690
}
683691

@@ -686,16 +694,10 @@ func (c *JSON) decodeObjectData(reader *proto.Reader, rows int) error {
686694

687695
err := col.decodeData(reader, rows)
688696
if err != nil {
689-
return fmt.Errorf("failed to decode dynamic path %s for json column: %w", dynamicPath, err)
697+
return fmt.Errorf("failed to decode dynamic path \"%s\" for json column: %w", dynamicPath, err)
690698
}
691699
}
692700

693-
// SharedData per row, ignored for now. May cause stream offset issues if present
694-
_, err := reader.ReadRaw(8 * rows) // one UInt64 per row
695-
if err != nil {
696-
return fmt.Errorf("failed to read shared data for json column: %w", err)
697-
}
698-
699701
return nil
700702
}
701703

@@ -712,6 +714,8 @@ func (c *JSON) ReadStatePrefix(reader *proto.Reader) error {
712714
c.serializationVersion = jsonSerializationVersion
713715

714716
switch jsonSerializationVersion {
717+
case JSONDeprecatedObjectSerializationVersion:
718+
return fmt.Errorf("deprecated json serialization version: %d, enable \"output_format_native_use_flattened_dynamic_and_json_serialization\" in your settings", jsonSerializationVersion)
715719
case JSONObjectSerializationVersion:
716720
err := c.decodeObjectHeader(reader)
717721
if err != nil {

0 commit comments

Comments
 (0)