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