Calling updateAndReturnDoc directly allows callers to: 1. Receive the updated document body in the response 2. Specify the existing document body/xattr/cas, to avoid initial retrieval of the doc in cases that the current contents are already known (e.g. import). On cas failure, the document will sti
(ctx context.Context, docid string, allowImport bool, expiry *uint32, opts *sgbucket.MutateInOptions, docUpdateEvent DocUpdateType, existingDoc *sgbucket.BucketDocument, isImport bool, updateRevCache bool, callback updateAndReturnDocCallback)
| 2690 | // 3. If isImport=true, document body will not be updated - only metadata xattr(s) |
| 2691 | |
| 2692 | func (db *DatabaseCollectionWithUser) updateAndReturnDoc(ctx context.Context, docid string, allowImport bool, expiry *uint32, opts *sgbucket.MutateInOptions, docUpdateEvent DocUpdateType, existingDoc *sgbucket.BucketDocument, isImport bool, updateRevCache bool, callback updateAndReturnDocCallback) (doc *Document, newRevID string, err error) { |
| 2693 | key := realDocID(docid) |
| 2694 | if key == "" { |
| 2695 | return nil, "", base.HTTPErrorf(400, "Invalid doc ID") |
| 2696 | } |
| 2697 | |
| 2698 | var prevCurrentRev string |
| 2699 | var storedDoc *Document |
| 2700 | var changedAccessPrincipals, changedRoleAccessUsers []string // Returned by documentUpdateFunc |
| 2701 | var docSequence uint64 // Must be scoped outside callback, used over multiple iterations |
| 2702 | var unusedSequences []uint64 // Must be scoped outside callback, used over multiple iterations |
| 2703 | var oldBodyJSON string // Stores previous revision body for use by DocumentChangeEvent |
| 2704 | var createNewRevIDSkipped bool |
| 2705 | var previousAttachments map[string][]string |
| 2706 | |
| 2707 | // Update the document |
| 2708 | inConflict := false |
| 2709 | upgradeInProgress := false |
| 2710 | docBytes := 0 // Track size of document written, for write stats |
| 2711 | xattrBytes := 0 // Track size of xattr written, for write stats |
| 2712 | skipObsoleteAttachmentsRemoval := false |
| 2713 | isNewDocCreation := false |
| 2714 | |
| 2715 | // Don't remove obsolete attachments if using ECCV - the other cluster may still need them! |
| 2716 | if db.dbCtx.CachedCCVEnabled.Load() { |
| 2717 | skipObsoleteAttachmentsRemoval = true |
| 2718 | } |
| 2719 | |
| 2720 | if db.UseXattrs() || upgradeInProgress { |
| 2721 | var casOut uint64 |
| 2722 | // Update the document, storing metadata in extended attribute |
| 2723 | if opts == nil { |
| 2724 | opts = &sgbucket.MutateInOptions{} |
| 2725 | } |
| 2726 | opts.MacroExpansion = macroExpandSpec(base.SyncXattrName) |
| 2727 | var initialExpiry uint32 |
| 2728 | if expiry != nil { |
| 2729 | initialExpiry = *expiry |
| 2730 | } |
| 2731 | casOut, err = db.dataStore.WriteUpdateWithXattrs(ctx, key, db.syncGlobalSyncMouRevSeqNoAndUserXattrKeys(), initialExpiry, existingDoc, opts, func(currentValue []byte, currentXattrs map[string][]byte, cas uint64) (updatedDoc sgbucket.UpdatedDoc, err error) { |
| 2732 | // Be careful: this block can be invoked multiple times if there are races! |
| 2733 | if doc, err = db.unmarshalDocumentWithXattrs(ctx, docid, currentValue, currentXattrs, cas, DocUnmarshalAll); err != nil { |
| 2734 | return |
| 2735 | } |
| 2736 | |
| 2737 | prevCurrentRev = doc.GetRevTreeID() |
| 2738 | |
| 2739 | // Check whether Sync Data originated in body |
| 2740 | currentSyncXattr := currentXattrs[base.SyncXattrName] |
| 2741 | if currentSyncXattr == nil && doc.Sequence > 0 { |
| 2742 | doc.inlineSyncData = true |
| 2743 | } |
| 2744 | |
| 2745 | previousAttachments, err = getAttachmentIDsForLeafRevisions(ctx, db, doc, newRevID) |
| 2746 | if err != nil { |
| 2747 | skipObsoleteAttachmentsRemoval = true |
| 2748 | base.ErrorfCtx(ctx, "Error retrieving previous leaf attachments of doc: %s, Error: %v", base.UD(docid), err) |
| 2749 | } |
no test coverage detected