getOldRevisionJSON looks up the raw JSON data of a revision that's been archived to a separate doc. If the revision isn't found (e.g. has been deleted by compaction) returns 404 error.
(ctx context.Context, docid string, revOrCV string)
| 244 | // getOldRevisionJSON looks up the raw JSON data of a revision that's been archived to a separate doc. |
| 245 | // If the revision isn't found (e.g. has been deleted by compaction) returns 404 error. |
| 246 | func (c *DatabaseCollection) getOldRevisionJSON(ctx context.Context, docid string, revOrCV string) ([]byte, base.Set, bool, error) { |
| 247 | data, _, err := c.dataStore.GetRaw(oldRevisionKey(docid, revOrCV)) |
| 248 | if base.IsDocNotFoundError(err) { |
| 249 | base.DebugfCtx(ctx, base.KeyCRUD, "No old revision %q / %q", base.UD(docid), revOrCV) |
| 250 | return nil, nil, false, ErrMissing |
| 251 | } else if err != nil { |
| 252 | return nil, nil, false, err |
| 253 | } |
| 254 | kind, revData := stripNonJSONPrefix(data) |
| 255 | switch kind { |
| 256 | case nonJSONPrefixKindRevBody: |
| 257 | base.DebugfCtx(ctx, base.KeyCRUD, "Got old revision %q / %q --> %d bytes", base.UD(docid), revOrCV, len(revData)) |
| 258 | return revData, nil, false, nil |
| 259 | case nonJSONPrefixKindRevPtr: |
| 260 | // pointer to a CV-keyed backup revision - do another fetch by CV for the body |
| 261 | base.DebugfCtx(ctx, base.KeyCRUD, "Found old revision pointer %q -> %q / %q", revOrCV, base.UD(docid), string(revData)) |
| 262 | return c.getOldRevisionJSON(ctx, docid, string(revData)) |
| 263 | case nonJSONPrefixKindRevWithMeta: |
| 264 | jsonBody, meta, err := getOldRevisionJSONAndMeta(revData) |
| 265 | if err != nil { |
| 266 | return nil, nil, false, err |
| 267 | } |
| 268 | base.DebugfCtx(ctx, base.KeyCRUD, "Got old revision (with %d meta properties) %q / %q --> %d bytes", len(meta), base.UD(docid), revOrCV, len(jsonBody)) |
| 269 | var channelSet base.Set |
| 270 | if ch, ok := meta[backupRevisionChannelsMetaKey]; ok && ch != nil { |
| 271 | err = base.JSONUnmarshal(ch, &channelSet) |
| 272 | if err != nil { |
| 273 | return nil, nil, false, fmt.Errorf("error unmarshalling _channels xattr for old revision %q / %q: %v", base.UD(docid), revOrCV, err) |
| 274 | } |
| 275 | } |
| 276 | var deletedDoc bool |
| 277 | if deletedMeta, ok := meta[backupRevisionBodyDeletedMetaKey]; ok && deletedMeta != nil { |
| 278 | err = base.JSONUnmarshal(deletedMeta, &deletedDoc) |
| 279 | if err != nil { |
| 280 | return nil, nil, false, fmt.Errorf("error unmarshalling _deleted xattr for old revision %q / %q: %v", base.UD(docid), revOrCV, err) |
| 281 | } |
| 282 | } |
| 283 | return jsonBody, channelSet, deletedDoc, nil |
| 284 | default: |
| 285 | return nil, nil, false, fmt.Errorf("unexpected prefix %b for old revision doc %q / %q", kind, base.UD(docid).String(), revOrCV) |
| 286 | } |
| 287 | } |
| 288 | |
| 289 | func getOldRevisionJSONAndMeta(payload []byte) (jsonBody []byte, kvPairs map[string][]byte, err error) { |
| 290 | body, meta, err := sgbucket.DecodeValueWithAllXattrs(payload) |