Repository: tableauio/tableau
Pull request #216 (2025-04-14 07:50:56)

feat: add "." as path sep and escape "key" when concatenating card prefixs

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/tableauio/tableau/pull/216?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 69.74%. Comparing base [(`71b40d4`)](https://app.codecov.io/gh/tableauio/tableau/commit/71b40d469c88a87aa2bafa1347b9623059d9d743?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) to head [(`5358796`)](https://app.codecov.io/gh/tableauio/tableau/commit/53587961f9b22031b6652754485bd14cc3364a10?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #216      +/-   ##
==========================================
+ Coverage   69.70%   69.74%   +0.03%     
==========================================
  Files          79       77       -2     
  Lines       10033     9858     -175     
==========================================
- Hits         6994     6875     -119     
+ Misses       2463     2422      -41     
+ Partials      576      561      -15     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/tableauio/tableau/pull/216?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>

Patch:
diff --git a/internal/confgen/document_parser.go b/internal/confgen/document_parser.go
index 920878b..70f4aad 100644
--- a/internal/confgen/document_parser.go
+++ b/internal/confgen/document_parser.go
@@ -1,7 +1,6 @@
 package confgen
 
 import (
-	"fmt"
 	"strconv"
 	"strings"
 
@@ -79,7 +78,7 @@ func (sp *documentParser) parseMessage(msg protoreflect.Message, node *book.Node
 					return xerrors.WrapKV(xerrors.E2014(field.opts.Name), kvs...)
 				}
 			}
-			newCardPrefix := cardPrefix + string(fd.Name())
+			newCardPrefix := cardPrefix + "." + string(fd.Name())
 			fieldPresent, err := sp.parseField(field, msg, fieldNode, newCardPrefix)
 			if err != nil {
 				return xerrors.WrapKV(err,
@@ -176,7 +175,7 @@ func (sp *documentParser) parseMapField(field *Field, msg protoreflect.Message,
 				} else {
 					newMapValue = reflectMap.NewValue()
 				}
-				newCardPrefix := cardPrefix + fmt.Sprint(newMapKey)
+				newCardPrefix := cardPrefix + "." + escapeMapKey(newMapKey.Value())
 				valuePresent, err := sp.parseMessage(newMapValue.Message(), elemNode, newCardPrefix)
 				if err != nil {
 					return false, xerrors.WrapKV(err, elemNode.DebugKV()...)
@@ -330,7 +329,7 @@ func (sp *documentParser) parseListField(field *Field, msg protoreflect.Message,
 		for _, elemNode := range node.Children {
 			elemPresent := false
 			elemValue := list.NewElement()
-			newCardPrefix := cardPrefix + strconv.Itoa(list.Len())
+			newCardPrefix := cardPrefix + "." + strconv.Itoa(list.Len())
 			if xproto.IsUnionField(field.fd) {
 				// cross-cell union list
 				elemPresent, err = sp.parseUnionMessage(field, elemValue.Message(), elemNode, newCardPrefix)
diff --git a/internal/confgen/table_parser.go b/internal/confgen/table_parser.go
index 417d3c3..d8b1538 100644
--- a/internal/confgen/table_parser.go
+++ b/internal/confgen/table_parser.go
@@ -144,7 +144,7 @@ func (sp *tableParser) parseMessage(msg protoreflect.Message, rc *book.RowCells,
 			// TODO(performance): cache the parsed field for reuse, as each table row will be parsed repeatedly.
 			field := sp.parseFieldDescriptor(fd)
 			defer field.release()
-			newCardPrefix := cardPrefix + string(fd.Name())
+			newCardPrefix := cardPrefix + "." + string(fd.Name())
 			fieldPresent, err := sp.parseField(field, msg, rc, prefix, newCardPrefix)
 			if err != nil {
 				return xerrors.WrapKV(err,
@@ -214,7 +214,7 @@ func (sp *tableParser) parseVerticalMapField(field *Field, msg protoreflect.Mess
 	if err != nil {
 		return false, xerrors.WrapKV(err, rc.CellDebugKV(keyColName)...)
 	}
-	newCardPrefix := cardPrefix + fmt.Sprint(newMapKey)
+	newCardPrefix := cardPrefix + "." + escapeMapKey(newMapKey.Value())
 	// value must be empty if key not present
 	if !keyPresent && reflectMap.Has(newMapKey) {
 		tempCheckMapValue := reflectMap.NewValue()
@@ -291,7 +291,7 @@ func (sp *tableParser) parseHorizontalMapField(field *Field, msg protoreflect.Me
 		if err != nil {
 			return false, xerrors.WrapKV(err, rc.CellDebugKV(keyColName)...)
 		}
-		newCardPrefix := cardPrefix + fmt.Sprint(newMapKey)
+		newCardPrefix := cardPrefix + "." + escapeMapKey(newMapKey.Value())
 		// value must be empty if key not present
 		if !keyPresent && reflectMap.Has(newMapKey) {
 			tempCheckMapValue := reflectMap.NewValue()
@@ -392,7 +392,7 @@ func (sp *tableParser) parseVerticalListField(field *Field, msg protoreflect.Mes
 	elemPresent := false
 	elemValue := list.NewElement()
 	newPrefix := prefix + field.opts.Name
-	newCardPrefix := cardPrefix + strconv.Itoa(list.Len())
+	newCardPrefix := cardPrefix + "." + strconv.Itoa(list.Len())
 	// struct list
 	if field.opts.Key != "" {
 		// KeyedList means the list is keyed by the specified Key option.
@@ -428,7 +428,7 @@ func (sp *tableParser) parseVerticalListField(field *Field, msg protoreflect.Mes
 			elemPresent = !keyedListElemExisted
 		}
 		// For KeyedList, use key but not len(list) as cardinality
-		newCardPrefix := cardPrefix + fmt.Sprint(key)
+		newCardPrefix := cardPrefix + "." + escapeMapKey(key)
 		present, err := sp.parseMessage(elemValue.Message(), rc, newPrefix, newCardPrefix)
 		if err != nil {
 			return false, err
@@ -484,7 +484,7 @@ func (sp *tableParser) parseHorizontalListField(field *Field, msg protoreflect.M
 		elemPresent := false
 		elemValue := list.NewElement()
 		elemPrefix := newPrefix + strconv.Itoa(i)
-		newCardPrefix := cardPrefix + strconv.Itoa(list.Len())
+		newCardPrefix := cardPrefix + "." + strconv.Itoa(list.Len())
 		var cell *book.RowCell
 		if field.fd.Kind() == protoreflect.MessageKind {
 			if types.IsWellKnownMessage(field.fd.Message().FullName()) {
diff --git a/internal/confgen/util.go b/internal/confgen/util.go
index bbd69e0..83a1fa1 100644
--- a/internal/confgen/util.go
+++ b/internal/confgen/util.go
@@ -1,6 +1,7 @@
 package confgen
 
 import (
+	"fmt"
 	"path/filepath"
 	"strings"
 	"sync"
@@ -308,3 +309,31 @@ func parseTableListLayout(layout tableaupb.Layout) tableaupb.Layout {
 	}
 	return layout
 }
+
+// isSafePathKeyChar returns true if the input character is safe for not
+// needing escaping.
+func isSafePathKeyChar(c byte) bool {
+	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+		(c >= '0' && c <= '9') || c <= ' ' || c > '~' || c == '_' ||
+		c == '-' || c == ':'
+}
+
+// refer: https://github.com/tidwall/gjson/blob/v1.18.0/gjson.go#L3560
+func escapeMapKey(key protoreflect.Value) string {
+	comp := fmt.Sprint(key)
+	for i := 0; i < len(comp); i++ {
+		if !isSafePathKeyChar(comp[i]) {
+			ncomp := make([]byte, len(comp)+1)
+			copy(ncomp, comp[:i])
+			ncomp = ncomp[:i]
+			for ; i < len(comp); i++ {
+				if !isSafePathKeyChar(comp[i]) {
+					ncomp = append(ncomp, '\\')
+				}
+				ncomp = append(ncomp, comp[i])
+			}
+			return string(ncomp)
+		}
+	}
+	return comp
+}


Test patch:
diff --git a/internal/confgen/document_parser_test.go b/internal/confgen/document_parser_test.go
index fe200a2..a29273c 100644
--- a/internal/confgen/document_parser_test.go
+++ b/internal/confgen/document_parser_test.go
@@ -919,6 +919,181 @@ func TestDocParser_parseDocumentUniqueFieldStructMap(t *testing.T) {
 			wantErr: true,
 			errcode: "E2005",
 		},
+		{
+			name:   "card prefix uniqueness",
+			parser: newDocParserForTest(),
+			args: args{
+				sheet: &book.Sheet{
+					Name: "DocumentUniqueFieldStructMap",
+					Document: &book.Node{
+						Kind: book.DocumentNode,
+						Name: "DocumentUniqueFieldStructMap",
+						Children: []*book.Node{
+							{
+								Kind: book.MapNode,
+								Children: []*book.Node{
+									{
+										Kind: book.MapNode,
+										Name: "Chapter",
+										Children: []*book.Node{
+											{
+												Kind: book.MapNode,
+												Name: "_infox",
+												Children: []*book.Node{
+													{
+														Name:  "Name",
+														Value: "ChapterOne",
+													},
+													{
+														Kind: book.MapNode,
+														Name: "Section",
+														Children: []*book.Node{
+															{
+																Kind: book.MapNode,
+																Name: "1",
+																Children: []*book.Node{
+																	{
+																		Name:  "Name",
+																		Value: "SectionOne",
+																	},
+																},
+															},
+														},
+													},
+												},
+											},
+										},
+									},
+									{
+										Kind: book.MapNode,
+										Name: "ChapterInfo",
+										Children: []*book.Node{
+											{
+												Kind: book.MapNode,
+												Name: "x",
+												Children: []*book.Node{
+													{
+														Name:  "Name",
+														Value: "ChapterOne",
+													},
+													{
+														Kind: book.MapNode,
+														Name: "Section",
+														Children: []*book.Node{
+															{
+																Kind: book.MapNode,
+																Name: "section",
+																Children: []*book.Node{
+																	{
+																		Name:  "Name",
+																		Value: "SectionOne",
+																	},
+																	{
+																		Kind: book.MapNode,
+																		Name: "Section",
+																		Children: []*book.Node{
+																			{
+																				Kind: book.MapNode,
+																				Name: "section",
+																				Children: []*book.Node{
+																					{
+																						Name:  "Name",
+																						Value: "SectionOne",
+																					},
+																					{
+																						Kind: book.MapNode,
+																						Name: "Section",
+																						Children: []*book.Node{
+																							{
+																								Kind: book.MapNode,
+																								Name: "section",
+																								Children: []*book.Node{
+																									{
+																										Name:  "Name",
+																										Value: "SectionOne",
+																									},
+																									{
+																										Kind: book.MapNode,
+																										Name: "Section",
+																										Children: []*book.Node{
+																											{
+																												Kind: book.MapNode,
+																												Name: "section",
+																												Children: []*book.Node{
+																													{
+																														Name:  "Name",
+																														Value: "SectionOne",
+																													},
+																												},
+																											},
+																										},
+																									},
+																								},
+																							},
+																						},
+																					},
+																				},
+																			},
+																		},
+																	},
+																},
+															},
+														},
+													},
+												},
+											},
+											{
+												Kind: book.MapNode,
+												Name: "x.section",
+												Children: []*book.Node{
+													{
+														Name:  "Name",
+														Value: "ChapterTwo",
+													},
+													{
+														Kind: book.MapNode,
+														Name: "Section",
+														Children: []*book.Node{
+															{
+																Kind: book.MapNode,
+																Name: "section.section",
+																Children: []*book.Node{
+																	{
+																		Name:  "Name",
+																		Value: "SectionOne",
+																	},
+																	{
+																		Kind: book.MapNode,
+																		Name: "Section",
+																		Children: []*book.Node{
+																			{
+																				Kind: book.MapNode,
+																				Name: "section",
+																				Children: []*book.Node{
+																					{
+																						Name:  "Name",
+																						Value: "SectionOne",
+																					},
+																				},
+																			},
+																		},
+																	},
+																},
+															},
+														},
+													},
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			wantErr: false,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/proto/tableau/protobuf/unittest/unittest.proto b/proto/tableau/protobuf/unittest/unittest.proto
index 8a169b6..204e378 100644
--- a/proto/tableau/protobuf/unittest/unittest.proto
+++ b/proto/tableau/protobuf/unittest/unittest.proto
@@ -194,12 +194,33 @@ message DocumentUniqueFieldStructMap {
   message Chapter {
     string key = 1 [(tableau.field) = {name:"@key" prop:{unique:true}}];
     string name = 2 [(tableau.field) = {name:"Name" prop:{unique:true}}];
-    map<uint32, Section> section = 3 [(tableau.field) = {name:"Section" key:"@key"}];
+    map<string, Section> section = 3 [(tableau.field) = {name:"Section" key:"@key"}];
     message Section {
-      uint32 key = 1 [(tableau.field) = {name:"@key" prop:{unique:true}}];
+      string key = 1 [(tableau.field) = {name:"@key" prop:{unique:true}}];
       string name = 2 [(tableau.field) = {name:"Name" prop:{unique:true}}];
     }
   }
   map<uint32, string> scalar_map = 2 [(tableau.field) = {name:"ScalarMap" prop:{optional:true}}];
   map<uint32, string> incell_map = 5 [(tableau.field) = {name:"IncellMap" layout:LAYOUT_INCELL prop:{optional:true}}];
+  // for card prefix tests
+  map<string, ChapterInfo> chapter_info = 13 [(tableau.field) = {name:"ChapterInfo" key:"@key" prop:{optional:true}}];
+  message ChapterInfo {
+    string key = 1 [(tableau.field) = {name:"@key" prop:{unique:true}}];
+    string name = 2 [(tableau.field) = {name:"Name" prop:{unique:true}}];
+    map<string, Section> section = 3 [(tableau.field) = {name:"Section" key:"@key" prop:{optional:true}}];
+    message Section {
+      string key = 1 [(tableau.field) = {name:"@key" prop:{unique:true}}];
+      string name = 2 [(tableau.field) = {name:"Name" prop:{unique:true}}];
+      map<string, Section> section = 3 [(tableau.field) = {name:"Section" key:"@key" prop:{optional:true}}];
+      message Section {
+        string key = 1 [(tableau.field) = {name:"@key" prop:{unique:true}}];
+        string name = 2 [(tableau.field) = {name:"Name" prop:{unique:true}}];
+        map<string, Section> section = 3 [(tableau.field) = {name:"Section" key:"@key" prop:{optional:true}}];
+        message Section {
+          string key = 1 [(tableau.field) = {name:"@key" prop:{unique:true}}];
+          string name = 2 [(tableau.field) = {name:"Name" prop:{unique:true}}];
+        }
+      }
+    }
+  }
 }
\ No newline at end of file
diff --git a/proto/tableaupb/unittestpb/unittest.pb.go b/proto/tableaupb/unittestpb/unittest.pb.go
index 97000a6..399a141 100644
--- a/proto/tableaupb/unittestpb/unittest.pb.go
+++ b/proto/tableaupb/unittestpb/unittest.pb.go
@@ -914,6 +914,8 @@ type DocumentUniqueFieldStructMap struct {
 	Chapter   map[string]*DocumentUniqueFieldStructMap_Chapter `protobuf:"bytes,1,rep,name=chapter,proto3" json:"chapter,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
 	ScalarMap map[uint32]string                                `protobuf:"bytes,2,rep,name=scalar_map,json=scalarMap,proto3" json:"scalar_map,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
 	IncellMap map[uint32]string                                `protobuf:"bytes,5,rep,name=incell_map,json=incellMap,proto3" json:"incell_map,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+	// for card prefix tests
+	ChapterInfo map[string]*DocumentUniqueFieldStructMap_ChapterInfo `protobuf:"bytes,13,rep,name=chapter_info,json=chapterInfo,proto3" json:"chapter_info,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
 }
 
 func (x *DocumentUniqueFieldStructMap) Reset() {
@@ -969,6 +971,13 @@ func (x *DocumentUniqueFieldStructMap) GetIncellMap() map[uint32]string {
 	return nil
 }
 
+func (x *DocumentUniqueFieldStructMap) GetChapterInfo() map[string]*DocumentUniqueFieldStructMap_ChapterInfo {
+	if x != nil {
+		return x.ChapterInfo
+	}
+	return nil
+}
+
 type IncellMap_Fruit struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -1818,7 +1827,7 @@ type DocumentUniqueFieldStructMap_Chapter struct {
 
 	Key     string                                                   `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
 	Name    string                                                   `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
-	Section map[uint32]*DocumentUniqueFieldStructMap_Chapter_Section `protobuf:"bytes,3,rep,name=section,proto3" json:"section,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+	Section map[string]*DocumentUniqueFieldStructMap_Chapter_Section `protobuf:"bytes,3,rep,name=section,proto3" json:"section,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
 }
 
 func (x *DocumentUniqueFieldStructMap_Chapter) Reset() {
@@ -1867,7 +1876,70 @@ func (x *DocumentUniqueFieldStructMap_Chapter) GetName() string {
 	return ""
 }
 
-func (x *DocumentUniqueFieldStructMap_Chapter) GetSection() map[uint32]*DocumentUniqueFieldStructMap_Chapter_Section {
+func (x *DocumentUniqueFieldStructMap_Chapter) GetSection() map[string]*DocumentUniqueFieldStructMap_Chapter_Section {
+	if x != nil {
+		return x.Section
+	}
+	return nil
+}
+
+type DocumentUniqueFieldStructMap_ChapterInfo struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Key     string                                                       `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
+	Name    string                                                       `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
+	Section map[string]*DocumentUniqueFieldStructMap_ChapterInfo_Section `protobuf:"bytes,3,rep,name=section,proto3" json:"section,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo) Reset() {
+	*x = DocumentUniqueFieldStructMap_ChapterInfo{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[55]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DocumentUniqueFieldStructMap_ChapterInfo) ProtoMessage() {}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo) ProtoReflect() protoreflect.Message {
+	mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[55]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use DocumentUniqueFieldStructMap_ChapterInfo.ProtoReflect.Descriptor instead.
+func (*DocumentUniqueFieldStructMap_ChapterInfo) Descriptor() ([]byte, []int) {
+	return file_tableau_protobuf_unittest_unittest_proto_rawDescGZIP(), []int{15, 5}
+}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo) GetKey() string {
+	if x != nil {
+		return x.Key
+	}
+	return ""
+}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo) GetSection() map[string]*DocumentUniqueFieldStructMap_ChapterInfo_Section {
 	if x != nil {
 		return x.Section
 	}
@@ -1879,14 +1951,14 @@ type DocumentUniqueFieldStructMap_Chapter_Section struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Key  uint32 `protobuf:"varint,1,opt,name=key,proto3" json:"key,omitempty"`
+	Key  string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
 	Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
 }
 
 func (x *DocumentUniqueFieldStructMap_Chapter_Section) Reset() {
 	*x = DocumentUniqueFieldStructMap_Chapter_Section{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[55]
+		mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[57]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1899,7 +1971,7 @@ func (x *DocumentUniqueFieldStructMap_Chapter_Section) String() string {
 func (*DocumentUniqueFieldStructMap_Chapter_Section) ProtoMessage() {}
 
 func (x *DocumentUniqueFieldStructMap_Chapter_Section) ProtoReflect() protoreflect.Message {
-	mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[55]
+	mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[57]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1915,11 +1987,11 @@ func (*DocumentUniqueFieldStructMap_Chapter_Section) Descriptor() ([]byte, []int
 	return file_tableau_protobuf_unittest_unittest_proto_rawDescGZIP(), []int{15, 1, 1}
 }
 
-func (x *DocumentUniqueFieldStructMap_Chapter_Section) GetKey() uint32 {
+func (x *DocumentUniqueFieldStructMap_Chapter_Section) GetKey() string {
 	if x != nil {
 		return x.Key
 	}
-	return 0
+	return ""
 }
 
 func (x *DocumentUniqueFieldStructMap_Chapter_Section) GetName() string {
@@ -1929,6 +2001,187 @@ func (x *DocumentUniqueFieldStructMap_Chapter_Section) GetName() string {
 	return ""
 }
 
+type DocumentUniqueFieldStructMap_ChapterInfo_Section struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Key     string                                                               `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
+	Name    string                                                               `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
+	Section map[string]*DocumentUniqueFieldStructMap_ChapterInfo_Section_Section `protobuf:"bytes,3,rep,name=section,proto3" json:"section,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo_Section) Reset() {
+	*x = DocumentUniqueFieldStructMap_ChapterInfo_Section{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[59]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo_Section) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DocumentUniqueFieldStructMap_ChapterInfo_Section) ProtoMessage() {}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo_Section) ProtoReflect() protoreflect.Message {
+	mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[59]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use DocumentUniqueFieldStructMap_ChapterInfo_Section.ProtoReflect.Descriptor instead.
+func (*DocumentUniqueFieldStructMap_ChapterInfo_Section) Descriptor() ([]byte, []int) {
+	return file_tableau_protobuf_unittest_unittest_proto_rawDescGZIP(), []int{15, 5, 1}
+}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo_Section) GetKey() string {
+	if x != nil {
+		return x.Key
+	}
+	return ""
+}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo_Section) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo_Section) GetSection() map[string]*DocumentUniqueFieldStructMap_ChapterInfo_Section_Section {
+	if x != nil {
+		return x.Section
+	}
+	return nil
+}
+
+type DocumentUniqueFieldStructMap_ChapterInfo_Section_Section struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Key     string                                                                       `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
+	Name    string                                                                       `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
+	Section map[string]*DocumentUniqueFieldStructMap_ChapterInfo_Section_Section_Section `protobuf:"bytes,3,rep,name=section,proto3" json:"section,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo_Section_Section) Reset() {
+	*x = DocumentUniqueFieldStructMap_ChapterInfo_Section_Section{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[61]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo_Section_Section) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DocumentUniqueFieldStructMap_ChapterInfo_Section_Section) ProtoMessage() {}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo_Section_Section) ProtoReflect() protoreflect.Message {
+	mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[61]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use DocumentUniqueFieldStructMap_ChapterInfo_Section_Section.ProtoReflect.Descriptor instead.
+func (*DocumentUniqueFieldStructMap_ChapterInfo_Section_Section) Descriptor() ([]byte, []int) {
+	return file_tableau_protobuf_unittest_unittest_proto_rawDescGZIP(), []int{15, 5, 1, 1}
+}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo_Section_Section) GetKey() string {
+	if x != nil {
+		return x.Key
+	}
+	return ""
+}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo_Section_Section) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo_Section_Section) GetSection() map[string]*DocumentUniqueFieldStructMap_ChapterInfo_Section_Section_Section {
+	if x != nil {
+		return x.Section
+	}
+	return nil
+}
+
+type DocumentUniqueFieldStructMap_ChapterInfo_Section_Section_Section struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Key  string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
+	Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
+}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo_Section_Section_Section) Reset() {
+	*x = DocumentUniqueFieldStructMap_ChapterInfo_Section_Section_Section{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[63]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo_Section_Section_Section) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DocumentUniqueFieldStructMap_ChapterInfo_Section_Section_Section) ProtoMessage() {}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo_Section_Section_Section) ProtoReflect() protoreflect.Message {
+	mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[63]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use DocumentUniqueFieldStructMap_ChapterInfo_Section_Section_Section.ProtoReflect.Descriptor instead.
+func (*DocumentUniqueFieldStructMap_ChapterInfo_Section_Section_Section) Descriptor() ([]byte, []int) {
+	return file_tableau_protobuf_unittest_unittest_proto_rawDescGZIP(), []int{15, 5, 1, 1, 1}
+}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo_Section_Section_Section) GetKey() string {
+	if x != nil {
+		return x.Key
+	}
+	return ""
+}
+
+func (x *DocumentUniqueFieldStructMap_ChapterInfo_Section_Section_Section) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
 var File_tableau_protobuf_unittest_unittest_proto protoreflect.FileDescriptor
 
 var file_tableau_protobuf_unittest_unittest_proto_rawDesc = []byte{
@@ -2371,8 +2624,8 @@ var file_tableau_protobuf_unittest_unittest_proto_rawDesc = []byte{
 	0x6e, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x42, 0x09, 0x82, 0xb5, 0x18, 0x05, 0x0a,
 	0x03, 0x4e, 0x75, 0x6d, 0x52, 0x03, 0x6e, 0x75, 0x6d, 0x3a, 0x23, 0x82, 0xb5, 0x18, 0x1f, 0x0a,
 	0x1d, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x46,
-	0x69, 0x65, 0x6c, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x22, 0xed,
-	0x07, 0x0a, 0x1c, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x69, 0x71, 0x75,
+	0x69, 0x65, 0x6c, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x9b,
+	0x12, 0x0a, 0x1c, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x69, 0x71, 0x75,
 	0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4d, 0x61, 0x70, 0x12,
 	0x66, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
 	0x32, 0x33, 0x2e, 0x75, 0x6e, 0x69, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x6f, 0x63, 0x75,
@@ -2393,54 +2646,137 @@ var file_tableau_protobuf_unittest_unittest_proto_rawDesc = []byte{
 	0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4d, 0x61, 0x70, 0x2e, 0x49,
 	0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x15, 0x82,
 	0xb5, 0x18, 0x11, 0x0a, 0x09, 0x49, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x4d, 0x61, 0x70, 0x20, 0x03,
-	0x7a, 0x02, 0x58, 0x01, 0x52, 0x09, 0x69, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x4d, 0x61, 0x70, 0x1a,
-	0x6a, 0x0a, 0x0c, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
-	0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
-	0x79, 0x12, 0x44, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
-	0x32, 0x2e, 0x2e, 0x75, 0x6e, 0x69, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x6f, 0x63, 0x75,
-	0x6d, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53,
-	0x74, 0x72, 0x75, 0x63, 0x74, 0x4d, 0x61, 0x70, 0x2e, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72,
-	0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x80, 0x03, 0x0a, 0x07,
-	0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
+	0x7a, 0x02, 0x58, 0x01, 0x52, 0x09, 0x69, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x4d, 0x61, 0x70, 0x12,
+	0x77, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18,
+	0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x75, 0x6e, 0x69, 0x74, 0x74, 0x65, 0x73, 0x74,
+	0x2e, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x46,
+	0x69, 0x65, 0x6c, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4d, 0x61, 0x70, 0x2e, 0x43, 0x68,
+	0x61, 0x70, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x1b,
+	0x82, 0xb5, 0x18, 0x17, 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66,
+	0x6f, 0x1a, 0x04, 0x40, 0x6b, 0x65, 0x79, 0x7a, 0x02, 0x58, 0x01, 0x52, 0x0b, 0x63, 0x68, 0x61,
+	0x70, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x6a, 0x0a, 0x0c, 0x43, 0x68, 0x61, 0x70,
+	0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x44, 0x0a, 0x05, 0x76, 0x61,
+	0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x75, 0x6e, 0x69, 0x74,
+	0x74, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x69,
+	0x71, 0x75, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4d, 0x61,
+	0x70, 0x2e, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+	0x3a, 0x02, 0x38, 0x01, 0x1a, 0x80, 0x03, 0x0a, 0x07, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72,
+	0x12, 0x20, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0x82,
+	0xb5, 0x18, 0x0a, 0x0a, 0x04, 0x40, 0x6b, 0x65, 0x79, 0x7a, 0x02, 0x10, 0x01, 0x52, 0x03, 0x6b,
+	0x65, 0x79, 0x12, 0x22, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+	0x42, 0x0e, 0x82, 0xb5, 0x18, 0x0a, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x7a, 0x02, 0x10, 0x01,
+	0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x6a, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f,
+	0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x75, 0x6e, 0x69, 0x74, 0x74, 0x65,
+	0x73, 0x74, 0x2e, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x69, 0x71, 0x75,
+	0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4d, 0x61, 0x70, 0x2e,
+	0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45,
+	0x6e, 0x74, 0x72, 0x79, 0x42, 0x13, 0x82, 0xb5, 0x18, 0x0f, 0x0a, 0x07, 0x53, 0x65, 0x63, 0x74,
+	0x69, 0x6f, 0x6e, 0x1a, 0x04, 0x40, 0x6b, 0x65, 0x79, 0x52, 0x07, 0x73, 0x65, 0x63, 0x74, 0x69,
+	0x6f, 0x6e, 0x1a, 0x72, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74,
+	0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x03, 0x6b, 0x65, 0x79, 0x12, 0x4c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x75, 0x6e, 0x69, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x44,
+	0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x46, 0x69, 0x65,
+	0x6c, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4d, 0x61, 0x70, 0x2e, 0x43, 0x68, 0x61, 0x70,
+	0x74, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c,
+	0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4f, 0x0a, 0x07, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f,
+	0x6e, 0x12, 0x20, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0e,
+	0x82, 0xb5, 0x18, 0x0a, 0x0a, 0x04, 0x40, 0x6b, 0x65, 0x79, 0x7a, 0x02, 0x10, 0x01, 0x52, 0x03,
+	0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x09, 0x42, 0x0e, 0x82, 0xb5, 0x18, 0x0a, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x7a, 0x02, 0x10,
+	0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x1a, 0x3c, 0x0a, 0x0e, 0x53, 0x63, 0x61, 0x6c, 0x61,
+	0x72, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76,
+	0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
+	0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3c, 0x0a, 0x0e, 0x49, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x4d,
+	0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
+	0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
+	0x02, 0x38, 0x01, 0x1a, 0x72, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x49, 0x6e,
+	0x66, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x48, 0x0a, 0x05, 0x76, 0x61, 0x6c,
+	0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x75, 0x6e, 0x69, 0x74, 0x74,
+	0x65, 0x73, 0x74, 0x2e, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x69, 0x71,
+	0x75, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4d, 0x61, 0x70,
+	0x2e, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61,
+	0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0xbe, 0x08, 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x70,
+	0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x20, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
 	0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0x82, 0xb5, 0x18, 0x0a, 0x0a, 0x04, 0x40, 0x6b, 0x65, 0x79,
 	0x7a, 0x02, 0x10, 0x01, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
 	0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0x82, 0xb5, 0x18, 0x0a, 0x0a, 0x04, 0x4e,
-	0x61, 0x6d, 0x65, 0x7a, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x6a, 0x0a,
-	0x07, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b,
+	0x61, 0x6d, 0x65, 0x7a, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x72, 0x0a,
+	0x07, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f,
 	0x2e, 0x75, 0x6e, 0x69, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65,
 	0x6e, 0x74, 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x74, 0x72,
-	0x75, 0x63, 0x74, 0x4d, 0x61, 0x70, 0x2e, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x2e, 0x53,
-	0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x13, 0x82, 0xb5, 0x18,
-	0x0f, 0x0a, 0x07, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x04, 0x40, 0x6b, 0x65, 0x79,
-	0x52, 0x07, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x72, 0x0a, 0x0c, 0x53, 0x65, 0x63,
-	0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x4c, 0x0a, 0x05, 0x76,
-	0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x75, 0x6e, 0x69,
-	0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x6e,
-	0x69, 0x71, 0x75, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4d,
-	0x61, 0x70, 0x2e, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x63, 0x74, 0x69,
-	0x6f, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4f, 0x0a,
-	0x07, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x0e, 0x82, 0xb5, 0x18, 0x0a, 0x0a, 0x04, 0x40, 0x6b, 0x65,
-	0x79, 0x7a, 0x02, 0x10, 0x01, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x04, 0x6e, 0x61,
-	0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0x82, 0xb5, 0x18, 0x0a, 0x0a, 0x04,
-	0x4e, 0x61, 0x6d, 0x65, 0x7a, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x1a, 0x3c,
-	0x0a, 0x0e, 0x53, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79,
-	0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b,
-	0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3c, 0x0a, 0x0e,
-	0x49, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
-	0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79,
-	0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x22, 0x82, 0xb5, 0x18, 0x1e,
-	0x0a, 0x1c, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65,
-	0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4d, 0x61, 0x70, 0x42, 0x56,
-	0x82, 0xb5, 0x18, 0x19, 0x0a, 0x17, 0x75, 0x6e, 0x69, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x55,
-	0x6e, 0x69, 0x74, 0x74, 0x65, 0x73, 0x74, 0x23, 0x2a, 0x2e, 0x63, 0x73, 0x76, 0x5a, 0x37, 0x67,
-	0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61,
-	0x75, 0x69, 0x6f, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2f, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x70, 0x62, 0x2f, 0x75, 0x6e, 0x69, 0x74,
-	0x74, 0x65, 0x73, 0x74, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x75, 0x63, 0x74, 0x4d, 0x61, 0x70, 0x2e, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x49, 0x6e,
+	0x66, 0x6f, 0x2e, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42,
+	0x17, 0x82, 0xb5, 0x18, 0x13, 0x0a, 0x07, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x04,
+	0x40, 0x6b, 0x65, 0x79, 0x7a, 0x02, 0x58, 0x01, 0x52, 0x07, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f,
+	0x6e, 0x1a, 0x76, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72,
+	0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
+	0x6b, 0x65, 0x79, 0x12, 0x50, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x75, 0x6e, 0x69, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x6f,
+	0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x46, 0x69, 0x65, 0x6c,
+	0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4d, 0x61, 0x70, 0x2e, 0x43, 0x68, 0x61, 0x70, 0x74,
+	0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05,
+	0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0xfc, 0x05, 0x0a, 0x07, 0x53, 0x65,
+	0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x09, 0x42, 0x0e, 0x82, 0xb5, 0x18, 0x0a, 0x0a, 0x04, 0x40, 0x6b, 0x65, 0x79, 0x7a, 0x02,
+	0x10, 0x01, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0x82, 0xb5, 0x18, 0x0a, 0x0a, 0x04, 0x4e, 0x61, 0x6d,
+	0x65, 0x7a, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x7a, 0x0a, 0x07, 0x73,
+	0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x47, 0x2e, 0x75,
+	0x6e, 0x69, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
+	0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63,
+	0x74, 0x4d, 0x61, 0x70, 0x2e, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f,
+	0x2e, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+	0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x17, 0x82, 0xb5, 0x18, 0x13, 0x0a, 0x07, 0x53, 0x65, 0x63,
+	0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x04, 0x40, 0x6b, 0x65, 0x79, 0x7a, 0x02, 0x58, 0x01, 0x52, 0x07,
+	0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x7e, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x74, 0x69,
+	0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x58, 0x0a, 0x05, 0x76, 0x61, 0x6c,
+	0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x75, 0x6e, 0x69, 0x74, 0x74,
+	0x65, 0x73, 0x74, 0x2e, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x69, 0x71,
+	0x75, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4d, 0x61, 0x70,
+	0x2e, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x65, 0x63,
+	0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x76, 0x61,
+	0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0xae, 0x03, 0x0a, 0x07, 0x53, 0x65, 0x63, 0x74,
+	0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x42, 0x0e, 0x82, 0xb5, 0x18, 0x0a, 0x0a, 0x04, 0x40, 0x6b, 0x65, 0x79, 0x7a, 0x02, 0x10, 0x01,
+	0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x09, 0x42, 0x0e, 0x82, 0xb5, 0x18, 0x0a, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x7a,
+	0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x82, 0x01, 0x0a, 0x07, 0x73, 0x65,
+	0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4f, 0x2e, 0x75, 0x6e,
+	0x69, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x55,
+	0x6e, 0x69, 0x71, 0x75, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74,
+	0x4d, 0x61, 0x70, 0x2e, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e,
+	0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e,
+	0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x17, 0x82, 0xb5,
+	0x18, 0x13, 0x0a, 0x07, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x04, 0x40, 0x6b, 0x65,
+	0x79, 0x7a, 0x02, 0x58, 0x01, 0x52, 0x07, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x86,
+	0x01, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
+	0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
+	0x79, 0x12, 0x60, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x4a, 0x2e, 0x75, 0x6e, 0x69, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x6f, 0x63, 0x75,
+	0x6d, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53,
+	0x74, 0x72, 0x75, 0x63, 0x74, 0x4d, 0x61, 0x70, 0x2e, 0x43, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72,
+	0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x63,
+	0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x76, 0x61,
+	0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4f, 0x0a, 0x07, 0x53, 0x65, 0x63, 0x74, 0x69,
+	0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42,
+	0x0e, 0x82, 0xb5, 0x18, 0x0a, 0x0a, 0x04, 0x40, 0x6b, 0x65, 0x79, 0x7a, 0x02, 0x10, 0x01, 0x52,
+	0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x09, 0x42, 0x0e, 0x82, 0xb5, 0x18, 0x0a, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x7a, 0x02,
+	0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x22, 0x82, 0xb5, 0x18, 0x1e, 0x0a, 0x1c,
+	0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x46, 0x69,
+	0x65, 0x6c, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4d, 0x61, 0x70, 0x42, 0x56, 0x82, 0xb5,
+	0x18, 0x19, 0x0a, 0x17, 0x75, 0x6e, 0x69, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x55, 0x6e, 0x69,
+	0x74, 0x74, 0x65, 0x73, 0x74, 0x23, 0x2a, 0x2e, 0x63, 0x73, 0x76, 0x5a, 0x37, 0x67, 0x69, 0x74,
+	0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x69,
+	0x6f, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
+	0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x70, 0x62, 0x2f, 0x75, 0x6e, 0x69, 0x74, 0x74, 0x65,
+	0x73, 0x74, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -2455,7 +2791,7 @@ func file_tableau_protobuf_unittest_unittest_proto_rawDescGZIP() []byte {
 	return file_tableau_protobuf_unittest_unittest_proto_rawDescData
 }
 
-var file_tableau_protobuf_unittest_unittest_proto_msgTypes = make([]protoimpl.MessageInfo, 56)
+var file_tableau_protobuf_unittest_unittest_proto_msgTypes = make([]protoimpl.MessageInfo, 64)
 var file_tableau_protobuf_unittest_unittest_proto_goTypes = []interface{}{
 	(*SimpleIncellMap)(nil),                 // 0: unittest.SimpleIncellMap
 	(*IncellMap)(nil),                       // 1: unittest.IncellMap
@@ -2511,22 +2847,30 @@ var file_tableau_protobuf_unittest_unittest_proto_goTypes = []interface{}{
 	(*DocumentUniqueFieldStructMap_Chapter)(nil), // 51: unittest.DocumentUniqueFieldStructMap.Chapter
 	nil, // 52: unittest.DocumentUniqueFieldStructMap.ScalarMapEntry
 	nil, // 53: unittest.DocumentUniqueFieldStructMap.IncellMapEntry
-	nil, // 54: unittest.DocumentUniqueFieldStructMap.Chapter.SectionEntry
-	(*DocumentUniqueFieldStructMap_Chapter_Section)(nil), // 55: unittest.DocumentUniqueFieldStructMap.Chapter.Section
-	(*Item)(nil),                  // 56: unittest.Item
-	(FruitFlavor)(0),              // 57: unittest.FruitFlavor
-	(FruitType)(0),                // 58: unittest.FruitType
-	(*timestamppb.Timestamp)(nil), // 59: google.protobuf.Timestamp
-	(*durationpb.Duration)(nil),   // 60: google.protobuf.Duration
+	nil, // 54: unittest.DocumentUniqueFieldStructMap.ChapterInfoEntry
+	(*DocumentUniqueFieldStructMap_ChapterInfo)(nil), // 55: unittest.DocumentUniqueFieldStructMap.ChapterInfo
+	nil, // 56: unittest.DocumentUniqueFieldStructMap.Chapter.SectionEntry
+	(*DocumentUniqueFieldStructMap_Chapter_Section)(nil), // 57: unittest.DocumentUniqueFieldStructMap.Chapter.Section
+	nil, // 58: unittest.DocumentUniqueFieldStructMap.ChapterInfo.SectionEntry
+	(*DocumentUniqueFieldStructMap_ChapterInfo_Section)(nil), // 59: unittest.DocumentUniqueFieldStructMap.ChapterInfo.Section
+	nil, // 60: unittest.DocumentUniqueFieldStructMap.ChapterInfo.Section.SectionEntry
+	(*DocumentUniqueFieldStructMap_ChapterInfo_Section_Section)(nil), // 61: unittest.DocumentUniqueFieldStructMap.ChapterInfo.Section.Section
+	nil, // 62: unittest.DocumentUniqueFieldStructMap.ChapterInfo.Section.Section.SectionEntry
+	(*DocumentUniqueFieldStructMap_ChapterInfo_Section_Section_Section)(nil), // 63: unittest.DocumentUniqueFieldStructMap.ChapterInfo.Section.Section.Section
+	(*Item)(nil),                  // 64: unittest.Item
+	(FruitFlavor)(0),              // 65: unittest.FruitFlavor
+	(FruitType)(0),                // 66: unittest.FruitType
+	(*timestamppb.Timestamp)(nil), // 67: google.protobuf.Timestamp
+	(*durationpb.Duration)(nil),   // 68: google.protobuf.Duration
 }
 var file_tableau_protobuf_unittest_unittest_proto_depIdxs = []int32{
 	16, // 0: unittest.SimpleIncellMap.item_map:type_name -> unittest.SimpleIncellMap.ItemMapEntry
 	17, // 1: unittest.IncellMap.fruit_map:type_name -> unittest.IncellMap.FruitMapEntry
 	19, // 2: unittest.IncellMap.flavor_map:type_name -> unittest.IncellMap.FlavorMapEntry
 	20, // 3: unittest.IncellMap.item_map:type_name -> unittest.IncellMap.ItemMapEntry
-	56, // 4: unittest.IncellStructList.item_list:type_name -> unittest.Item
-	57, // 5: unittest.IncellList.flavor_list:type_name -> unittest.FruitFlavor
-	56, // 6: unittest.IncellList.item_list:type_name -> unittest.Item
+	64, // 4: unittest.IncellStructList.item_list:type_name -> unittest.Item
+	65, // 5: unittest.IncellList.flavor_list:type_name -> unittest.FruitFlavor
+	64, // 6: unittest.IncellList.item_list:type_name -> unittest.Item
 	22, // 7: unittest.ItemConf.item_map:type_name -> unittest.ItemConf.ItemMapEntry
 	23, // 8: unittest.MallConf.shop_map:type_name -> unittest.MallConf.ShopMapEntry
 	27, // 9: unittest.RewardConf.reward_map:type_name -> unittest.RewardConf.RewardMapEntry
@@ -2543,44 +2887,52 @@ var file_tableau_protobuf_unittest_unittest_proto_depIdxs = []int32{
 	50, // 20: unittest.DocumentUniqueFieldStructMap.chapter:type_name -> unittest.DocumentUniqueFieldStructMap.ChapterEntry
 	52, // 21: unittest.DocumentUniqueFieldStructMap.scalar_map:type_name -> unittest.DocumentUniqueFieldStructMap.ScalarMapEntry
 	53, // 22: unittest.DocumentUniqueFieldStructMap.incell_map:type_name -> unittest.DocumentUniqueFieldStructMap.IncellMapEntry
-	18, // 23: unittest.IncellMap.FruitMapEntry.value:type_name -> unittest.IncellMap.Fruit
-	58, // 24: unittest.IncellMap.Fruit.key:type_name -> unittest.FruitType
-	57, // 25: unittest.IncellMap.FlavorMapEntry.value:type_name -> unittest.FruitFlavor
-	21, // 26: unittest.IncellMap.ItemMapEntry.value:type_name -> unittest.IncellMap.Item
-	58, // 27: unittest.IncellMap.Item.key:type_name -> unittest.FruitType
-	57, // 28: unittest.IncellMap.Item.value:type_name -> unittest.FruitFlavor
-	56, // 29: unittest.ItemConf.ItemMapEntry.value:type_name -> unittest.Item
-	24, // 30: unittest.MallConf.ShopMapEntry.value:type_name -> unittest.MallConf.Shop
-	25, // 31: unittest.MallConf.Shop.goods_map:type_name -> unittest.MallConf.Shop.GoodsMapEntry
-	26, // 32: unittest.MallConf.Shop.GoodsMapEntry.value:type_name -> unittest.MallConf.Shop.Goods
-	28, // 33: unittest.RewardConf.RewardMapEntry.value:type_name -> unittest.RewardConf.Reward
-	29, // 34: unittest.RewardConf.Reward.item_map:type_name -> unittest.RewardConf.Reward.ItemMapEntry
-	56, // 35: unittest.RewardConf.Reward.ItemMapEntry.value:type_name -> unittest.Item
-	59, // 36: unittest.PatchMergeConf.Time.start:type_name -> google.protobuf.Timestamp
-	60, // 37: unittest.PatchMergeConf.Time.expiry:type_name -> google.protobuf.Duration
-	56, // 38: unittest.PatchMergeConf.ItemMapEntry.value:type_name -> unittest.Item
-	56, // 39: unittest.PatchMergeConf.ReplaceItemMapEntry.value:type_name -> unittest.Item
-	34, // 40: unittest.RecursivePatchConf.ShopMapEntry.value:type_name -> unittest.RecursivePatchConf.Shop
-	35, // 41: unittest.RecursivePatchConf.Shop.goods_map:type_name -> unittest.RecursivePatchConf.Shop.GoodsMapEntry
-	36, // 42: unittest.RecursivePatchConf.Shop.GoodsMapEntry.value:type_name -> unittest.RecursivePatchConf.Shop.Goods
-	37, // 43: unittest.RecursivePatchConf.Shop.Goods.currency_map:type_name -> unittest.RecursivePatchConf.Shop.Goods.CurrencyMapEntry
-	39, // 44: unittest.RecursivePatchConf.Shop.Goods.award_list:type_name -> unittest.RecursivePatchConf.Shop.Goods.Award
-	38, // 45: unittest.RecursivePatchConf.Shop.Goods.CurrencyMapEntry.value:type_name -> unittest.RecursivePatchConf.Shop.Goods.Currency
-	40, // 46: unittest.RecursivePatchConf.Shop.Goods.Currency.value_list:type_name -> unittest.RecursivePatchConf.Shop.Goods.Currency.ValueListEntry
-	41, // 47: unittest.RecursivePatchConf.Shop.Goods.Currency.message_list:type_name -> unittest.RecursivePatchConf.Shop.Goods.Currency.MessageListEntry
-	9,  // 48: unittest.JsonUtilTestData.MapFieldEntry.value:type_name -> unittest.PatchMergeConf
-	45, // 49: unittest.VerticalUniqueFieldStructMap.MainMapEntry.value:type_name -> unittest.VerticalUniqueFieldStructMap.Main
-	46, // 50: unittest.VerticalUniqueFieldStructMap.Main.main_kv_map:type_name -> unittest.VerticalUniqueFieldStructMap.Main.MainKvMapEntry
-	47, // 51: unittest.VerticalUniqueFieldStructMap.Main.sub_map:type_name -> unittest.VerticalUniqueFieldStructMap.Main.SubMapEntry
-	48, // 52: unittest.VerticalUniqueFieldStructMap.Main.SubMapEntry.value:type_name -> unittest.VerticalUniqueFieldStructMap.Main.Sub
-	51, // 53: unittest.DocumentUniqueFieldStructMap.ChapterEntry.value:type_name -> unittest.DocumentUniqueFieldStructMap.Chapter
-	54, // 54: unittest.DocumentUniqueFieldStructMap.Chapter.section:type_name -> unittest.DocumentUniqueFieldStructMap.Chapter.SectionEntry
-	55, // 55: unittest.DocumentUniqueFieldStructMap.Chapter.SectionEntry.value:type_name -> unittest.DocumentUniqueFieldStructMap.Chapter.Section
-	56, // [56:56] is the sub-list for method output_type
-	56, // [56:56] is the sub-list for method input_type
-	56, // [56:56] is the sub-list for extension type_name
-	56, // [56:56] is the sub-list for extension extendee
-	0,  // [0:56] is the sub-list for field type_name
+	54, // 23: unittest.DocumentUniqueFieldStructMap.chapter_info:type_name -> unittest.DocumentUniqueFieldStructMap.ChapterInfoEntry
+	18, // 24: unittest.IncellMap.FruitMapEntry.value:type_name -> unittest.IncellMap.Fruit
+	66, // 25: unittest.IncellMap.Fruit.key:type_name -> unittest.FruitType
+	65, // 26: unittest.IncellMap.FlavorMapEntry.value:type_name -> unittest.FruitFlavor
+	21, // 27: unittest.IncellMap.ItemMapEntry.value:type_name -> unittest.IncellMap.Item
+	66, // 28: unittest.IncellMap.Item.key:type_name -> unittest.FruitType
+	65, // 29: unittest.IncellMap.Item.value:type_name -> unittest.FruitFlavor
+	64, // 30: unittest.ItemConf.ItemMapEntry.value:type_name -> unittest.Item
+	24, // 31: unittest.MallConf.ShopMapEntry.value:type_name -> unittest.MallConf.Shop
+	25, // 32: unittest.MallConf.Shop.goods_map:type_name -> unittest.MallConf.Shop.GoodsMapEntry
+	26, // 33: unittest.MallConf.Shop.GoodsMapEntry.value:type_name -> unittest.MallConf.Shop.Goods
+	28, // 34: unittest.RewardConf.RewardMapEntry.value:type_name -> unittest.RewardConf.Reward
+	29, // 35: unittest.RewardConf.Reward.item_map:type_name -> unittest.RewardConf.Reward.ItemMapEntry
+	64, // 36: unittest.RewardConf.Reward.ItemMapEntry.value:type_name -> unittest.Item
+	67, // 37: unittest.PatchMergeConf.Time.start:type_name -> google.protobuf.Timestamp
+	68, // 38: unittest.PatchMergeConf.Time.expiry:type_name -> google.protobuf.Duration
+	64, // 39: unittest.PatchMergeConf.ItemMapEntry.value:type_name -> unittest.Item
+	64, // 40: unittest.PatchMergeConf.ReplaceItemMapEntry.value:type_name -> unittest.Item
+	34, // 41: unittest.RecursivePatchConf.ShopMapEntry.value:type_name -> unittest.RecursivePatchConf.Shop
+	35, // 42: unittest.RecursivePatchConf.Shop.goods_map:type_name -> unittest.RecursivePatchConf.Shop.GoodsMapEntry
+	36, // 43: unittest.RecursivePatchConf.Shop.GoodsMapEntry.value:type_name -> unittest.RecursivePatchConf.Shop.Goods
+	37, // 44: unittest.RecursivePatchConf.Shop.Goods.currency_map:type_name -> unittest.RecursivePatchConf.Shop.Goods.CurrencyMapEntry
+	39, // 45: unittest.RecursivePatchConf.Shop.Goods.award_list:type_name -> unittest.RecursivePatchConf.Shop.Goods.Award
+	38, // 46: unittest.RecursivePatchConf.Shop.Goods.CurrencyMapEntry.value:type_name -> unittest.RecursivePatchConf.Shop.Goods.Currency
+	40, // 47: unittest.RecursivePatchConf.Shop.Goods.Currency.value_list:type_name -> unittest.RecursivePatchConf.Shop.Goods.Currency.ValueListEntry
+	41, // 48: unittest.RecursivePatchConf.Shop.Goods.Currency.message_list:type_name -> unittest.RecursivePatchConf.Shop.Goods.Currency.MessageListEntry
+	9,  // 49: unittest.JsonUtilTestData.MapFieldEntry.value:type_name -> unittest.PatchMergeConf
+	45, // 50: unittest.VerticalUniqueFieldStructMap.MainMapEntry.value:type_name -> unittest.VerticalUniqueFieldStructMap.Main
+	46, // 51: unittest.VerticalUniqueFieldStructMap.Main.main_kv_map:type_name -> unittest.VerticalUniqueFieldStructMap.Main.MainKvMapEntry
+	47, // 52: unittest.VerticalUniqueFieldStructMap.Main.sub_map:type_name -> unittest.VerticalUniqueFieldStructMap.Main.SubMapEntry
+	48, // 53: unittest.VerticalUniqueFieldStructMap.Main.SubMapEntry.value:type_name -> unittest.VerticalUniqueFieldStructMap.Main.Sub
+	51, // 54: unittest.DocumentUniqueFieldStructMap.ChapterEntry.value:type_name -> unittest.DocumentUniqueFieldStructMap.Chapter
+	56, // 55: unittest.DocumentUniqueFieldStructMap.Chapter.section:type_name -> unittest.DocumentUniqueFieldStructMap.Chapter.SectionEntry
+	55, // 56: unittest.DocumentUniqueFieldStructMap.ChapterInfoEntry.value:type_name -> unittest.DocumentUniqueFieldStructMap.ChapterInfo
+	58, // 57: unittest.DocumentUniqueFieldStructMap.ChapterInfo.section:type_name -> unittest.DocumentUniqueFieldStructMap.ChapterInfo.SectionEntry
+	57, // 58: unittest.DocumentUniqueFieldStructMap.Chapter.SectionEntry.value:type_name -> unittest.DocumentUniqueFieldStructMap.Chapter.Section
+	59, // 59: unittest.DocumentUniqueFieldStructMap.ChapterInfo.SectionEntry.value:type_name -> unittest.DocumentUniqueFieldStructMap.ChapterInfo.Section
+	60, // 60: unittest.DocumentUniqueFieldStructMap.ChapterInfo.Section.section:type_name -> unittest.DocumentUniqueFieldStructMap.ChapterInfo.Section.SectionEntry
+	61, // 61: unittest.DocumentUniqueFieldStructMap.ChapterInfo.Section.SectionEntry.value:type_name -> unittest.DocumentUniqueFieldStructMap.ChapterInfo.Section.Section
+	62, // 62: unittest.DocumentUniqueFieldStructMap.ChapterInfo.Section.Section.section:type_name -> unittest.DocumentUniqueFieldStructMap.ChapterInfo.Section.Section.SectionEntry
+	63, // 63: unittest.DocumentUniqueFieldStructMap.ChapterInfo.Section.Section.SectionEntry.value:type_name -> unittest.DocumentUniqueFieldStructMap.ChapterInfo.Section.Section.Section
+	64, // [64:64] is the sub-list for method output_type
+	64, // [64:64] is the sub-list for method input_type
+	64, // [64:64] is the sub-list for extension type_name
+	64, // [64:64] is the sub-list for extension extendee
+	0,  // [0:64] is the sub-list for field type_name
 }
 
 func init() { file_tableau_protobuf_unittest_unittest_proto_init() }
@@ -2963,6 +3315,18 @@ func file_tableau_protobuf_unittest_unittest_proto_init() {
 			}
 		}
 		file_tableau_protobuf_unittest_unittest_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DocumentUniqueFieldStructMap_ChapterInfo); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tableau_protobuf_unittest_unittest_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*DocumentUniqueFieldStructMap_Chapter_Section); i {
 			case 0:
 				return &v.state
@@ -2974,6 +3338,42 @@ func file_tableau_protobuf_unittest_unittest_proto_init() {
 				return nil
 			}
 		}
+		file_tableau_protobuf_unittest_unittest_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DocumentUniqueFieldStructMap_ChapterInfo_Section); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tableau_protobuf_unittest_unittest_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DocumentUniqueFieldStructMap_ChapterInfo_Section_Section); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tableau_protobuf_unittest_unittest_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DocumentUniqueFieldStructMap_ChapterInfo_Section_Section_Section); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
 	}
 	file_tableau_protobuf_unittest_unittest_proto_msgTypes[9].OneofWrappers = []interface{}{}
 	type x struct{}
@@ -2982,7 +3382,7 @@ func file_tableau_protobuf_unittest_unittest_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_tableau_protobuf_unittest_unittest_proto_rawDesc,
 			NumEnums:      0,
-			NumMessages:   56,
+			NumMessages:   64,
 			NumExtensions: 0,
 			NumServices:   0,
 		},