// PyStorage.cpp -- // $Id: PyStorage.cpp,v 1.27 2004/01/26 09:54:45 jcw Exp $ // This is part of MetaKit, the homepage is http://www.equi4.com/metakit/ // Copyright (C) 1999-2004 Gordon McMillan and Jean-Claude Wippler. // // Storage class implementation and main entry point #include #include "PyStorage.h" #include #include #include "PyView.h" #include "PyProperty.h" #include "PyRowRef.h" #include "mk4str.h" #include "mk4io.h" #if !defined _WIN32 #define __declspec(x) #endif static char mk4py_module_documentation[] = "This is the Python interface of the embeddable MetaKit database library.\n" "Example of use:\n" "\n" " import Mk4py\n" " mk = Mk4py\n" "\n" " s = mk.storage('demo.dat', 1)\n" " v = s.getas('people[first:S,last:S,shoesize:I]')\n" "\n" " v.append(first='John',last='Lennon',shoesize=44)\n" " v.append(first='Flash',last='Gordon',shoesize=42)\n" " s.commit()\n" "\n" " def dump(v):\n" " print len(v)\n" " for r in v: print r.first, r.last, r.shoesize\n" "\n" " v2 = v.sort(v.last)\n" " dump(v2)\n" " v[0].last = 'Doe'\n" " dump(v2)\n" " v2 = v.select(last='Doe')\n" " dump(v2)\n" " del s\n" "\n" "See the website area at http://www.equi4.com/metakit/ for full details.\n"; /////////////////////////////////////////////////////////////////////////////// class c4_PyStream : public c4_Stream { PyObject* _stream; public: c4_PyStream (PyObject* stream_); virtual int Read(void* buffer_, int length_); virtual bool Write(const void* buffer_, int length_); }; c4_PyStream::c4_PyStream (PyObject* stream_) : _stream (stream_) { } int c4_PyStream::Read(void* buffer_, int length_) { PyObject* o = PyObject_CallMethod(_stream, "read", "i", length_); int n = o != 0 ? PyString_Size(o) : 0; if (n > 0) memcpy(buffer_, PyString_AsString(o), n); return n; } bool c4_PyStream::Write(const void* buffer_, int length_) { PyObject_CallMethod(_stream, "write", "s#", buffer_, length_); return true; //!! how do we detect write errors here? } /////////////////////////////////////////////////////////////////////////////// // A "storage in a storage" strategy class for MetaKit class SiasStrategy : public c4_Strategy { c4_Storage& _storage; c4_View _view; c4_BytesProp _memo; int _row; public: SiasStrategy (c4_Storage& storage_, const c4_View& view_, const c4_BytesProp& memo_, int row_) : _storage (storage_), _view (view_), _memo (memo_), _row (row_) { // set up mapping if the memo itself is mapped in its entirety c4_Strategy& strat = storage_.Strategy(); if (strat._mapStart != 0) { c4_RowRef r = _view[_row]; c4_Bytes data = _memo(r).Access(0); const t4_byte* ptr = data.Contents(); if (data.Size() == _memo(r).GetSize() && strat._mapStart != 0 && ptr >= strat._mapStart && ptr - strat._mapStart < strat._dataSize) { _mapStart = ptr; _dataSize = data.Size(); } } } virtual ~SiasStrategy () { _view = c4_View(); _mapStart = 0; _dataSize = 0; } virtual int DataRead(t4_i32 pos_, void* buffer_, int length_) { int i = 0; while (i < length_) { c4_Bytes data = _memo(_view[_row]).Access(pos_ + i, length_ - i); int n = data.Size(); if (n <= 0) break; memcpy((char*) buffer_ + i, data.Contents(), n); i += n; } return i; } virtual void DataWrite(t4_i32 pos_, const void* buffer_, int length_) { c4_Bytes data (buffer_, length_); if (!_memo(_view[_row]).Modify(data, pos_)) ++_failure; } virtual void DataCommit(t4_i32 newSize_) { if (newSize_ > 0) _memo(_view[_row]).Modify(c4_Bytes (), newSize_); } virtual void ResetFileMapping() { _mapStart = 0; // never called, but just in case } }; /////////////////////////////////////////////////////////////////////////////// static char* autocommit__doc = "autocommit() -- turn on autocommit (i.e. commit when storage object is deleted)"; static PyObject* PyStorage_Autocommit(PyStorage *o, PyObject* _args) { try { o->AutoCommit(); Py_INCREF(Py_None); return Py_None; } catch (...) { return 0; } } static char* contents__doc = "contents() -- return view with one row, representing entire storage (internal use)"; static PyObject* PyStorage_Contents(PyStorage *o, PyObject* _args) { try { return new PyView(*o); } catch (...) { return 0; } } static char* description__doc = "description(name='') -- return a description of named view, or of entire storage"; static PyObject* PyStorage_Description(PyStorage *o, PyObject* _args) { try { PWOSequence args(_args); PWOString nm (""); if (args.len() > 0) nm = args[0]; const char *descr = o->Description(nm); if (descr) { PWOString rslt(descr); return rslt.disOwn(); } Fail(PyExc_KeyError, nm); } catch (...) { } return 0; /* satisfy compiler */ } static char* commit__doc = "commit(full=0) -- permanently commit data and structure changes to disk"; static PyObject* PyStorage_Commit(PyStorage *o, PyObject* _args) { try { PWOSequence args(_args); PWONumber flag (0); if (args.len() > 0) flag = args[0]; if (!o->Commit((int) flag != 0)) Fail(PyExc_IOError, "commit failed"); Py_INCREF(Py_None); return Py_None; } catch (...) { return 0; } } static char* rollback__doc = "rollback(full=0) -- revert data and structure as was last committed to disk"; static PyObject* PyStorage_Rollback(PyStorage *o, PyObject* _args) { try { PWOSequence args(_args); PWONumber flag (0); if (args.len() > 0) flag = args[0]; if (!o->Rollback((int) flag != 0)) Fail(PyExc_IOError, "rollback failed"); Py_INCREF(Py_None); return Py_None; } catch (...) { return 0; } } static char* aside__doc = "aside() -- revert data and structure as was last committed to disk"; static PyObject* PyStorage_Aside(PyStorage *o, PyObject* _args) { try { PWOSequence args(_args); if (!PyStorage_Check((PyObject*) args[0])) Fail(PyExc_TypeError, "First arg must be a storage"); c4_Storage& storage = *(PyStorage*)(PyObject*) args[0]; if (!o->SetAside(storage)) Fail(PyExc_IOError, "aside failed"); Py_INCREF(Py_None); return Py_None; } catch (...) { return 0; } } static char* view__doc = "view(viewname) -- return top-level view in storage, given its name"; static PyObject* PyStorage_View(PyStorage *o, PyObject* _args) { try { PWOSequence args(_args); PWOString nm(args[0]); return new PyView(o->View(nm)); } catch (...) { return 0; } } static char* getas__doc = "getas(description) -- return view, create / restructure as needed to match"; static PyObject* PyStorage_GetAs(PyStorage *o, PyObject* _args) { try { PWOSequence args(_args); PWOString descr(args[0]); return new PyView(o->GetAs(descr)); } catch (...) { return 0; } } static char* load__doc = "load(file) -- replace storage object contents from file (or any obj supporting read)"; static PyObject* PyStorage_load(PyStorage *o, PyObject* _args) { try { PWOSequence args(_args); if (args.len() != 1) Fail(PyExc_ValueError, "load requires a file-like object"); c4_PyStream stream (args[0]); o->LoadFrom(stream); Py_INCREF(Py_None); return Py_None; } catch (...) { return 0; } } static char* save__doc = "save(file) -- store storage object contents to file (or any obj supporting write)"; static PyObject* PyStorage_save(PyStorage *o, PyObject* _args) { try { PWOSequence args(_args); if (args.len() != 1) Fail(PyExc_ValueError, "save requires a file-like object"); c4_PyStream stream (args[0]); o->SaveTo(stream); Py_INCREF(Py_None); return Py_None; } catch (...) { return 0; } } static PyMethodDef StorageMethods[] = { {"getas", (PyCFunction)PyStorage_GetAs, METH_VARARGS, getas__doc}, {"view", (PyCFunction)PyStorage_View, METH_VARARGS, view__doc}, {"rollback", (PyCFunction)PyStorage_Rollback, METH_VARARGS, rollback__doc}, {"commit", (PyCFunction)PyStorage_Commit, METH_VARARGS, commit__doc}, {"aside", (PyCFunction)PyStorage_Aside, METH_VARARGS, aside__doc}, {"description", (PyCFunction)PyStorage_Description, METH_VARARGS, description__doc}, {"contents", (PyCFunction)PyStorage_Contents, METH_VARARGS, contents__doc}, {"autocommit", (PyCFunction)PyStorage_Autocommit, METH_VARARGS, autocommit__doc}, {"load", (PyCFunction)PyStorage_load, METH_VARARGS, load__doc}, {"save", (PyCFunction)PyStorage_save, METH_VARARGS, save__doc}, {0, 0, 0, 0} }; static void PyStorage_dealloc(PyStorage *o) { //o->~PyStorage(); delete o; } static int PyStorage_print(PyStorage *o, FILE *f, int) { fprintf(f, "", (long) o); return 0; } static PyObject* PyStorage_getattr(PyStorage *o, char *nm) { return Py_FindMethod(StorageMethods, o, nm); } PyTypeObject PyStoragetype = { PyObject_HEAD_INIT(&PyType_Type) 0, "PyStorage", sizeof(PyStorage), 0, (destructor)PyStorage_dealloc, /*tp_dealloc*/ (printfunc)PyStorage_print, /*tp_print*/ (getattrfunc)PyStorage_getattr, /*tp_getattr*/ 0, /*tp_setattr*/ (cmpfunc)0, /*tp_compare*/ (reprfunc)0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ }; static char* storage__doc = "storage() -- create a new in-memory storage (can load/save, but not commit/rollback)\n" "storage(file) -- attach a storage object to manage an already opened stdio file\n" "storage(filename, rw) -- open file, rw=0: r/o, rw=1: r/w, rw=2: extend"; static PyObject* PyStorage_new(PyObject* o, PyObject* _args) { try { PWOSequence args(_args); PyStorage* ps = 0; switch (args.len()) { case 0: ps = new PyStorage; break; case 1: if (!PyFile_Check((PyObject *)args[0])) { if (PyString_Check((PyObject *)args[0])) Fail(PyExc_TypeError, "rw parameter missing"); else Fail(PyExc_TypeError, "argument not an open file"); break; } ps = new PyStorage(*new c4_FileStrategy (PyFile_AsFile(args[0])), true); break; case 4: // Rrrrrr... { if (!PyStorage_Check((PyObject *)args[0])) Fail(PyExc_TypeError, "First arg must be a storage object"); c4_Storage& storage = *(PyStorage*)(PyObject *)args[0]; if (!PyView_Check((PyObject *)args[1])) Fail(PyExc_TypeError, "Second arg must be a view object"); c4_View& view = *(PyView*)(PyObject *)args[1]; if (!PyProperty_Check((PyObject *)args[2])) Fail(PyExc_TypeError, "Third arg must be a property object"); c4_BytesProp& prop = *(c4_BytesProp*)(c4_Property*)(PyProperty*)(PyObject*)args[2]; int row = PWONumber(args[3]); ps = new PyStorage(*new SiasStrategy (storage, view, prop, row), true); break; } case 2: { PWOString fnm(args[0]); if (PyInt_Check((PyObject* )args[1])) { PWONumber mode (args[1]); ps = new PyStorage(fnm, mode); if (!ps->Strategy().IsValid()) { delete ps; ps = 0; Fail(PyExc_IOError, "can't open storage file"); } } break; } default: Fail(PyExc_ValueError, "storage() takes at most 4 arguments"); } return ps; } catch (...) { return 0; } } class PyViewer : public c4_CustomViewer { PWOSequence _data; c4_View _template; c4_Row _tempRow; bool _byPos; public: PyViewer (const PWOSequence& data_, const c4_View& template_, bool byPos_); virtual ~PyViewer (); virtual c4_View GetTemplate(); virtual int GetSize(); virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); virtual bool SetItem(int row_, int col_, const c4_Bytes& buf_); }; PyViewer::PyViewer (const PWOSequence& data_, const c4_View& template_, bool byPos_) : _data (data_), _template (template_), _byPos (byPos_) { } PyViewer::~PyViewer () { } c4_View PyViewer::GetTemplate() { return _template; } int PyViewer::GetSize() { return _data.len(); } bool PyViewer::GetItem(int row_, int col_, c4_Bytes& buf_) { const c4_Property& prop = _template.NthProperty(col_); if (_byPos) { PWOSequence item (_data[row_]); PyRowRef::setFromPython(_tempRow, prop, item[col_]); return prop (_tempRow).GetData(buf_); } PyObject *item = _data[row_]; if (PyInstance_Check(item)) { PyObject* attr = PyObject_GetAttrString(item, (char *)prop.Name()); PyRowRef::setFromPython(_tempRow, prop, attr); return prop (_tempRow).GetData(buf_); } if (PyDict_Check(item)) { PyObject* attr = PyDict_GetItemString(item, (char *)prop.Name()); PyRowRef::setFromPython(_tempRow, prop, attr); return prop (_tempRow).GetData(buf_); } if (_template.NumProperties() == 1) { PyRowRef::setFromPython(_tempRow, prop, _data[row_]); return prop (_tempRow).GetData(buf_); } Fail(PyExc_ValueError, "Object has no usable attributes"); return false; // create a row with just this single property value // this detour handles dicts and objects, because makeRow does /* c4_Row one; PyView v (prop); // nasty, stack-based temp to get at makeRow v.makeRow(one, _data[row_]); return prop (one).GetData(buf_); */ } bool PyViewer::SetItem(int row_, int col_, const c4_Bytes& buf_) { const c4_Property& prop = _template.NthProperty(col_); c4_Row one; prop (one).SetData(buf_); PyRowRef r (one); // careful, stack-based temp PyObject* item = r.asPython(prop); if (_byPos) { PWOSequence item (_data[row_]); item[col_] = item; } else if (PyDict_Check((PyObject*) _data)) PyDict_SetItemString(_data, (char *)prop.Name(), item); else PyObject_SetAttrString(_data, (char *)prop.Name(), item); Py_DECREF(item); return true; } static PyObject* PyView_wrap(PyObject* o, PyObject* _args) { try { PWOSequence args(_args); PWOSequence seq(args[0]); PWOSequence types(args[1]); PWONumber usetuples(0); if (args.len() > 2) usetuples = args[2]; c4_View templ; for (int i = 0; i < types.len(); ++i) { const c4_Property& prop = *(PyProperty*)(PyObject*) types[i]; templ.AddProperty(prop); } c4_View cv = new PyViewer (seq, templ, (int) usetuples != 0); return new PyView (cv, 0, ROVIEWER); } catch (...) { return 0; } } static PyMethodDef Mk4Methods[] = { {"view", PyView_new, METH_VARARGS, "view() - create a new unattached view"}, {"storage", PyStorage_new, METH_VARARGS, storage__doc}, {"property", PyProperty_new, METH_VARARGS, "property(type, name) -- create a property of given type and name"}, {"wrap", PyView_wrap, METH_VARARGS, "wrap(seq,props,usetuples=0) - wrap a sequence as a read-only view"}, {0, 0, 0, 0} }; extern "C" __declspec(dllexport) /*#ifdef _DEBUG void initMk4py() { PyObject *m = Py_InitModule4("Mk4py", Mk4Methods, mk4py_module_documentation, 0,PYTHON_API_VERSION); PyObject_SetAttrString(m, "version", PyString_FromString("2.4.9.3")); PyObject_SetAttrString(m, "ViewType", (PyObject*)&PyViewtype); PyObject_SetAttrString(m, "ViewerType", (PyObject*)&PyViewertype); PyObject_SetAttrString(m, "ROViewerType", (PyObject*)&PyROViewertype); PyObject_SetAttrString(m, "RowRefType", (PyObject*)&PyRowReftype); PyObject_SetAttrString(m, "RORowRefType", (PyObject*)&PyRORowReftype); } #else*/ void initMk4py() { PyObject* m = Py_InitModule4("Mk4py", Mk4Methods, mk4py_module_documentation, 0, PYTHON_API_VERSION); PyObject_SetAttrString(m, "version", PyString_FromString("2.4.9.3")); PyObject_SetAttrString(m, "ViewType", (PyObject*)&PyViewtype); PyObject_SetAttrString(m, "ViewerType", (PyObject*)&PyViewertype); PyObject_SetAttrString(m, "ROViewerType", (PyObject*)&PyROViewertype); PyObject_SetAttrString(m, "RowRefType", (PyObject*)&PyRowReftype); PyObject_SetAttrString(m, "RORowRefType", (PyObject*)&PyRORowReftype); } //#endif