1// store.cpp --
2// $Id: store.cpp 1230 2007-03-09 15:58:53Z jcw $
3// This is part of Metakit, the homepage is http://www.equi4.com/metakit.html
4
5/** @file
6 * Storage management and several other loose ends
7 */
8
9#include "header.h"
10#include "handler.h"  // 19990906
11#include "store.h"
12#include "field.h"
13#include "persist.h"
14#include "format.h"   // 19990906
15
16#include "mk4io.h"    // 19991104
17
18#if !q4_INLINE
19#include "store.inl"
20#endif
21
22/////////////////////////////////////////////////////////////////////////////
23
24c4_Dependencies::c4_Dependencies() {
25  _refs.SetSize(0, 3); // a little optimization
26}
27
28c4_Dependencies::~c4_Dependencies(){}
29
30void c4_Dependencies::Add(c4_Sequence *seq_) {
31  for (int i = 0; i < _refs.GetSize(); ++i)
32    d4_assert(_refs.GetAt(i) != seq_);
33
34  _refs.Add(seq_);
35}
36
37bool c4_Dependencies::Remove(c4_Sequence *seq_) {
38  int n = _refs.GetSize() - 1;
39  d4_assert(n >= 0);
40
41  for (int i = 0; i <= n; ++i)
42  if (_refs.GetAt(i) == seq_) {
43    _refs.SetAt(i, _refs.GetAt(n));
44    _refs.SetSize(n);
45    return n > 0;
46  }
47
48  d4_assert(0); // dependency not found
49  return true;
50}
51
52/////////////////////////////////////////////////////////////////////////////
53
54c4_Notifier::~c4_Notifier() {
55  if (_type > kNone && _origin->GetDependencies()) {
56    c4_PtrArray &refs = _origin->GetDependencies()->_refs;
57
58    for (int i = 0; i < refs.GetSize(); ++i) {
59      c4_Sequence *seq = (c4_Sequence*)refs.GetAt(i);
60      d4_assert(seq != 0);
61
62      seq->PostChange(*this);
63
64      if (_chain && _chain->_origin == seq) {
65        c4_Notifier *next = _chain->_next;
66        _chain->_next = 0;
67
68        delete _chain;
69
70        _chain = next;
71      }
72    }
73  }
74
75  d4_assert(!_chain);
76  d4_assert(!_next);
77}
78
79void c4_Notifier::StartSetAt(int index_, c4_Cursor &cursor_) {
80  _type = kSetAt;
81  _index = index_;
82  _cursor = &cursor_;
83
84  Notify();
85}
86
87void c4_Notifier::StartInsertAt(int i_, c4_Cursor &cursor_, int n_) {
88  _type = kInsertAt;
89  _index = i_;
90  _cursor = &cursor_;
91  _count = n_;
92
93  Notify();
94}
95
96void c4_Notifier::StartRemoveAt(int index_, int count_) {
97  _type = kRemoveAt;
98  _index = index_;
99  _count = count_;
100
101  Notify();
102}
103
104void c4_Notifier::StartMove(int from_, int to_) {
105  _type = kMove;
106  _index = from_;
107  _count = to_;
108
109  Notify();
110}
111
112void c4_Notifier::StartSet(int i_, int propId_, const c4_Bytes &buf_) {
113  _type = kSet;
114  _index = i_;
115  _propId = propId_;
116  _bytes = &buf_;
117
118  Notify();
119}
120
121void c4_Notifier::Notify() {
122  d4_assert(_origin->GetDependencies() != 0);
123  c4_PtrArray &refs = _origin->GetDependencies()->_refs;
124
125  int n = refs.GetSize();
126  d4_assert(n > 0);
127
128  c4_Notifier **rover = &_chain;
129
130  for (int i = 0; i < n; ++i) {
131    c4_Sequence *seq = (c4_Sequence*)refs.GetAt(i);
132    d4_assert(seq != 0);
133
134    c4_Notifier *ptr = seq->PreChange(*this);
135    if (ptr) {
136      d4_assert(ptr->_origin == seq);
137
138      d4_assert(! *rover);
139      *rover = ptr;
140      rover = &ptr->_next;
141    }
142  }
143}
144
145/////////////////////////////////////////////////////////////////////////////
146
147/** @class c4_Storage
148 *
149 *  Manager for persistent storage of view structures.
150 *
151 *  The storage class uses a view, with additional functionality to be able
152 *  to store and reload the data it contains (including nested subviews).
153 *
154 *  By default, data is loaded on demand, i.e. whenever data which has
155 *  not yet been referenced is used for the first time.  Loading is limited
156 *  to the lifetime of this storage object, since the storage object carries
157 *  the file descriptor with it that is needed to access the data file.
158 *
159 *  To save changes, call the Commit member.  This is the only time
160 *  that data is written to file - when using a read-only file simply avoid
161 *  calling Commit.
162 *
163 *  The LoadFromStream and SaveToStream members can be used to
164 *  serialize the contents of this storage row using only sequential I/O
165 *  (no seeking, only read or write calls).
166 *
167 *  The data storage mechanism implementation provides fail-safe operation:
168 *  if anything prevents Commit from completing its task, the last
169 *  succesfully committed version of the saved data will be recovered on
170 *  the next open. This also includes changes made to the table structure.
171 *
172 *  The following code creates a view with 1 row and stores it on file:
173 * @code
174 *    c4_StringProp pName ("Name");
175 *    c4_IntProp pAge ("Age");
176 *
177 *    c4_Storage storage ("myfile.dat", true);
178 *    c4_View myView = storage.GetAs("Musicians[Name:S,Age:I]");
179 *
180 *    myView.Add(pName ["John Williams"] + pAge [43]);
181 *
182 *    storage.Commit();
183 * @endcode
184 */
185
186c4_Storage::c4_Storage() {
187  // changed to r/o, now that commits don't crash on it anymore
188  Initialize(*d4_new c4_Strategy, true, 0);
189}
190
191c4_Storage::c4_Storage(c4_Strategy &strategy_, bool owned_, int mode_) {
192  Initialize(strategy_, owned_, mode_);
193  Persist()->LoadAll();
194}
195
196c4_Storage::c4_Storage(const char *fname_, int mode_) {
197  c4_FileStrategy *strat = d4_new c4_FileStrategy;
198  strat->DataOpen(fname_, mode_);
199
200  Initialize(*strat, true, mode_);
201  if (strat->IsValid())
202    Persist()->LoadAll();
203}
204
205c4_Storage::c4_Storage(const c4_View &root_) {
206  if (root_.Persist() != 0)
207  // only restore if view was indeed persistent
208    *(c4_View*)this = root_;
209  else
210  // if this was not possible, start with a fresh empty storage
211    Initialize(*d4_new c4_Strategy, true, 0);
212}
213
214c4_Storage::~c4_Storage() {
215  // cannot unmap here, because there may still be an autocommit pending
216  //((c4_HandlerSeq*) _seq)->UnmapAll();
217}
218
219void c4_Storage::Initialize(c4_Strategy &strategy_, bool owned_, int mode_) {
220  c4_Persist *pers = d4_new c4_Persist(strategy_, owned_, mode_);
221  c4_HandlerSeq *seq = d4_new c4_HandlerSeq(pers);
222  seq->DefineRoot();
223  *(c4_View*)this = seq;
224  pers->SetRoot(seq);
225}
226
227/// Get or set a named view in this storage object
228c4_ViewRef c4_Storage::View(const char *name_) {
229  /*
230  The easy solution would seem to be:
231
232  c4_ViewProp prop (name_);
233  return prop (Contents());
234
235  But this does not work, because the return value would point to
236  an object allocated on the stack.
237
238  Instead, make sure the view *has* such a property, and use the
239  one inside the c4_Handler for it (since this will stay around).
240   */
241
242  //  int n = _root->PropIndex(c4_ViewProp (name_));
243
244  c4_ViewProp prop(name_);
245  int n = AddProperty(prop);
246  d4_assert(n >= 0);
247
248  // the following is an expression of the form "property (rowref)"
249  return NthProperty(n)(GetAt(0));
250}
251
252/// Get a named view, redefining it to match the given structure
253c4_View c4_Storage::GetAs(const char *description_) {
254  d4_assert(description_ != 0);
255
256  // Dec 2001: now that GetAs is being used so much more frequently,
257  // add a quick check to see whether restructuring is needed at all
258  const char *q = strchr(description_, '[');
259  if (q != 0) {
260    c4_String vname(description_, q - description_);
261    const char *d = Description(vname);
262    if (d != 0) {
263      c4_String desc(d);
264      if (("[" + desc + "]").CompareNoCase(q) == 0)
265        return View(vname);
266    }
267  }
268
269  c4_Field *field = d4_new c4_Field(description_);
270  d4_assert(field != 0);
271
272  d4_assert(! *description_);
273
274  c4_String name = field->Name();
275  d4_assert(!name.IsEmpty());
276
277  c4_Field &curr = Persist()->Root().Definition();
278
279  c4_String newField = "," + field->Description();
280  bool keep = newField.Find('[') >= 0;
281
282  c4_String newDef;
283
284  // go through all subfields
285  for (int i = 0; i < curr.NumSubFields(); ++i) {
286    c4_Field &of = curr.SubField(i);
287    if (of.Name().CompareNoCase(name) == 0) {
288      if (field->IsRepeating())
289        newDef += newField;
290      // else new is not a repeating entry, so drop this entire field
291
292      newField.Empty(); // don't append it later on
293      continue;
294    }
295
296    newDef += "," + of.Description(); // keep original field
297  }
298
299  if (keep)
300  // added 19990824 ignore if deletion
301    newDef += newField;
302  // appends new definition if not found earlier
303
304  delete field;
305
306  const char *p = newDef;
307  SetStructure(*p ? ++p: p); // skip the leading comma
308
309  if (!keep)
310  // 19990916: avoid adding an empty view again
311    return c4_View();
312
313  return View(name);
314}
315
316/// Define the complete view structure of the storage
317void c4_Storage::SetStructure(const char *description_) {
318  d4_assert(description_ != 0);
319
320  if (description_ != Description()) {
321    c4_String s = "[" + c4_String(description_) + "]";
322    description_ = s;
323
324    c4_Field *field = d4_new c4_Field(description_);
325    d4_assert(! *description_);
326
327    d4_assert(field != 0);
328    Persist()->Root().Restructure(*field, false);
329  }
330}
331
332/// Return the strategy object associated with this storage
333c4_Strategy &c4_Storage::Strategy()const {
334  return Persist()->Strategy();
335}
336
337/// Return a description of the view structure (default is all)
338const char *c4_Storage::Description(const char *name_) {
339  if (name_ == 0 ||  *name_ == 0)
340    return c4_View::Description();
341
342  c4_View v = View(name_);
343  return v.Description();
344}
345
346/// Define the storage to use for differential commits
347bool c4_Storage::SetAside(c4_Storage &aside_) {
348  c4_Persist *pers = Persist();
349  bool f = pers->SetAside(aside_);
350  // adjust our copy when the root view has been replaced
351  *(c4_View*)this = &pers->Root();
352  return f;
353}
354
355/// Return storage used for differential commits, or null
356c4_Storage *c4_Storage::GetAside()const {
357  return Persist()->GetAside();
358}
359
360/// Flush pending changes to file right now
361bool c4_Storage::Commit(bool full_) {
362  return Strategy().IsValid() && Persist()->Commit(full_);
363}
364
365/** (Re)initialize for on-demand loading
366 *
367 *  Calling Rollback will cancel all uncommitted changes.
368 */
369bool c4_Storage::Rollback(bool full_) {
370  c4_Persist *pers = Persist();
371  bool f = Strategy().IsValid() && pers->Rollback(full_);
372  // adjust our copy when the root view has been replaced
373  *(c4_View*)this = &pers->Root();
374  return f;
375}
376
377/// Set storage up to always call Commit in the destructor
378bool c4_Storage::AutoCommit(bool flag_) {
379  return Persist()->AutoCommit(flag_);
380}
381
382/// Load contents from the specified input stream
383bool c4_Storage::LoadFrom(c4_Stream &stream_) {
384  c4_HandlerSeq *newRoot = c4_Persist::Load(&stream_);
385  if (newRoot == 0)
386    return false;
387
388  // fix commit-after-load bug, by using a full view copy
389  // this is inefficient, but avoids mapping/strategy problems
390  c4_View temp(newRoot);
391
392  SetSize(0);
393  SetStructure(temp.Description());
394  InsertAt(0, temp);
395
396  return true;
397}
398
399/// Save contents to the specified output stream
400void c4_Storage::SaveTo(c4_Stream &stream_) {
401  c4_Persist::Save(&stream_, Persist()->Root());
402}
403
404t4_i32 c4_Storage::FreeSpace(t4_i32 *bytes_) {
405  return Persist()->FreeBytes(bytes_);
406}
407
408/////////////////////////////////////////////////////////////////////////////
409
410c4_DerivedSeq::c4_DerivedSeq(c4_Sequence &seq_): _seq(seq_) {
411  _seq.Attach(this);
412}
413
414c4_DerivedSeq::~c4_DerivedSeq() {
415  _seq.Detach(this);
416}
417
418int c4_DerivedSeq::RemapIndex(int index_, const c4_Sequence *seq_)const {
419  return seq_ == this ? index_ : _seq.RemapIndex(index_, seq_);
420}
421
422int c4_DerivedSeq::NumRows()const {
423  return _seq.NumRows();
424}
425
426int c4_DerivedSeq::NumHandlers()const {
427  return _seq.NumHandlers();
428}
429
430c4_Handler &c4_DerivedSeq::NthHandler(int colNum_)const {
431  return _seq.NthHandler(colNum_);
432}
433
434const c4_Sequence *c4_DerivedSeq::HandlerContext(int colNum_)const {
435  return _seq.HandlerContext(colNum_);
436}
437
438int c4_DerivedSeq::AddHandler(c4_Handler *handler_) {
439  return _seq.AddHandler(handler_);
440}
441
442c4_Handler *c4_DerivedSeq::CreateHandler(const c4_Property &prop_) {
443  return _seq.CreateHandler(prop_);
444}
445
446void c4_DerivedSeq::SetNumRows(int size_) {
447  _seq.SetNumRows(size_);
448}
449
450c4_Notifier *c4_DerivedSeq::PreChange(c4_Notifier &nf_) {
451  if (!GetDependencies())
452    return 0;
453
454  c4_Notifier *chg = d4_new c4_Notifier(this);
455
456  switch (nf_._type) {
457    case c4_Notifier::kSetAt: chg->StartSetAt(nf_._index,  *nf_._cursor);
458    break;
459
460    case c4_Notifier::kSet: chg->StartSet(nf_._index, nf_._propId,  *nf_._bytes)
461      ;
462    break;
463
464    case c4_Notifier::kInsertAt: chg->StartInsertAt(nf_._index,  *nf_._cursor,
465      nf_._count);
466    break;
467
468    case c4_Notifier::kRemoveAt: chg->StartRemoveAt(nf_._index, nf_._count);
469    break;
470
471    case c4_Notifier::kMove: chg->StartMove(nf_._index, nf_._count);
472    break;
473  }
474
475  return chg;
476}
477
478/////////////////////////////////////////////////////////////////////////////
479
480c4_StreamStrategy::c4_StreamStrategy(t4_i32 buflen_): _stream(0), _buffer
481  (d4_new t4_byte[buflen_]), _buflen(buflen_), _position(0) {
482  _mapStart = _buffer;
483  _dataSize = buflen_;
484}
485
486c4_StreamStrategy::c4_StreamStrategy(c4_Stream *stream_): _stream(stream_),
487  _buffer(0), _buflen(0), _position(0){}
488
489c4_StreamStrategy::~c4_StreamStrategy() {
490  _mapStart = 0;
491  _dataSize = 0;
492
493  if (_buffer != 0)
494    delete [] _buffer;
495}
496
497bool c4_StreamStrategy::IsValid()const {
498  return true;
499}
500
501int c4_StreamStrategy::DataRead(t4_i32 pos_, void *buffer_, int length_) {
502  if (_buffer != 0) {
503    d4_assert(pos_ <= _buflen);
504    _position = pos_ + _baseOffset;
505
506    if (length_ > _buflen - _position)
507      length_ = _buflen - _position;
508    if (length_ > 0)
509      memcpy(buffer_, _buffer + _position, length_);
510  } else {
511    d4_assert(_position == pos_ + _baseOffset);
512    length_ = _stream != 0 ? _stream->Read(buffer_, length_): 0;
513  }
514
515  _position += length_;
516  return length_;
517}
518
519void c4_StreamStrategy::DataWrite(t4_i32 pos_, const void *buffer_, int length_)
520  {
521  if (_buffer != 0) {
522    d4_assert(pos_ <= _buflen);
523    _position = pos_ + _baseOffset;
524
525    int n = length_;
526    if (n > _buflen - _position)
527      n = _buflen - _position;
528    if (n > 0)
529      memcpy(_buffer + _position, buffer_, n);
530  } else {
531    d4_assert(_position == pos_ + _baseOffset);
532    if (_stream != 0 && !_stream->Write(buffer_, length_))
533      ++_failure;
534  }
535
536  _position += length_;
537}
538
539t4_i32 c4_StreamStrategy::FileSize() {
540  return _position;
541}
542
543/////////////////////////////////////////////////////////////////////////////
544