Preserve append history across tiers
This commit is contained in:
@@ -45,10 +45,11 @@ func (c *retainedNodeCounter) add(root *document.Node) bool {
|
||||
// Replay reconciles with canonical decoding before domain resolution so both
|
||||
// contracts agree on the effective result without retaining a tree per field.
|
||||
type historyApplication struct {
|
||||
layer Layer
|
||||
path Path
|
||||
value *document.Node
|
||||
origin Origin
|
||||
layer Layer
|
||||
path Path
|
||||
value *document.Node
|
||||
origin Origin
|
||||
sequences []sequenceHistoryContribution
|
||||
}
|
||||
|
||||
func cloneHistoryApplications(values []historyApplication) []historyApplication {
|
||||
@@ -59,7 +60,7 @@ func cloneHistoryApplications(values []historyApplication) []historyApplication
|
||||
for index, value := range values {
|
||||
cloned[index] = historyApplication{
|
||||
layer: value.layer, path: value.path.clone(), value: value.value,
|
||||
origin: cloneOrigin(value.origin),
|
||||
origin: cloneOrigin(value.origin), sequences: cloneSequenceHistoryContributions(value.sequences),
|
||||
}
|
||||
}
|
||||
return cloned
|
||||
@@ -75,6 +76,24 @@ func applicationOperationPath(application historyApplication, query Path, lookup
|
||||
}
|
||||
return Path{}, false
|
||||
}
|
||||
sequencePathMatched := false
|
||||
for _, contribution := range application.sequences {
|
||||
if !pathContains(contribution.path, query) && !pathContains(query, contribution.path) {
|
||||
continue
|
||||
}
|
||||
sequencePathMatched = true
|
||||
if pathContains(contribution.path, query) && len(query.segments) > len(contribution.path.segments) {
|
||||
segment := query.segments[len(contribution.path.segments)]
|
||||
itemIndex, ok := schemaIndex(segment)
|
||||
if !ok || itemIndex < contribution.offset || itemIndex >= contribution.offset+contribution.length {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return query.clone(), true
|
||||
}
|
||||
if sequencePathMatched {
|
||||
return Path{}, false
|
||||
}
|
||||
if len(query.segments) == 0 {
|
||||
return Path{}, true
|
||||
}
|
||||
@@ -100,6 +119,22 @@ func historyApplicationOrigin(application historyApplication, operationPath Path
|
||||
if !ok {
|
||||
return origin
|
||||
}
|
||||
for _, contribution := range application.sequences {
|
||||
if !pathContains(contribution.path, operationPath) {
|
||||
continue
|
||||
}
|
||||
relative, ok = relativeHistoryPath(Path{}, operationPath)
|
||||
if !ok || len(relative.segments) < len(contribution.path.segments)+1 {
|
||||
break
|
||||
}
|
||||
itemPosition := len(contribution.path.segments)
|
||||
itemIndex, valid := schemaIndex(relative.segments[itemPosition])
|
||||
if !valid || itemIndex < contribution.offset || itemIndex >= contribution.offset+contribution.length {
|
||||
break
|
||||
}
|
||||
relative.segments[itemPosition] = pathSegment{kind: pathSegmentIndex, index: itemIndex - contribution.offset}
|
||||
break
|
||||
}
|
||||
if node, present := documentAtPathWithLookup(application.value, relative, lookup); present {
|
||||
origin.Locations = []Location{publicLocation(node.Location)}
|
||||
}
|
||||
@@ -119,13 +154,17 @@ func pathsIntersect(left, right Path) bool {
|
||||
|
||||
func applyHistoryApplication(root **document.Node, application historyApplication, index *mappingIndex) {
|
||||
if application.path.String() == "" {
|
||||
mergeHistoryDocument(root, application.value, index)
|
||||
mergeHistoryDocumentAtPath(root, application.value, Path{}, index, application.sequences)
|
||||
return
|
||||
}
|
||||
mergeHistoryAtPath(root, application.path, application.value, index)
|
||||
}
|
||||
|
||||
func mergeHistoryDocument(target **document.Node, incoming *document.Node, index *mappingIndex) {
|
||||
mergeHistoryDocumentAtPath(target, incoming, Path{}, index, nil)
|
||||
}
|
||||
|
||||
func mergeHistoryDocumentAtPath(target **document.Node, incoming *document.Node, path Path, index *mappingIndex, sequences []sequenceHistoryContribution) {
|
||||
if incoming == nil {
|
||||
return
|
||||
}
|
||||
@@ -141,10 +180,49 @@ func mergeHistoryDocument(target **document.Node, incoming *document.Node, index
|
||||
index.append(*target, cloned)
|
||||
continue
|
||||
}
|
||||
mergeHistoryDocument(&(*target).Mapping[position].Value, entry.Value, index)
|
||||
entryPath := path.Key(entry.Key)
|
||||
if contribution, found := sequenceHistoryAt(sequences, entryPath); found {
|
||||
applySequenceHistory(&(*target).Mapping[position].Value, entry.Value, contribution)
|
||||
continue
|
||||
}
|
||||
mergeHistoryDocumentAtPath(&(*target).Mapping[position].Value, entry.Value, entryPath, index, sequences)
|
||||
}
|
||||
}
|
||||
|
||||
func sequenceHistoryAt(contributions []sequenceHistoryContribution, path Path) (sequenceHistoryContribution, bool) {
|
||||
for _, contribution := range contributions {
|
||||
if contribution.path.String() == path.String() {
|
||||
return contribution, true
|
||||
}
|
||||
}
|
||||
return sequenceHistoryContribution{}, false
|
||||
}
|
||||
|
||||
func applySequenceHistory(target **document.Node, incoming *document.Node, contribution sequenceHistoryContribution) {
|
||||
if incoming == nil || incoming.Kind != document.Sequence {
|
||||
return
|
||||
}
|
||||
if contribution.mode == sequenceHistoryReplace || *target == nil || (*target).Kind != document.Sequence {
|
||||
*target = document.Clone(incoming)
|
||||
return
|
||||
}
|
||||
for _, item := range incoming.Sequence {
|
||||
(*target).Sequence = append((*target).Sequence, document.Clone(item))
|
||||
}
|
||||
}
|
||||
|
||||
func cloneSequenceHistoryContributions(values []sequenceHistoryContribution) []sequenceHistoryContribution {
|
||||
if values == nil {
|
||||
return nil
|
||||
}
|
||||
cloned := make([]sequenceHistoryContribution, len(values))
|
||||
for index, value := range values {
|
||||
cloned[index] = value
|
||||
cloned[index].path = value.path.clone()
|
||||
}
|
||||
return cloned
|
||||
}
|
||||
|
||||
func mergeHistoryAtPath(root **document.Node, path Path, incoming *document.Node, index *mappingIndex) {
|
||||
if root == nil || *root == nil || len(path.segments) == 0 {
|
||||
mergeHistoryDocument(root, incoming, index)
|
||||
@@ -152,7 +230,19 @@ func mergeHistoryAtPath(root **document.Node, path Path, incoming *document.Node
|
||||
}
|
||||
current := *root
|
||||
for segmentIndex, segment := range path.segments {
|
||||
if current.Kind != document.Mapping || segment.kind == pathSegmentIndex {
|
||||
if segment.kind == pathSegmentIndex {
|
||||
position, ok := schemaIndex(segment)
|
||||
if current.Kind != document.Sequence || !ok || position >= len(current.Sequence) {
|
||||
return
|
||||
}
|
||||
if segmentIndex == len(path.segments)-1 {
|
||||
mergeHistoryDocument(¤t.Sequence[position], incoming, index)
|
||||
return
|
||||
}
|
||||
current = current.Sequence[position]
|
||||
continue
|
||||
}
|
||||
if current.Kind != document.Mapping {
|
||||
return
|
||||
}
|
||||
position := index.find(current, segment.key)
|
||||
|
||||
Reference in New Issue
Block a user