Commit d4e577aa authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent 0979eab2
......@@ -90,7 +90,7 @@ func NeedPy(t testing.TB, modules ...string) {
// ZObject represents object state to be committed.
type ZObject struct {
Oid zodb.Oid
Data string
Data string // raw serialized zodb data
}
// ZPyCommit commits new transaction into database @ zurl with data specified by objv.
......
// Copyright (C) 2019 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// 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
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
package zodb
// things exported for zodb_test package.
import (
"lab.nexedi.com/kirr/go123/mem"
)
func PSerialize(obj IPersistent) *mem.Buf {
return obj.pSerialize()
}
......@@ -20,5 +20,4 @@
package zodb
// things imported at runtime via import_x_test due to cyclic dependency
var ZPyCommit func(string, Tid, ...interface{}) (Tid, error) // XXX ZObject
var ZPyCommit func(string, Tid, ...IPersistent) (Tid, error)
......@@ -28,9 +28,20 @@ import (
)
// import at runtime few things into zodb, that zodb cannot import itself due to cyclic dependency.
func init() {
//zodb.ZPyCommit = xtesting.ZPyCommit
zodb.ZPyCommit = func(zurl string, at zodb.Tid, objv ...interface{}) (zodb.Tid, error) {
return xtesting.ZPyCommit(zurl, at) // XXX + objv
zodb.ZPyCommit = ZPyCommit
}
func ZPyCommit(zurl string, at zodb.Tid, objv ...zodb.IPersistent) (zodb.Tid, error) {
var zobjv []xtesting.ZObject // raw zodb objects data to commit
for _, obj := range objv {
zobj := xtesting.ZObject{
Oid: obj.POid(),
Data: string(zodb.PSerialize(obj).XData()),
}
zobjv = append(zobjv, zobj)
}
return xtesting.ZPyCommit(zurl, at, zobjv...)
}
......@@ -121,6 +121,24 @@ type Stateful interface {
GetState() *mem.Buf
}
// ---- serialize ----
// pSerialize implements IPersistent.
func (obj *Persistent) pSerialize() *mem.Buf {
// XXX locking
// XXX panic on state == GHOST
switch istate := obj.istate().(type) {
case Stateful:
return istate.GetState()
case PyStateful:
return pyGetState(istate, ClassOf(obj))
default:
panic(obj.badf("serialize: !stateful instance"))
}
}
// ---- activate/deactivate/invalidate ----
......
// Copyright (c) 2001, 2002 Zope Foundation and Contributors.
// All Rights Reserved.
//
// Copyright (C) 2018 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
// Copyright (C) 2018-2019 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This software is subject to the provisions of the Zope Public License,
// Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
......@@ -16,6 +16,8 @@ package zodb
import (
"context"
"lab.nexedi.com/kirr/go123/mem"
)
// IPersistent is the interface that every in-RAM object representing any database object implements.
......@@ -97,6 +99,16 @@ type IPersistent interface {
// XXX probably don't need this.
//PState() ObjectState // in-RAM object state.
// XXX move vvv -> iPersistent?
// pSerialize returns object in serialized form to be saved in the database.
//
// pSerialize is non-public method that is exposed and used only by ZODB internally.
//
// XXX more text.
// XXX when called? invariants?
pSerialize() *mem.Buf
}
// ObjectState describes state of in-RAM object.
......
// Copyright (C) 2016-2018 Nexedi SA and Contributors.
// Copyright (C) 2016-2019 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
......@@ -24,6 +24,7 @@ import (
"bytes"
"encoding/binary"
"fmt"
"strings"
pickle "github.com/kisielk/og-rek"
"lab.nexedi.com/kirr/go123/xerr"
......@@ -59,6 +60,35 @@ func (d PyData) ClassName() string {
return pyclassPath(klass)
}
// encodePyData encodes Python class and state into raw ZODB python data.
func encodePyData(pyclass pickle.Class, pystate interface{}) PyData {
buf := &bytes.Buffer{}
p := pickle.NewEncoderWithConfig(buf, &pickle.EncoderConfig{
// allow pristine python2 to decode the pickle.
// TODO 2 -> 3 since ZODB switched to it and uses zodbpickle.
Protocol: 2,
PersistentRef: persistentRef,
})
// emit: object type
err := p.Encode(pyclass)
if err != nil {
// buf.Write never errors, as well as pickle encoder on supported types.
// -> the error here is a bug.
panic(fmt.Errorf("pydata: encode: class: %s", err))
}
// emit: object state
err = p.Encode(pystate)
if err != nil {
// see ^^^
panic(fmt.Errorf("pydata: encode: state: %s", err))
}
return PyData(buf.Bytes())
}
// TODO PyData.referencesf
// decode decodes raw ZODB python data into Python class and state.
......@@ -90,6 +120,21 @@ func (d PyData) decode(jar *Connection) (pyclass pickle.Class, pystate interface
return klass, state, nil
}
// persistentRef decides whether to encode obj as persistent reference, and if yes - how.
func persistentRef(obj interface{}) *pickle.Ref {
pobj, ok := obj.(IPersistent)
if !ok {
// regular object - include its state when encoding referee
return nil
}
// Persistent object - when encoding someone who references it - don't
// include obj state and just reference to obj.
return &pickle.Ref{
Pid: pickle.Tuple{pobj.POid(), zpyclass(ClassOf(pobj))}, // (oid, class)
}
}
// loadref loads persistent references resolving them through jar.
//
// https://github.com/zopefoundation/ZODB/blob/a89485c1/src/ZODB/serialize.py#L80
......@@ -122,6 +167,22 @@ func (jar *Connection) loadref(ref pickle.Ref) (_ interface{}, err error) {
return jar.get(class, oid)
}
// zpyclass converts ZODB class into Python class.
func zpyclass(zclass string) pickle.Class {
// BTrees.LOBTree.LOBucket -> BTrees.LOBTree, LOBucket
var zmod, zname string
dot := strings.LastIndexByte(zclass, '.')
if dot == -1 {
// zmod remains ""
zname = zclass
} else {
zmod = zclass[:dot]
zname = zclass[dot+1:]
}
return pickle.Class{Module: zmod, Name: zname}
}
// xpyclass verifies and extracts py class from unpickled value.
//
// it normalizes py class that has just been decoded from a serialized ZODB
......
......@@ -67,8 +67,13 @@ func pySetState(obj PyStateful, objClass string, state *mem.Buf, jar *Connection
return obj.PySetState(pystate)
}
// TODO pyGetState
// pyGetState encodes obj as zodb/py serialized stream.
func pyGetState(obj PyStateful, objClass string) *mem.Buf {
pyclass := zpyclass(objClass)
pystate := obj.PyGetState()
data := encodePyData(pyclass, pystate)
return &mem.Buf{Data: data} // XXX -> better bufalloc (and in encodePyData)
}
// loadpy loads object specified by oid and decodes it as a ZODB Python object.
......
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