You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
501 lines
12 KiB
501 lines
12 KiB
// handler.cpp --
|
|
// $Id$
|
|
// This is part of Metakit, see http://www.equi4.com/metakit/
|
|
|
|
/** @file
|
|
* Handlers store data in column-wise format
|
|
*/
|
|
|
|
#include "header.h"
|
|
#include "handler.h"
|
|
#include "format.h"
|
|
#include "field.h"
|
|
#include "column.h"
|
|
#include "persist.h"
|
|
|
|
#if !q4_INLINE
|
|
#include "handler.inl"
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// c4_Handler
|
|
|
|
void c4_Handler::ClearBytes(c4_Bytes& buf_) const
|
|
{
|
|
static char zeros[8];
|
|
|
|
int n = f4_ClearFormat(Property().Type());
|
|
d4_assert(n <= sizeof zeros);
|
|
|
|
buf_ = c4_Bytes (zeros, n);
|
|
}
|
|
|
|
int c4_Handler::Compare(int index_, const c4_Bytes& buf_)
|
|
{
|
|
// create a copy for small data, since ints use a common _item buffer
|
|
c4_Bytes copy (buf_.Contents(), buf_.Size(), buf_.Size() <= 8);
|
|
|
|
c4_Bytes data;
|
|
GetBytes(index_, data);
|
|
|
|
return f4_CompareFormat(Property().Type(), data, copy);
|
|
}
|
|
|
|
void c4_Handler::Commit(c4_SaveContext&)
|
|
{
|
|
d4_assert(0);
|
|
}
|
|
|
|
void c4_Handler::OldDefine(char, c4_Persist&)
|
|
{
|
|
d4_assert(0);
|
|
}
|
|
|
|
// this is how the old "Get" was, keep it until no longer needed
|
|
void c4_Handler::GetBytes(int index_, c4_Bytes& buf_, bool copySmall_)
|
|
{
|
|
int n;
|
|
const void* p = Get(index_, n);
|
|
buf_ = c4_Bytes (p, n, copySmall_ && n <= 8);
|
|
}
|
|
|
|
void c4_Handler::Move(int from_, int to_)
|
|
{
|
|
if (from_ != to_) {
|
|
c4_Bytes data;
|
|
GetBytes(from_, data);
|
|
|
|
Remove(from_, 1);
|
|
|
|
if (to_ > from_)
|
|
--to_;
|
|
|
|
Insert(to_, data, 1);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// c4_HandlerSeq
|
|
|
|
c4_HandlerSeq::c4_HandlerSeq (c4_Persist* persist_)
|
|
: _persist (persist_), _field (0), _tqparent (0), _numRows (0)
|
|
{
|
|
}
|
|
|
|
c4_HandlerSeq::c4_HandlerSeq (c4_HandlerSeq& owner_, c4_Handler* handler_)
|
|
: _persist (owner_.Persist()), _field (owner_.FindField(handler_)),
|
|
_tqparent (&owner_), _numRows (0)
|
|
{
|
|
for (int i = 0; i < NumFields(); ++i) {
|
|
c4_Field& field = Field(i);
|
|
c4_Property prop (field.Type(), field.Name());
|
|
|
|
d4_dbgdef(int n =)
|
|
AddHandler(f4_CreateFormat(prop, *this));
|
|
d4_assert(n == i);
|
|
}
|
|
}
|
|
|
|
c4_HandlerSeq::~c4_HandlerSeq ()
|
|
{
|
|
const bool rootLevel = _tqparent == this;
|
|
c4_Persist* pers = _persist;
|
|
|
|
if (rootLevel && pers != 0)
|
|
pers->DoAutoCommit();
|
|
|
|
DetachFromParent();
|
|
DetachFromStorage(true);
|
|
|
|
for (int i = 0; i < NumHandlers(); ++i)
|
|
delete & NthHandler(i);
|
|
_handlers.SetSize(0);
|
|
|
|
ClearCache();
|
|
|
|
if (rootLevel) {
|
|
delete _field;
|
|
|
|
d4_assert(pers != 0);
|
|
delete pers;
|
|
}
|
|
}
|
|
|
|
c4_Persist* c4_HandlerSeq::Persist() const
|
|
{
|
|
return _persist;
|
|
}
|
|
|
|
void c4_HandlerSeq::DefineRoot()
|
|
{
|
|
d4_assert(_field == 0);
|
|
d4_assert(_tqparent == 0);
|
|
|
|
SetNumRows(1);
|
|
|
|
const char* desc = "[]";
|
|
_field = d4_new c4_Field (desc);
|
|
d4_assert(!*desc);
|
|
|
|
_tqparent = this;
|
|
}
|
|
|
|
c4_Handler* c4_HandlerSeq::CreateHandler(const c4_Property& prop_)
|
|
{
|
|
return f4_CreateFormat(prop_, *this);
|
|
}
|
|
|
|
c4_Field& c4_HandlerSeq::Definition() const
|
|
{
|
|
d4_assert(_field != 0);
|
|
|
|
return *_field;
|
|
}
|
|
|
|
void c4_HandlerSeq::DetachFromParent()
|
|
{
|
|
if (_field != 0) {
|
|
const char* desc = "[]";
|
|
c4_Field f (desc);
|
|
d4_assert(!*desc);
|
|
Restructure(f, false);
|
|
_field = 0;
|
|
}
|
|
|
|
_tqparent = 0;
|
|
}
|
|
|
|
void c4_HandlerSeq::DetachFromStorage(bool full_)
|
|
{
|
|
if (_persist != 0) {
|
|
int limit = full_ ? 0 : NumFields();
|
|
|
|
// get rid of all handlers which might do I/O
|
|
for (int c = NumHandlers(); --c >= 0; ) {
|
|
c4_Handler& h = NthHandler(c);
|
|
|
|
// all nested fields are detached recursively
|
|
if (IsNested(c))
|
|
for (int r = 0; r < NumRows(); ++r)
|
|
if (h.HasSubview(r))
|
|
SubEntry(c, r).DetachFromStorage(full_);
|
|
|
|
if (c >= limit) {
|
|
if (h.IsPersistent()) {
|
|
delete &h;
|
|
_handlers.RemoveAt(c);
|
|
ClearCache();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (full_) {
|
|
//UnmappedAll();
|
|
_persist = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void c4_HandlerSeq::DetermineSpaceUsage()
|
|
{
|
|
for (int c = 0; c < NumFields(); ++c)
|
|
if (IsNested(c)) {
|
|
c4_Handler& h = NthHandler(c);
|
|
for (int r = 0; r < NumRows(); ++r)
|
|
if (h.HasSubview(r))
|
|
SubEntry(c, r).DetermineSpaceUsage();
|
|
}
|
|
}
|
|
|
|
void c4_HandlerSeq::SetNumRows(int numRows_)
|
|
{
|
|
d4_assert(_numRows >= 0);
|
|
|
|
_numRows = numRows_;
|
|
}
|
|
|
|
int c4_HandlerSeq::AddHandler(c4_Handler* handler_)
|
|
{
|
|
d4_assert(handler_ != 0);
|
|
|
|
return _handlers.Add(handler_);
|
|
}
|
|
|
|
const char* c4_HandlerSeq::Description()
|
|
{
|
|
// 19-01-2003: avoid too dense code, since Sun CC seems to choke on it
|
|
//return _field != 0 ? UseTempBuffer(Definition().DescribeSubFields()) : 0;
|
|
if (_field == 0)
|
|
return 0;
|
|
c4_String s = _field->DescribeSubFields();
|
|
return UseTempBuffer(s);
|
|
}
|
|
|
|
void c4_HandlerSeq::Restructure(c4_Field& field_, bool remove_)
|
|
{
|
|
//d4_assert(_field != 0);
|
|
|
|
// all nested fields must be set up, before we shuffle them around
|
|
for (int k = 0; k < NumHandlers(); ++k)
|
|
if (IsNested(k)) {
|
|
c4_Handler& h = NthHandler(k);
|
|
for (int n = 0; n < NumRows(); ++n)
|
|
if (h.HasSubview(n))
|
|
SubEntry(k, n);
|
|
}
|
|
|
|
for (int i = 0; i < field_.NumSubFields(); ++i) {
|
|
c4_Field& nf = field_.SubField(i);
|
|
c4_Property prop (nf.Type(), nf.Name());
|
|
|
|
int n = PropIndex(prop.GetId());
|
|
if (n == i)
|
|
continue;
|
|
|
|
if (n < 0) {
|
|
_handlers.InsertAt(i, f4_CreateFormat(prop, *this));
|
|
NthHandler(i).Define(NumRows(), 0);
|
|
} else {
|
|
// move the handler to the front
|
|
d4_assert(n > i);
|
|
_handlers.InsertAt(i, _handlers.GetAt(n));
|
|
_handlers.RemoveAt(++n);
|
|
}
|
|
|
|
ClearCache(); // we mess with the order of handler, keep clearing it
|
|
|
|
d4_assert(PropIndex(prop.GetId()) == i);
|
|
}
|
|
|
|
c4_Field* ofld = _field;
|
|
// special case if we're "restructuring a view out of persistence", see below
|
|
|
|
_field = remove_ ? 0 : &field_;
|
|
|
|
// let handler do additional init once all have been prepared
|
|
//for (int n = 0; n < NumHandlers(); ++n)
|
|
// NthHandler(n).Define(NumRows(), 0);
|
|
|
|
const char* desc = "[]";
|
|
c4_Field temp (desc);
|
|
|
|
// all nested fields are restructured recursively
|
|
for (int j = 0; j < NumHandlers(); ++j)
|
|
if (IsNested(j)) {
|
|
c4_Handler& h = NthHandler(j);
|
|
for (int n = 0; n < NumRows(); ++n)
|
|
if (h.HasSubview(n)) {
|
|
c4_HandlerSeq& seq = SubEntry(j, n);
|
|
if (j < NumFields())
|
|
seq.Restructure(field_.SubField(j), false);
|
|
else if (seq._field != 0)
|
|
seq.Restructure(temp, true);
|
|
}
|
|
}
|
|
|
|
if (_tqparent == this)
|
|
delete ofld; // the root table owns its field structure tree
|
|
}
|
|
|
|
int c4_HandlerSeq::NumFields() const
|
|
{
|
|
return _field != 0 ? _field->NumSubFields() : 0;
|
|
}
|
|
|
|
char c4_HandlerSeq::ColumnType(int index_) const
|
|
{
|
|
return NthHandler(index_).Property().Type();
|
|
}
|
|
|
|
bool c4_HandlerSeq::IsNested(int index_) const
|
|
{
|
|
return ColumnType(index_) == 'V';
|
|
}
|
|
|
|
c4_Field& c4_HandlerSeq::Field(int index_) const
|
|
{
|
|
d4_assert(_field != 0);
|
|
|
|
return _field->SubField(index_);
|
|
}
|
|
|
|
void c4_HandlerSeq::Prepare(const t4_byte** ptr_, bool selfDesc_)
|
|
{
|
|
if (ptr_ != 0) {
|
|
d4_dbgdef(t4_i32 sias =)
|
|
c4_Column::PullValue(*ptr_);
|
|
d4_assert(sias == 0); // not yet
|
|
|
|
if (selfDesc_) {
|
|
t4_i32 n = c4_Column::PullValue(*ptr_);
|
|
if (n > 0) {
|
|
c4_String s = "[" + c4_String ((const char*) *ptr_, n) + "]";
|
|
const char* desc = s;
|
|
|
|
c4_Field* f = d4_new c4_Field (desc);
|
|
d4_assert(!*desc);
|
|
|
|
Restructure(*f, false);
|
|
*ptr_ += n;
|
|
}
|
|
}
|
|
|
|
int rows = (int) c4_Column::PullValue(*ptr_);
|
|
if (rows > 0) {
|
|
SetNumRows(rows);
|
|
|
|
for (int i = 0; i < NumFields(); ++i)
|
|
NthHandler(i).Define(rows, ptr_);
|
|
}
|
|
}
|
|
}
|
|
|
|
void c4_HandlerSeq::OldPrepare()
|
|
{
|
|
d4_assert(_persist != 0);
|
|
|
|
for (int i = 0; i < NumFields(); ++i) {
|
|
char origType = _field->SubField(i).OrigType();
|
|
NthHandler(i).OldDefine(origType, *_persist);
|
|
}
|
|
}
|
|
|
|
void c4_HandlerSeq::FlipAllBytes()
|
|
{
|
|
for (int i = 0; i < NumHandlers(); ++i) {
|
|
c4_Handler& h = NthHandler(i);
|
|
h.FlipBytes();
|
|
}
|
|
}
|
|
|
|
// New 19990903: swap rows in tables without touching the memo fields
|
|
// or subviews on disk. This is used by the new c4_View::RelocateRows.
|
|
|
|
void c4_HandlerSeq::ExchangeEntries(int srcPos_, c4_HandlerSeq& dst_, int dstPos_)
|
|
{
|
|
d4_assert(NumHandlers() == dst_.NumHandlers());
|
|
|
|
c4_Bytes t1, t2;
|
|
|
|
for (int col = 0; col < NumHandlers(); ++col)
|
|
{
|
|
if (IsNested(col))
|
|
{
|
|
d4_assert(dst_.IsNested(col));
|
|
|
|
int n;
|
|
c4_HandlerSeq** e1 = (c4_HandlerSeq**) NthHandler(col).Get(srcPos_, n);
|
|
c4_HandlerSeq** e2 = (c4_HandlerSeq**) dst_.NthHandler(col).Get(dstPos_, n);
|
|
d4_assert(*e1 != 0 && *e2 != 0);
|
|
|
|
// swap the two entries
|
|
c4_HandlerSeq* e = *e1;
|
|
*e1 = *e2;
|
|
*e2 = e;
|
|
|
|
// shorthand, *after* the swap
|
|
c4_HandlerSeq& t1 = SubEntry(col, srcPos_);
|
|
c4_HandlerSeq& t2 = dst_.SubEntry(col, dstPos_);
|
|
|
|
// adjust the parents
|
|
t1._tqparent = this;
|
|
t2._tqparent = &dst_;
|
|
|
|
// reattach the proper field structures
|
|
t1.Restructure(Field(col), false);
|
|
t2.Restructure(dst_.Field(col), false);
|
|
}
|
|
else
|
|
{
|
|
d4_assert(ColumnType(col) == dst_.ColumnType(col));
|
|
|
|
c4_Handler& h1 = NthHandler(col);
|
|
c4_Handler& h2 = dst_.NthHandler(col);
|
|
|
|
#if 0 // memo's are 'B' now, but tricky to deal with, so copy them for now
|
|
if (ColumnType(col) == 'M')
|
|
{
|
|
c4_Column* c1 = h1.GetNthMemoCol(srcPos_, true);
|
|
c4_Column* c2 = h2.GetNthMemoCol(dstPos_, true);
|
|
|
|
t4_i32 p1 = c1 ? c1->Position() : 0;
|
|
t4_i32 p2 = c2 ? c2->Position() : 0;
|
|
|
|
t4_i32 s1 = c1 ? c1->ColSize() : 0;
|
|
t4_i32 s2 = c2 ? c2->ColSize() : 0;
|
|
|
|
d4_assert(false); // broken
|
|
//!h1.SetNthMemoPos(srcPos_, p2, s2, c2);
|
|
//!h2.SetNthMemoPos(dstPos_, p1, s1, c1);
|
|
}
|
|
#endif
|
|
// 10-4-2002: Need to use copies in case either item points into
|
|
// memory that could move, or if access re-uses a shared buffer.
|
|
// The special cases are sufficiently tricky that it's NOT being
|
|
// optimized for now (temp bufs, mmap ptrs, c4_Bytes buffering).
|
|
|
|
int n1, n2;
|
|
const void* p1 = h1.Get(srcPos_, n1);
|
|
const void* p2 = h2.Get(dstPos_, n2);
|
|
|
|
c4_Bytes t1 (p1, n1, true);
|
|
c4_Bytes t2 (p2, n2, true);
|
|
|
|
h1.Set(srcPos_, t2);
|
|
h2.Set(dstPos_, t1);
|
|
}
|
|
}
|
|
}
|
|
|
|
c4_HandlerSeq& c4_HandlerSeq::SubEntry(int col_, int row_) const
|
|
{
|
|
d4_assert(IsNested(col_));
|
|
|
|
c4_Bytes temp;
|
|
NthHandler(col_).GetBytes(row_, temp);
|
|
|
|
d4_assert(temp.Size() == sizeof (c4_HandlerSeq**));
|
|
c4_HandlerSeq** p = (c4_HandlerSeq**) temp.Contents(); // loses const
|
|
|
|
d4_assert(p != 0 && *p != 0);
|
|
|
|
return **p;
|
|
}
|
|
|
|
c4_Field* c4_HandlerSeq::FindField(const c4_Handler* handler_)
|
|
{
|
|
for (int i = 0; i < NumFields(); ++i)
|
|
if (handler_ == &NthHandler(i))
|
|
return &Field(i);
|
|
return 0;
|
|
}
|
|
|
|
void c4_HandlerSeq::UnmappedAll()
|
|
{
|
|
for (int i = 0; i < NumFields(); ++i)
|
|
NthHandler(i).Unmapped();
|
|
}
|
|
|
|
// construct meta view from a pre-parsed field tree structure
|
|
// this will one day be converted to directly parse the description string
|
|
void c4_HandlerSeq::BuildMeta(int parent_, int colnum_, c4_View& meta_,
|
|
const c4_Field& field_)
|
|
{
|
|
c4_IntProp pP ("P"), pC ("C");
|
|
c4_ViewProp pF ("F");
|
|
c4_StringProp pN ("N"), pT ("T");
|
|
|
|
int n = meta_.Add(pP [parent_] + pC [colnum_]);
|
|
c4_View fields = pF (meta_[n]);
|
|
|
|
for (int i = 0; i < field_.NumSubFields(); ++i) {
|
|
const c4_Field& f = field_.SubField(i);
|
|
char type = f.Type();
|
|
fields.Add(pN [f.Name()] + pT [c4_String (&type, 1)]);
|
|
if (type == 'V')
|
|
BuildMeta(n, i, meta_, f);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|