Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
N
neo
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
2
Merge Requests
2
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Jobs
Commits
Open sidebar
Kirill Smelkov
neo
Commits
ded591a9
Commit
ded591a9
authored
Feb 05, 2019
by
Kirill Smelkov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
.
parent
cce696b2
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
97 additions
and
130 deletions
+97
-130
go/zodb/btree/btree.go.in
go/zodb/btree/btree.go.in
+32
-43
go/zodb/btree/btree_test.go
go/zodb/btree/btree_test.go
+1
-1
go/zodb/btree/ziobtree.go
go/zodb/btree/ziobtree.go
+32
-43
go/zodb/btree/zlobtree.go
go/zodb/btree/zlobtree.go
+32
-43
No files found.
go/zodb/btree/btree.go.in
View file @
ded591a9
...
@@ -61,7 +61,7 @@ type BTree struct {
...
@@ -61,7 +61,7 @@ type BTree struct {
//
Key
limits
child
's keys - see BTree.Entryv for details.
//
Key
limits
child
's keys - see BTree.Entryv for details.
type Entry struct {
type Entry struct {
key KEY
key KEY
child
interface{}
// BTree or Bucket
child
zodb.IPersistent
// BTree or Bucket
}
}
// Bucket is a leaf node of a B⁺ tree.
// Bucket is a leaf node of a B⁺ tree.
...
@@ -99,7 +99,7 @@ type BucketEntry struct {
...
@@ -99,7 +99,7 @@ type BucketEntry struct {
func (e *Entry) Key() KEY { return e.key }
func (e *Entry) Key() KEY { return e.key }
// Child returns BTree entry child.
// Child returns BTree entry child.
func (e *Entry) Child()
interface{}
{ return e.child }
func (e *Entry) Child()
zodb.IPersistent
{ return e.child }
// Entryv returns entries of a BTree node.
// Entryv returns entries of a BTree node.
//
//
...
@@ -153,30 +153,33 @@ func (b *Bucket) Next() *Bucket {
...
@@ -153,30 +153,33 @@ func (b *Bucket) Next() *Bucket {
//
//
// t need not be activated beforehand for Get to work.
// t need not be activated beforehand for Get to work.
func (t *BTree) Get(ctx context.Context, key KEY) (_ interface{}, _ bool, err error) {
func (t *BTree) Get(ctx context.Context, key KEY) (_ interface{}, _ bool, err error) {
v, ok, _, _, err := t.GetTo(ctx, key)
return t.GetTo(ctx, key, nil)
return v, ok, err
}
}
// GetTo searches BTree by key and
returns value and path
.
// GetTo searches BTree by key and
visits btree-path nodes
.
//
//
// It is similar to Get, but additionally provides information via which
// It is similar to Get, but additionally calls visit on every BTree node
// intermediate tree nodes and leaf bucket the key was found.
// (BTree or Bucket) it traverses from root to leaf to find the key.
func (t *BTree) GetTo(ctx context.Context, key KEY) (_ interface{}, _ bool, path []*BTree, leaf *Bucket, err error) {
//
// Visit is called with node being activated.
func (t *BTree) GetTo(ctx context.Context, key KEY, visit func(node zodb.IPersistent)) (_ interface{}, _ bool, err error) {
defer xerr.Contextf(&err, "btree(%s): get %v", t.POid(), key)
defer xerr.Contextf(&err, "btree(%s): get %v", t.POid(), key)
err = t.PActivate(ctx)
err = t.PActivate(ctx)
if err != nil {
if err != nil {
return nil, false, nil, nil, err
return nil, false, err
}
if visit != nil {
visit(t)
}
}
if len(t.data) == 0 {
if len(t.data) == 0 {
// empty btree
// empty btree
t.PDeactivate()
t.PDeactivate()
return nil, false, nil
, nil, nil
return nil, false, nil
}
}
for {
for {
path = append(path, t)
// search i: K(i) ≤ k < K(i+1) ; K(0) = -∞, K(len) = +∞
// search i: K(i) ≤ k < K(i+1) ; K(0) = -∞, K(len) = +∞
i := sort.Search(len(t.data), func(i int) bool {
i := sort.Search(len(t.data), func(i int) bool {
j := i + 1
j := i + 1
...
@@ -186,47 +189,33 @@ func (t *BTree) GetTo(ctx context.Context, key KEY) (_ interface{}, _ bool, path
...
@@ -186,47 +189,33 @@ func (t *BTree) GetTo(ctx context.Context, key KEY) (_ interface{}, _ bool, path
return key < t.data[j].key
return key < t.data[j].key
})
})
switch child := t.data[i].child.(type) {
child := t.data[i].child
t.PDeactivate()
err = child.PActivate(ctx)
if err != nil {
return nil, false, err
}
if visit != nil {
visit(child)
}
switch child := child.(type) {
case *BTree:
case *BTree:
t.PDeactivate()
t = child
t = child
err = t.PActivate(ctx)
if err != nil {
return nil, false, nil, nil, err
}
case *Bucket:
case *Bucket:
t.PDeactivate()
v, ok := child.Get(key)
v, ok, err := child.Get(ctx, key)
child.PDeactivate()
if err != nil {
return v, ok, nil
path = nil
child = nil
}
return v, ok, path, child, err
}
}
}
}
}
}
// Get searches Bucket by key.
// Get searches Bucket by key.
//
//
// TODO Bucket.Get should not get ctx argument and just require that the bucket
// must be already activated. Correspondingly there should be no error returned.
func (b *Bucket) Get(ctx context.Context, key KEY) (_ interface{}, _ bool, err error) {
defer xerr.Contextf(&err, "bucket(%s): get %v", b.POid(), key)
err = b.PActivate(ctx)
if err != nil {
return nil, false, err
}
v, ok := b.get(key)
b.PDeactivate()
return v, ok, nil
}
// get searches Bucket by key.
//
// no loading from database is done. The bucket must be already activated.
// no loading from database is done. The bucket must be already activated.
func (b *Bucket)
g
et(key KEY) (interface{}, bool) {
func (b *Bucket)
G
et(key KEY) (interface{}, bool) {
// search i: K(i-1) < k ≤ K(i) ; K(-1) = -∞, K(len) = +∞
// search i: K(i-1) < k ≤ K(i) ; K(-1) = -∞, K(len) = +∞
i := sort.Search(len(b.keys), func(i int) bool {
i := sort.Search(len(b.keys), func(i int) bool {
return key <= b.keys[i]
return key <= b.keys[i]
...
@@ -599,7 +588,7 @@ func (bt *btreeState) PySetState(pystate interface{}) (err error) {
...
@@ -599,7 +588,7 @@ func (bt *btreeState) PySetState(pystate interface{}) (err error) {
fmt.Errorf("data: [%d]: children must be of the same type", i)
fmt.Errorf("data: [%d]: children must be of the same type", i)
}
}
bt.data = append(bt.data, Entry{key: kkey, child: child})
bt.data = append(bt.data, Entry{key: kkey, child: child
.(zodb.IPersistent)
})
}
}
return nil
return nil
...
...
go/zodb/btree/btree_test.go
View file @
ded591a9
...
@@ -79,7 +79,7 @@ func (b *bucketWrap) withBucket(ctx context.Context, f func()) error {
...
@@ -79,7 +79,7 @@ func (b *bucketWrap) withBucket(ctx context.Context, f func()) error {
func
(
b
*
bucketWrap
)
Get
(
ctx
context
.
Context
,
key
int64
)
(
v
interface
{},
ok
bool
,
err
error
)
{
func
(
b
*
bucketWrap
)
Get
(
ctx
context
.
Context
,
key
int64
)
(
v
interface
{},
ok
bool
,
err
error
)
{
err
=
b
.
withBucket
(
ctx
,
func
()
{
err
=
b
.
withBucket
(
ctx
,
func
()
{
v
,
ok
=
b
.
bucket
()
.
get
(
key
)
// TODO -> Get
v
,
ok
=
b
.
bucket
()
.
Get
(
key
)
})
})
return
return
}
}
...
...
go/zodb/btree/ziobtree.go
View file @
ded591a9
...
@@ -63,7 +63,7 @@ type IOBTree struct {
...
@@ -63,7 +63,7 @@ type IOBTree struct {
// Key limits child's keys - see IOBTree.Entryv for details.
// Key limits child's keys - see IOBTree.Entryv for details.
type
IOEntry
struct
{
type
IOEntry
struct
{
key
int32
key
int32
child
interface
{}
// IOBTree or IOBucket
child
zodb
.
IPersistent
// IOBTree or IOBucket
}
}
// IOBucket is a leaf node of a B⁺ tree.
// IOBucket is a leaf node of a B⁺ tree.
...
@@ -101,7 +101,7 @@ type IOBucketEntry struct {
...
@@ -101,7 +101,7 @@ type IOBucketEntry struct {
func
(
e
*
IOEntry
)
Key
()
int32
{
return
e
.
key
}
func
(
e
*
IOEntry
)
Key
()
int32
{
return
e
.
key
}
// Child returns IOBTree entry child.
// Child returns IOBTree entry child.
func
(
e
*
IOEntry
)
Child
()
interface
{}
{
return
e
.
child
}
func
(
e
*
IOEntry
)
Child
()
zodb
.
IPersistent
{
return
e
.
child
}
// Entryv returns entries of a IOBTree node.
// Entryv returns entries of a IOBTree node.
//
//
...
@@ -155,30 +155,33 @@ func (b *IOBucket) Next() *IOBucket {
...
@@ -155,30 +155,33 @@ func (b *IOBucket) Next() *IOBucket {
//
//
// t need not be activated beforehand for Get to work.
// t need not be activated beforehand for Get to work.
func
(
t
*
IOBTree
)
Get
(
ctx
context
.
Context
,
key
int32
)
(
_
interface
{},
_
bool
,
err
error
)
{
func
(
t
*
IOBTree
)
Get
(
ctx
context
.
Context
,
key
int32
)
(
_
interface
{},
_
bool
,
err
error
)
{
v
,
ok
,
_
,
_
,
err
:=
t
.
GetTo
(
ctx
,
key
)
return
t
.
GetTo
(
ctx
,
key
,
nil
)
return
v
,
ok
,
err
}
}
// GetTo searches IOBTree by key and
returns value and path
.
// GetTo searches IOBTree by key and
visits btree-path nodes
.
//
//
// It is similar to Get, but additionally provides information via which
// It is similar to Get, but additionally calls visit on every IOBTree node
// intermediate tree nodes and leaf bucket the key was found.
// (IOBTree or IOBucket) it traverses from root to leaf to find the key.
func
(
t
*
IOBTree
)
GetTo
(
ctx
context
.
Context
,
key
int32
)
(
_
interface
{},
_
bool
,
path
[]
*
IOBTree
,
leaf
*
IOBucket
,
err
error
)
{
//
// Visit is called with node being activated.
func
(
t
*
IOBTree
)
GetTo
(
ctx
context
.
Context
,
key
int32
,
visit
func
(
node
zodb
.
IPersistent
))
(
_
interface
{},
_
bool
,
err
error
)
{
defer
xerr
.
Contextf
(
&
err
,
"btree(%s): get %v"
,
t
.
POid
(),
key
)
defer
xerr
.
Contextf
(
&
err
,
"btree(%s): get %v"
,
t
.
POid
(),
key
)
err
=
t
.
PActivate
(
ctx
)
err
=
t
.
PActivate
(
ctx
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
false
,
nil
,
nil
,
err
return
nil
,
false
,
err
}
if
visit
!=
nil
{
visit
(
t
)
}
}
if
len
(
t
.
data
)
==
0
{
if
len
(
t
.
data
)
==
0
{
// empty btree
// empty btree
t
.
PDeactivate
()
t
.
PDeactivate
()
return
nil
,
false
,
nil
,
nil
,
nil
return
nil
,
false
,
nil
}
}
for
{
for
{
path
=
append
(
path
,
t
)
// search i: K(i) ≤ k < K(i+1) ; K(0) = -∞, K(len) = +∞
// search i: K(i) ≤ k < K(i+1) ; K(0) = -∞, K(len) = +∞
i
:=
sort
.
Search
(
len
(
t
.
data
),
func
(
i
int
)
bool
{
i
:=
sort
.
Search
(
len
(
t
.
data
),
func
(
i
int
)
bool
{
j
:=
i
+
1
j
:=
i
+
1
...
@@ -188,47 +191,33 @@ func (t *IOBTree) GetTo(ctx context.Context, key int32) (_ interface{}, _ bool,
...
@@ -188,47 +191,33 @@ func (t *IOBTree) GetTo(ctx context.Context, key int32) (_ interface{}, _ bool,
return
key
<
t
.
data
[
j
]
.
key
return
key
<
t
.
data
[
j
]
.
key
})
})
switch
child
:=
t
.
data
[
i
]
.
child
.
(
type
)
{
child
:=
t
.
data
[
i
]
.
child
t
.
PDeactivate
()
err
=
child
.
PActivate
(
ctx
)
if
err
!=
nil
{
return
nil
,
false
,
err
}
if
visit
!=
nil
{
visit
(
child
)
}
switch
child
:=
child
.
(
type
)
{
case
*
IOBTree
:
case
*
IOBTree
:
t
.
PDeactivate
()
t
=
child
t
=
child
err
=
t
.
PActivate
(
ctx
)
if
err
!=
nil
{
return
nil
,
false
,
nil
,
nil
,
err
}
case
*
IOBucket
:
case
*
IOBucket
:
t
.
PDeactivate
()
v
,
ok
:=
child
.
Get
(
key
)
v
,
ok
,
err
:=
child
.
Get
(
ctx
,
key
)
child
.
PDeactivate
()
if
err
!=
nil
{
return
v
,
ok
,
nil
path
=
nil
child
=
nil
}
return
v
,
ok
,
path
,
child
,
err
}
}
}
}
}
}
// Get searches IOBucket by key.
// Get searches IOBucket by key.
//
//
// TODO IOBucket.Get should not get ctx argument and just require that the bucket
// must be already activated. Correspondingly there should be no error returned.
func
(
b
*
IOBucket
)
Get
(
ctx
context
.
Context
,
key
int32
)
(
_
interface
{},
_
bool
,
err
error
)
{
defer
xerr
.
Contextf
(
&
err
,
"bucket(%s): get %v"
,
b
.
POid
(),
key
)
err
=
b
.
PActivate
(
ctx
)
if
err
!=
nil
{
return
nil
,
false
,
err
}
v
,
ok
:=
b
.
get
(
key
)
b
.
PDeactivate
()
return
v
,
ok
,
nil
}
// get searches IOBucket by key.
//
// no loading from database is done. The bucket must be already activated.
// no loading from database is done. The bucket must be already activated.
func
(
b
*
IOBucket
)
g
et
(
key
int32
)
(
interface
{},
bool
)
{
func
(
b
*
IOBucket
)
G
et
(
key
int32
)
(
interface
{},
bool
)
{
// search i: K(i-1) < k ≤ K(i) ; K(-1) = -∞, K(len) = +∞
// search i: K(i-1) < k ≤ K(i) ; K(-1) = -∞, K(len) = +∞
i
:=
sort
.
Search
(
len
(
b
.
keys
),
func
(
i
int
)
bool
{
i
:=
sort
.
Search
(
len
(
b
.
keys
),
func
(
i
int
)
bool
{
return
key
<=
b
.
keys
[
i
]
return
key
<=
b
.
keys
[
i
]
...
@@ -601,7 +590,7 @@ func (bt *iobtreeState) PySetState(pystate interface{}) (err error) {
...
@@ -601,7 +590,7 @@ func (bt *iobtreeState) PySetState(pystate interface{}) (err error) {
fmt
.
Errorf
(
"data: [%d]: children must be of the same type"
,
i
)
fmt
.
Errorf
(
"data: [%d]: children must be of the same type"
,
i
)
}
}
bt
.
data
=
append
(
bt
.
data
,
IOEntry
{
key
:
kkey
,
child
:
child
})
bt
.
data
=
append
(
bt
.
data
,
IOEntry
{
key
:
kkey
,
child
:
child
.
(
zodb
.
IPersistent
)
})
}
}
return
nil
return
nil
...
...
go/zodb/btree/zlobtree.go
View file @
ded591a9
...
@@ -63,7 +63,7 @@ type LOBTree struct {
...
@@ -63,7 +63,7 @@ type LOBTree struct {
// Key limits child's keys - see LOBTree.Entryv for details.
// Key limits child's keys - see LOBTree.Entryv for details.
type
LOEntry
struct
{
type
LOEntry
struct
{
key
int64
key
int64
child
interface
{}
// LOBTree or LOBucket
child
zodb
.
IPersistent
// LOBTree or LOBucket
}
}
// LOBucket is a leaf node of a B⁺ tree.
// LOBucket is a leaf node of a B⁺ tree.
...
@@ -101,7 +101,7 @@ type LOBucketEntry struct {
...
@@ -101,7 +101,7 @@ type LOBucketEntry struct {
func
(
e
*
LOEntry
)
Key
()
int64
{
return
e
.
key
}
func
(
e
*
LOEntry
)
Key
()
int64
{
return
e
.
key
}
// Child returns LOBTree entry child.
// Child returns LOBTree entry child.
func
(
e
*
LOEntry
)
Child
()
interface
{}
{
return
e
.
child
}
func
(
e
*
LOEntry
)
Child
()
zodb
.
IPersistent
{
return
e
.
child
}
// Entryv returns entries of a LOBTree node.
// Entryv returns entries of a LOBTree node.
//
//
...
@@ -155,30 +155,33 @@ func (b *LOBucket) Next() *LOBucket {
...
@@ -155,30 +155,33 @@ func (b *LOBucket) Next() *LOBucket {
//
//
// t need not be activated beforehand for Get to work.
// t need not be activated beforehand for Get to work.
func
(
t
*
LOBTree
)
Get
(
ctx
context
.
Context
,
key
int64
)
(
_
interface
{},
_
bool
,
err
error
)
{
func
(
t
*
LOBTree
)
Get
(
ctx
context
.
Context
,
key
int64
)
(
_
interface
{},
_
bool
,
err
error
)
{
v
,
ok
,
_
,
_
,
err
:=
t
.
GetTo
(
ctx
,
key
)
return
t
.
GetTo
(
ctx
,
key
,
nil
)
return
v
,
ok
,
err
}
}
// GetTo searches LOBTree by key and
returns value and path
.
// GetTo searches LOBTree by key and
visits btree-path nodes
.
//
//
// It is similar to Get, but additionally provides information via which
// It is similar to Get, but additionally calls visit on every LOBTree node
// intermediate tree nodes and leaf bucket the key was found.
// (LOBTree or LOBucket) it traverses from root to leaf to find the key.
func
(
t
*
LOBTree
)
GetTo
(
ctx
context
.
Context
,
key
int64
)
(
_
interface
{},
_
bool
,
path
[]
*
LOBTree
,
leaf
*
LOBucket
,
err
error
)
{
//
// Visit is called with node being activated.
func
(
t
*
LOBTree
)
GetTo
(
ctx
context
.
Context
,
key
int64
,
visit
func
(
node
zodb
.
IPersistent
))
(
_
interface
{},
_
bool
,
err
error
)
{
defer
xerr
.
Contextf
(
&
err
,
"btree(%s): get %v"
,
t
.
POid
(),
key
)
defer
xerr
.
Contextf
(
&
err
,
"btree(%s): get %v"
,
t
.
POid
(),
key
)
err
=
t
.
PActivate
(
ctx
)
err
=
t
.
PActivate
(
ctx
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
false
,
nil
,
nil
,
err
return
nil
,
false
,
err
}
if
visit
!=
nil
{
visit
(
t
)
}
}
if
len
(
t
.
data
)
==
0
{
if
len
(
t
.
data
)
==
0
{
// empty btree
// empty btree
t
.
PDeactivate
()
t
.
PDeactivate
()
return
nil
,
false
,
nil
,
nil
,
nil
return
nil
,
false
,
nil
}
}
for
{
for
{
path
=
append
(
path
,
t
)
// search i: K(i) ≤ k < K(i+1) ; K(0) = -∞, K(len) = +∞
// search i: K(i) ≤ k < K(i+1) ; K(0) = -∞, K(len) = +∞
i
:=
sort
.
Search
(
len
(
t
.
data
),
func
(
i
int
)
bool
{
i
:=
sort
.
Search
(
len
(
t
.
data
),
func
(
i
int
)
bool
{
j
:=
i
+
1
j
:=
i
+
1
...
@@ -188,47 +191,33 @@ func (t *LOBTree) GetTo(ctx context.Context, key int64) (_ interface{}, _ bool,
...
@@ -188,47 +191,33 @@ func (t *LOBTree) GetTo(ctx context.Context, key int64) (_ interface{}, _ bool,
return
key
<
t
.
data
[
j
]
.
key
return
key
<
t
.
data
[
j
]
.
key
})
})
switch
child
:=
t
.
data
[
i
]
.
child
.
(
type
)
{
child
:=
t
.
data
[
i
]
.
child
t
.
PDeactivate
()
err
=
child
.
PActivate
(
ctx
)
if
err
!=
nil
{
return
nil
,
false
,
err
}
if
visit
!=
nil
{
visit
(
child
)
}
switch
child
:=
child
.
(
type
)
{
case
*
LOBTree
:
case
*
LOBTree
:
t
.
PDeactivate
()
t
=
child
t
=
child
err
=
t
.
PActivate
(
ctx
)
if
err
!=
nil
{
return
nil
,
false
,
nil
,
nil
,
err
}
case
*
LOBucket
:
case
*
LOBucket
:
t
.
PDeactivate
()
v
,
ok
:=
child
.
Get
(
key
)
v
,
ok
,
err
:=
child
.
Get
(
ctx
,
key
)
child
.
PDeactivate
()
if
err
!=
nil
{
return
v
,
ok
,
nil
path
=
nil
child
=
nil
}
return
v
,
ok
,
path
,
child
,
err
}
}
}
}
}
}
// Get searches LOBucket by key.
// Get searches LOBucket by key.
//
//
// TODO LOBucket.Get should not get ctx argument and just require that the bucket
// must be already activated. Correspondingly there should be no error returned.
func
(
b
*
LOBucket
)
Get
(
ctx
context
.
Context
,
key
int64
)
(
_
interface
{},
_
bool
,
err
error
)
{
defer
xerr
.
Contextf
(
&
err
,
"bucket(%s): get %v"
,
b
.
POid
(),
key
)
err
=
b
.
PActivate
(
ctx
)
if
err
!=
nil
{
return
nil
,
false
,
err
}
v
,
ok
:=
b
.
get
(
key
)
b
.
PDeactivate
()
return
v
,
ok
,
nil
}
// get searches LOBucket by key.
//
// no loading from database is done. The bucket must be already activated.
// no loading from database is done. The bucket must be already activated.
func
(
b
*
LOBucket
)
g
et
(
key
int64
)
(
interface
{},
bool
)
{
func
(
b
*
LOBucket
)
G
et
(
key
int64
)
(
interface
{},
bool
)
{
// search i: K(i-1) < k ≤ K(i) ; K(-1) = -∞, K(len) = +∞
// search i: K(i-1) < k ≤ K(i) ; K(-1) = -∞, K(len) = +∞
i
:=
sort
.
Search
(
len
(
b
.
keys
),
func
(
i
int
)
bool
{
i
:=
sort
.
Search
(
len
(
b
.
keys
),
func
(
i
int
)
bool
{
return
key
<=
b
.
keys
[
i
]
return
key
<=
b
.
keys
[
i
]
...
@@ -601,7 +590,7 @@ func (bt *lobtreeState) PySetState(pystate interface{}) (err error) {
...
@@ -601,7 +590,7 @@ func (bt *lobtreeState) PySetState(pystate interface{}) (err error) {
fmt
.
Errorf
(
"data: [%d]: children must be of the same type"
,
i
)
fmt
.
Errorf
(
"data: [%d]: children must be of the same type"
,
i
)
}
}
bt
.
data
=
append
(
bt
.
data
,
LOEntry
{
key
:
kkey
,
child
:
child
})
bt
.
data
=
append
(
bt
.
data
,
LOEntry
{
key
:
kkey
,
child
:
child
.
(
zodb
.
IPersistent
)
})
}
}
return
nil
return
nil
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment