Commit aa1d7e12 authored by Kirill Smelkov's avatar Kirill Smelkov

go/zodb/zodbtools: Catobj

`zodb catobj` command to dump content of an object - similarly to `git
cat-file`. Two modes: raw and verbose with `zodb dump` like headers for
the object present.

There is no such command currently in zodbtools/py.
parent 27d02ad5
// Copyright (C) 2017 Nexedi SA and Contributors.
// Kirill Smelkov <>
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// See COPYING file for full licensing terms.
// See for rationale and options.
package zodbtools
// Catobj - dump content of a database object
import (
// Catobj dumps content of one ZODB object.
// The object is printed in raw form without any headers (see Dumpobj)
func Catobj(ctx context.Context, w io.Writer, stor zodb.IStorage, xid zodb.Xid) error {
buf, _, err := stor.Load(ctx, xid)
if err != nil {
return err
_, err = w.Write(buf.Data) // NOTE deleted data are returned as err by Load
if err != nil {
err = fmt.Errorf("%s: catobj %s: %s", stor.URL(), xid, err)
return err
// Dumpobj dumps content of one ZODB object with zodbdump-like header
func Dumpobj(ctx context.Context, w io.Writer, stor zodb.IStorage, xid zodb.Xid, hashOnly bool) error {
var objInfo zodb.DataInfo
buf, serial, err := stor.Load(ctx, xid)
if err != nil {
return err
objInfo.Oid = xid.Oid
objInfo.Tid = serial
objInfo.Data = buf.Data
objInfo.DataTidHint = 0 // no copy detection at catobj - just dump raw content
d := dumper{W: w, HashOnly: hashOnly}
err = d.DumpData(&objInfo)
return err
// ----------------------------------------
const catobjSummary = "dump content of a database object"
func catobjUsage(w io.Writer) {
`Usage: zodb catobj [OPTIONS] <storage> xid...
Dump content of a ZODB database object.
<storage> is an URL (see 'zodb help zurl') of a ZODB-storage.
xid is object address (see 'zodb help xid').
-h --help this help text.
-hashonly dump only hashes of objects without content.
-raw dump object data without any headers. Only one object allowed.
func catobjMain(argv []string) {
hashOnly := false
raw := false
flags := flag.FlagSet{Usage: func() { catobjUsage(os.Stderr) }}
flags.Init("", flag.ExitOnError)
flags.BoolVar(&hashOnly, "hashonly", hashOnly, "dump only hashes of objects")
flags.BoolVar(&raw, "raw", hashOnly, "dump object data without any headers. Only one object allowed.")
// TODO also -batch to serve objects a-la `git cat-file --batch` ?
argv = flags.Args()
if len(argv) < 2 {
storUrl := argv[0]
if hashOnly && raw {
prog.Fatal("-hashonly & -raw are incompatible")
xidv := []zodb.Xid{}
for _, arg := range argv[1:] {
xid, err := zodb.ParseXid(arg)
if err != nil {
xidv = append(xidv, xid)
if raw && len(xidv) > 1 {
prog.Fatal("only 1 object allowed with -raw")
ctx := context.Background()
stor, err := zodb.OpenStorage(ctx, storUrl, &zodb.OpenOptions{ReadOnly: true})
if err != nil {
// TODO defer stor.Close()
catobj := func(xid zodb.Xid) error {
if raw {
return Catobj(ctx, os.Stdout, stor, xid)
} else {
return Dumpobj(ctx, os.Stdout, stor, xid, hashOnly)
for _, xid := range xidv {
err = catobj(xid)
if err != nil {
......@@ -27,6 +27,7 @@ var commands = prog.CommandRegistry{
// NOTE the order commands are listed here is the order how they will appear in help
{"info", infoSummary, infoUsage, infoMain},
{"dump", dumpSummary, dumpUsage, dumpMain},
{"catobj", catobjSummary, catobjUsage, catobjMain},
// main zodbtools driver
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment