/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef _MDB_ #include "mdb.h" #endif #ifndef _MORK_ #include "mork.h" #endif #ifndef _MORKNODE_ #include "morkNode.h" #endif #ifndef _MORKMAP_ #include "morkMap.h" #endif #ifndef _MORKENV_ #include "morkEnv.h" #endif #ifndef _MORKTABLE_ #include "morkTable.h" #endif #ifndef _MORKSTORE_ #include "morkStore.h" #endif #ifndef _MORKROWSPACE_ #include "morkRowSpace.h" #endif #ifndef _MORKARRAY_ #include "morkArray.h" #endif #ifndef _MORKROW_ #include "morkRow.h" #endif #ifndef _MORKTABLEROWCURSOR_ #include "morkTableRowCursor.h" #endif #ifndef _MORKROWOBJECT_ #include "morkRowObject.h" #endif //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 // ````` ````` ````` ````` ````` // { ===== begin morkNode interface ===== /*public virtual*/ void morkTable::CloseMorkNode(morkEnv* ev) /*i*/ // CloseTable() only if open { if ( this->IsOpenNode() ) { morkObject::CloseMorkNode(ev); // give base class a chance. this->MarkClosing(); this->CloseTable(ev); this->MarkShut(); } } /*public virtual*/ morkTable::~morkTable() /*i*/ // assert CloseTable() executed earlier { CloseMorkNode(mMorkEnv); MORK_ASSERT(this->IsShutNode()); MORK_ASSERT(mTable_Store==0); MORK_ASSERT(mTable_RowSpace==0); } /*public non-poly*/ morkTable::morkTable(morkEnv* ev, /*i*/ const morkUsage& inUsage, nsIMdbHeap* ioHeap, morkStore* ioStore, nsIMdbHeap* ioSlotHeap, morkRowSpace* ioRowSpace, const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying mork_tid inTid, mork_kind inKind, mork_bool inMustBeUnique) : morkObject(ev, inUsage, ioHeap, (mork_color) inTid, (morkHandle*) 0) , mTable_Store( 0 ) , mTable_RowSpace( 0 ) , mTable_MetaRow( 0 ) , mTable_RowMap( 0 ) // , mTable_RowMap(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap, // morkTable_kStartRowMapSlotCount) , mTable_RowArray(ev, morkUsage::kMember, (nsIMdbHeap*) 0, morkTable_kStartRowArraySize, ioSlotHeap) , mTable_ChangeList() , mTable_ChangesCount( 0 ) , mTable_ChangesMax( 3 ) // any very small number greater than zero , mTable_Kind( inKind ) , mTable_Flags( 0 ) , mTable_Priority( morkPriority_kLo ) // NOT high priority , mTable_GcUses( 0 ) , mTable_Pad( 0 ) { this->mLink_Next = 0; this->mLink_Prev = 0; if ( ev->Good() ) { if ( ioStore && ioSlotHeap && ioRowSpace ) { if ( inKind ) { if ( inMustBeUnique ) this->SetTableUnique(); mTable_Store = ioStore; mTable_RowSpace = ioRowSpace; if ( inOptionalMetaRowOid ) mTable_MetaRowOid = *inOptionalMetaRowOid; else { mTable_MetaRowOid.mOid_Scope = 0; mTable_MetaRowOid.mOid_Id = morkRow_kMinusOneRid; } if ( ev->Good() ) { if ( this->MaybeDirtySpaceStoreAndTable() ) this->SetTableRewrite(); // everything is dirty mNode_Derived = morkDerived_kTable; } this->MaybeDirtySpaceStoreAndTable(); // new table might dirty store } else ioRowSpace->ZeroKindError(ev); } else ev->NilPointerError(); } } NS_IMPL_ISUPPORTS_INHERITED(morkTable, morkObject, nsIMdbTable) /*public non-poly*/ void morkTable::CloseTable(morkEnv* ev) /*i*/ // called by CloseMorkNode(); { if ( this->IsNode() ) { morkRowMap::SlotStrongRowMap((morkRowMap*) 0, ev, &mTable_RowMap); // mTable_RowMap.CloseMorkNode(ev); mTable_RowArray.CloseMorkNode(ev); mTable_Store = 0; mTable_RowSpace = 0; this->MarkShut(); } else this->NonNodeError(ev); } // } ===== end morkNode methods ===== // ````` ````` ````` ````` ````` // { ===== begin nsIMdbCollection methods ===== // { ----- begin attribute methods ----- NS_IMETHODIMP morkTable::GetSeed(nsIMdbEnv* mev, mdb_seed* outSeed) // member change count { nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if ( ev ) { *outSeed = mTable_RowArray.mArray_Seed; outErr = ev->AsErr(); } return outErr; } NS_IMETHODIMP morkTable::GetCount(nsIMdbEnv* mev, mdb_count* outCount) // member count { NS_ENSURE_ARG_POINTER(outCount); *outCount = mTable_RowArray.mArray_Fill; return NS_OK; } NS_IMETHODIMP morkTable::GetPort(nsIMdbEnv* mev, nsIMdbPort** acqPort) // collection container { (void) morkEnv::FromMdbEnv(mev); NS_ENSURE_ARG_POINTER(acqPort); *acqPort = mTable_Store; return NS_OK; } // } ----- end attribute methods ----- // { ----- begin cursor methods ----- NS_IMETHODIMP morkTable::GetCursor( // make a cursor starting iter at inMemberPos nsIMdbEnv* mev, // context mdb_pos inMemberPos, // zero-based ordinal pos of member in collection nsIMdbCursor** acqCursor) // acquire new cursor instance { return this->GetTableRowCursor(mev, inMemberPos, (nsIMdbTableRowCursor**) acqCursor); } // } ----- end cursor methods ----- // { ----- begin ID methods ----- NS_IMETHODIMP morkTable::GetOid(nsIMdbEnv* mev, mdbOid* outOid) // read object identity { morkEnv* ev = morkEnv::FromMdbEnv(mev); GetTableOid(ev, outOid); return NS_OK; } NS_IMETHODIMP morkTable::BecomeContent(nsIMdbEnv* mev, const mdbOid* inOid) // exchange content { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; // remember table->MaybeDirtySpaceStoreAndTable(); } // } ----- end ID methods ----- // { ----- begin activity dropping methods ----- NS_IMETHODIMP morkTable::DropActivity( // tell collection usage no longer expected nsIMdbEnv* mev) { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } // } ----- end activity dropping methods ----- // } ===== end nsIMdbCollection methods ===== // { ===== begin nsIMdbTable methods ===== // { ----- begin attribute methods ----- NS_IMETHODIMP morkTable::SetTablePriority(nsIMdbEnv* mev, mdb_priority inPrio) { nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if ( ev ) { if ( inPrio > morkPriority_kMax ) inPrio = morkPriority_kMax; mTable_Priority = inPrio; outErr = ev->AsErr(); } return outErr; } NS_IMETHODIMP morkTable::GetTablePriority(nsIMdbEnv* mev, mdb_priority* outPrio) { nsresult outErr = NS_OK; mork_priority prio = 0; morkEnv* ev = morkEnv::FromMdbEnv(mev); if ( ev ) { prio = mTable_Priority; if ( prio > morkPriority_kMax ) { prio = morkPriority_kMax; mTable_Priority = prio; } outErr = ev->AsErr(); } if ( outPrio ) *outPrio = prio; return outErr; } NS_IMETHODIMP morkTable:: GetTableBeVerbose(nsIMdbEnv* mev, mdb_bool* outBeVerbose) { NS_ENSURE_ARG_POINTER(outBeVerbose); *outBeVerbose = IsTableVerbose(); return NS_OK; } NS_IMETHODIMP morkTable::SetTableBeVerbose(nsIMdbEnv* mev, mdb_bool inBeVerbose) { nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if ( ev ) { if ( inBeVerbose ) SetTableVerbose(); else ClearTableVerbose(); outErr = ev->AsErr(); } return outErr; } NS_IMETHODIMP morkTable::GetTableIsUnique(nsIMdbEnv* mev, mdb_bool* outIsUnique) { NS_ENSURE_ARG_POINTER(outIsUnique); *outIsUnique = IsTableUnique(); return NS_OK; } NS_IMETHODIMP morkTable::GetTableKind(nsIMdbEnv* mev, mdb_kind* outTableKind) { NS_ENSURE_ARG_POINTER(outTableKind); *outTableKind = mTable_Kind; return NS_OK; } NS_IMETHODIMP morkTable::GetRowScope(nsIMdbEnv* mev, mdb_scope* outRowScope) { nsresult outErr = NS_OK; mdb_scope rowScope = 0; morkEnv* ev = morkEnv::FromMdbEnv(mev); if ( ev ) { if ( mTable_RowSpace ) rowScope = mTable_RowSpace->SpaceScope(); else NilRowSpaceError(ev); outErr = ev->AsErr(); } if ( outRowScope ) *outRowScope = rowScope; return outErr; } NS_IMETHODIMP morkTable::GetMetaRow( nsIMdbEnv* mev, const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying mdbOid* outOid, // output meta row oid, can be nil to suppress output nsIMdbRow** acqRow) // acquire table's unique singleton meta row // The purpose of a meta row is to support the persistent recording of // meta info about a table as cells put into the distinguished meta row. // Each table has exactly one meta row, which is not considered a member // of the collection of rows inside the table. The only way to tell // whether a row is a meta row is by the fact that it is returned by this // GetMetaRow() method from some table. Otherwise nothing distinguishes // a meta row from any other row. A meta row can be used anyplace that // any other row can be used, and can even be put into other tables (or // the same table) as a table member, if this is useful for some reason. // The first attempt to access a table's meta row using GetMetaRow() will // cause the meta row to be created if it did not already exist. When the // meta row is created, it will have the row oid that was previously // requested for this table's meta row; or if no oid was ever explicitly // specified for this meta row, then a unique oid will be generated in // the row scope named "metaScope" (so obviously MDB clients should not // manually allocate any row IDs from that special meta scope namespace). // The meta row oid can be specified either when the table is created, or // else the first time that GetMetaRow() is called, by passing a non-nil // pointer to an oid for parameter inOptionalMetaRowOid. The meta row's // actual oid is returned in outOid (if this is a non-nil pointer), and // it will be different from inOptionalMetaRowOid when the meta row was // already given a different oid earlier. { nsresult outErr = NS_OK; nsIMdbRow* outRow = 0; morkEnv* ev = morkEnv::FromMdbEnv(mev); if ( ev ) { morkRow* row = GetMetaRow(ev, inOptionalMetaRowOid); if ( row && ev->Good() ) { if ( outOid ) *outOid = row->mRow_Oid; outRow = row->AcquireRowHandle(ev, mTable_Store); } outErr = ev->AsErr(); } if ( acqRow ) *acqRow = outRow; if ( ev->Bad() && outOid ) { outOid->mOid_Scope = 0; outOid->mOid_Id = morkRow_kMinusOneRid; } return outErr; } // } ----- end attribute methods ----- // { ----- begin cursor methods ----- NS_IMETHODIMP morkTable::GetTableRowCursor( // make a cursor, starting iteration at inRowPos nsIMdbEnv* mev, // context mdb_pos inRowPos, // zero-based ordinal position of row in table nsIMdbTableRowCursor** acqCursor) // acquire new cursor instance { nsresult outErr = NS_OK; nsIMdbTableRowCursor* outCursor = 0; morkEnv* ev = morkEnv::FromMdbEnv(mev); if ( ev ) { morkTableRowCursor* cursor = NewTableRowCursor(ev, inRowPos); if ( cursor ) { if ( ev->Good() ) { // cursor->mCursor_Seed = (mork_seed) inRowPos; outCursor = cursor; outCursor->AddRef(); } } outErr = ev->AsErr(); } if ( acqCursor ) *acqCursor = outCursor; return outErr; } // } ----- end row position methods ----- // { ----- begin row position methods ----- NS_IMETHODIMP morkTable::PosToOid( // get row member for a table position nsIMdbEnv* mev, // context mdb_pos inRowPos, // zero-based ordinal position of row in table mdbOid* outOid) // row oid at the specified position { nsresult outErr = NS_OK; mdbOid roid; roid.mOid_Scope = 0; roid.mOid_Id = (mork_id) -1; morkEnv* ev = morkEnv::FromMdbEnv(mev); if ( ev ) { morkRow* row = SafeRowAt(ev, inRowPos); if ( row ) roid = row->mRow_Oid; outErr = ev->AsErr(); } if ( outOid ) *outOid = roid; return outErr; } NS_IMETHODIMP morkTable::OidToPos( // test for the table position of a row member nsIMdbEnv* mev, // context const mdbOid* inOid, // row to find in table mdb_pos* outPos) // zero-based ordinal position of row in table { nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if ( ev ) { mork_pos pos = ArrayHasOid(ev, inOid); if ( outPos ) *outPos = pos; outErr = ev->AsErr(); } return outErr; } NS_IMETHODIMP morkTable::PosToRow( // get row member for a table position nsIMdbEnv* mev, // context mdb_pos inRowPos, // zero-based ordinal position of row in table nsIMdbRow** acqRow) // acquire row at table position inRowPos { nsresult outErr = NS_OK; nsIMdbRow* outRow = 0; morkEnv* ev = morkEnv::FromMdbEnv(mev); if ( ev ) { morkRow* row = SafeRowAt(ev, inRowPos); if ( row && mTable_Store ) outRow = row->AcquireRowHandle(ev, mTable_Store); outErr = ev->AsErr(); } if ( acqRow ) *acqRow = outRow; return outErr; } NS_IMETHODIMP morkTable::RowToPos( // test for the table position of a row member nsIMdbEnv* mev, // context nsIMdbRow* ioRow, // row to find in table mdb_pos* outPos) // zero-based ordinal position of row in table { nsresult outErr = NS_OK; mork_pos pos = -1; morkEnv* ev = morkEnv::FromMdbEnv(mev); if ( ev ) { morkRowObject* row = (morkRowObject*) ioRow; pos = ArrayHasOid(ev, &row->mRowObject_Row->mRow_Oid); outErr = ev->AsErr(); } if ( outPos ) *outPos = pos; return outErr; } // Note that HasRow() performs the inverse oid->pos mapping // } ----- end row position methods ----- // { ----- begin oid set methods ----- NS_IMETHODIMP morkTable::AddOid( // make sure the row with inOid is a table member nsIMdbEnv* mev, // context const mdbOid* inOid) // row to ensure membership in table { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::HasOid( // test for the table position of a row member nsIMdbEnv* mev, // context const mdbOid* inOid, // row to find in table mdb_bool* outHasOid) // whether inOid is a member row { nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if ( ev ) { if ( outHasOid ) *outHasOid = MapHasOid(ev, inOid); outErr = ev->AsErr(); } return outErr; } NS_IMETHODIMP morkTable::CutOid( // make sure the row with inOid is not a member nsIMdbEnv* mev, // context const mdbOid* inOid) // row to remove from table { nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if ( ev ) { if ( inOid && mTable_Store ) { morkRow* row = mTable_Store->GetRow(ev, inOid); if ( row ) CutRow(ev, row); } else ev->NilPointerError(); outErr = ev->AsErr(); } return outErr; } // } ----- end oid set methods ----- // { ----- begin row set methods ----- NS_IMETHODIMP morkTable::NewRow( // create a new row instance in table nsIMdbEnv* mev, // context mdbOid* ioOid, // please use zero (unbound) rowId for db-assigned IDs nsIMdbRow** acqRow) // create new row { nsresult outErr = NS_OK; nsIMdbRow* outRow = 0; morkEnv* ev = morkEnv::FromMdbEnv(mev); if ( ev ) { if ( ioOid && mTable_Store ) { morkRow* row = 0; if ( ioOid->mOid_Id == morkRow_kMinusOneRid ) row = mTable_Store->NewRow(ev, ioOid->mOid_Scope); else row = mTable_Store->NewRowWithOid(ev, ioOid); if ( row && AddRow(ev, row) ) outRow = row->AcquireRowHandle(ev, mTable_Store); } else ev->NilPointerError(); outErr = ev->AsErr(); } if ( acqRow ) *acqRow = outRow; return outErr; } NS_IMETHODIMP morkTable::AddRow( // make sure the row with inOid is a table member nsIMdbEnv* mev, // context nsIMdbRow* ioRow) // row to ensure membership in table { nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if ( ev ) { morkRowObject *rowObj = (morkRowObject *) ioRow; morkRow* row = rowObj->mRowObject_Row; AddRow(ev, row); outErr = ev->AsErr(); } return outErr; } NS_IMETHODIMP morkTable::HasRow( // test for the table position of a row member nsIMdbEnv* mev, // context nsIMdbRow* ioRow, // row to find in table mdb_bool* outBool) // zero-based ordinal position of row in table { nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if ( ev ) { morkRowObject *rowObj = (morkRowObject *) ioRow; morkRow* row = rowObj->mRowObject_Row; if ( outBool ) *outBool = MapHasOid(ev, &row->mRow_Oid); outErr = ev->AsErr(); } return outErr; } NS_IMETHODIMP morkTable::CutRow( // make sure the row with inOid is not a member nsIMdbEnv* mev, // context nsIMdbRow* ioRow) // row to remove from table { nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if ( ev ) { morkRowObject *rowObj = (morkRowObject *) ioRow; morkRow* row = rowObj->mRowObject_Row; CutRow(ev, row); outErr = ev->AsErr(); } return outErr; } NS_IMETHODIMP morkTable::CutAllRows( // remove all rows from the table nsIMdbEnv* mev) // context { nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if ( ev ) { CutAllRows(ev); outErr = ev->AsErr(); } return outErr; } // } ----- end row set methods ----- // { ----- begin searching methods ----- NS_IMETHODIMP morkTable::FindRowMatches( // search variable number of sorted cols nsIMdbEnv* mev, // context const mdbYarn* inPrefix, // content to find as prefix in row's column cell nsIMdbTableRowCursor** acqCursor) // set of matching rows { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::GetSearchColumns( // query columns used by FindRowMatches() nsIMdbEnv* mev, // context mdb_count* outCount, // context mdbColumnSet* outColSet) // caller supplied space to put columns // GetSearchColumns() returns the columns actually searched when the // FindRowMatches() method is called. No more than mColumnSet_Count // slots of mColumnSet_Columns will be written, since mColumnSet_Count // indicates how many slots are present in the column array. The // actual number of search column used by the table is returned in // the outCount parameter; if this number exceeds mColumnSet_Count, // then a caller needs a bigger array to read the entire column set. // The minimum of mColumnSet_Count and outCount is the number slots // in mColumnSet_Columns that were actually written by this method. // // Callers are expected to change this set of columns by calls to // nsIMdbTable::SearchColumnsHint() or SetSearchSorting(), or both. { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } // } ----- end searching methods ----- // { ----- begin hinting methods ----- NS_IMETHODIMP morkTable::SearchColumnsHint( // advise re future expected search cols nsIMdbEnv* mev, // context const mdbColumnSet* inColumnSet) // columns likely to be searched { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::SortColumnsHint( // advise re future expected sort columns nsIMdbEnv* mev, // context const mdbColumnSet* inColumnSet) // columns for likely sort requests { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::StartBatchChangeHint( // advise before many adds and cuts nsIMdbEnv* mev, // context const void* inLabel) // intend unique address to match end call // If batch starts nest by virtue of nesting calls in the stack, then // the address of a local variable makes a good batch start label that // can be used at batch end time, and such addresses remain unique. { // we don't do anything here. return NS_OK; } NS_IMETHODIMP morkTable::EndBatchChangeHint( // advise before many adds and cuts nsIMdbEnv* mev, // context const void* inLabel) // label matching start label // Suppose a table is maintaining one or many sort orders for a table, // so that every row added to the table must be inserted in each sort, // and every row cut must be removed from each sort. If a db client // intends to make many such changes before needing any information // about the order or positions of rows inside a table, then a client // might tell the table to start batch changes in order to disable // sorting of rows for the interim. Presumably a table will then do // a full sort of all rows at need when the batch changes end, or when // a surprise request occurs for row position during batch changes. { // we don't do anything here. return NS_OK; } // } ----- end hinting methods ----- // { ----- begin sorting methods ----- // sorting: note all rows are assumed sorted by row ID as a secondary // sort following the primary column sort, when table rows are sorted. NS_IMETHODIMP morkTable::CanSortColumn( // query which column is currently used for sorting nsIMdbEnv* mev, // context mdb_column inColumn, // column to query sorting potential mdb_bool* outCanSort) // whether the column can be sorted { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::GetSorting( // view same table in particular sorting nsIMdbEnv* mev, // context mdb_column inColumn, // requested new column for sorting table nsIMdbSorting** acqSorting) // acquire sorting for column { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::SetSearchSorting( // use this sorting in FindRowMatches() nsIMdbEnv* mev, // context mdb_column inColumn, // often same as nsIMdbSorting::GetSortColumn() nsIMdbSorting* ioSorting) // requested sorting for some column // SetSearchSorting() attempts to inform the table that ioSorting // should be used during calls to FindRowMatches() for searching // the column which is actually sorted by ioSorting. This method // is most useful in conjunction with nsIMdbSorting::SetCompare(), // because otherwise a caller would not be able to override the // comparison ordering method used during searches. Note that some // database implementations might be unable to use an arbitrarily // specified sort order, either due to schema or runtime interface // constraints, in which case ioSorting might not actually be used. // Presumably ioSorting is an instance that was returned from some // earlier call to nsIMdbTable::GetSorting(). A caller can also // use nsIMdbTable::SearchColumnsHint() to specify desired change // in which columns are sorted and searched by FindRowMatches(). // // A caller can pass a nil pointer for ioSorting to request that // column inColumn no longer be used at all by FindRowMatches(). // But when ioSorting is non-nil, then inColumn should match the // column actually sorted by ioSorting; when these do not agree, // implementations are instructed to give precedence to the column // specified by ioSorting (so this means callers might just pass // zero for inColumn when ioSorting is also provided, since then // inColumn is both redundant and ignored). { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } // } ----- end sorting methods ----- // { ----- begin moving methods ----- // moving a row does nothing unless a table is currently unsorted NS_IMETHODIMP morkTable::MoveOid( // change position of row in unsorted table nsIMdbEnv* mev, // context const mdbOid* inOid, // row oid to find in table mdb_pos inHintFromPos, // suggested hint regarding start position mdb_pos inToPos, // desired new position for row inOid mdb_pos* outActualPos) // actual new position of row in table { nsresult outErr = NS_OK; mdb_pos actualPos = -1; // meaning it was never found in table morkEnv* ev = morkEnv::FromMdbEnv(mev); if ( ev ) { if ( inOid && mTable_Store ) { morkRow* row = mTable_Store->GetRow(ev, inOid); if ( row ) actualPos = MoveRow(ev, row, inHintFromPos, inToPos); } else ev->NilPointerError(); outErr = ev->AsErr(); } if ( outActualPos ) *outActualPos = actualPos; return outErr; } NS_IMETHODIMP morkTable::MoveRow( // change position of row in unsorted table nsIMdbEnv* mev, // context nsIMdbRow* ioRow, // row oid to find in table mdb_pos inHintFromPos, // suggested hint regarding start position mdb_pos inToPos, // desired new position for row ioRow mdb_pos* outActualPos) // actual new position of row in table { mdb_pos actualPos = -1; // meaning it was never found in table nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if ( ev ) { morkRowObject *rowObj = (morkRowObject *) ioRow; morkRow* row = rowObj->mRowObject_Row; actualPos = MoveRow(ev, row, inHintFromPos, inToPos); outErr = ev->AsErr(); } if ( outActualPos ) *outActualPos = actualPos; return outErr; } // } ----- end moving methods ----- // { ----- begin index methods ----- NS_IMETHODIMP morkTable::AddIndex( // create a sorting index for column if possible nsIMdbEnv* mev, // context mdb_column inColumn, // the column to sort by index nsIMdbThumb** acqThumb) // acquire thumb for incremental index building // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and // then the index addition will be finished. { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::CutIndex( // stop supporting a specific column index nsIMdbEnv* mev, // context mdb_column inColumn, // the column with index to be removed nsIMdbThumb** acqThumb) // acquire thumb for incremental index destroy // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and // then the index removal will be finished. { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::HasIndex( // query for current presence of a column index nsIMdbEnv* mev, // context mdb_column inColumn, // the column to investigate mdb_bool* outHasIndex) // whether column has index for this column { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::EnableIndexOnSort( // create an index for col on first sort nsIMdbEnv* mev, // context mdb_column inColumn) // the column to index if ever sorted { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::QueryIndexOnSort( // check whether index on sort is enabled nsIMdbEnv* mev, // context mdb_column inColumn, // the column to investigate mdb_bool* outIndexOnSort) // whether column has index-on-sort enabled { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::DisableIndexOnSort( // prevent future index creation on sort nsIMdbEnv* mev, // context mdb_column inColumn) // the column to index if ever sorted { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } // } ----- end index methods ----- // } ===== end nsIMdbTable methods ===== // we override these so that we'll use the xpcom add and release ref. #ifndef _MSC_VER mork_refs morkTable::AddStrongRef(nsIMdbEnv *ev) { return (mork_refs) AddRef(); } #endif mork_refs morkTable::AddStrongRef(morkEnv *ev) { return (mork_refs) AddRef(); } #ifndef _MSC_VER nsresult morkTable::CutStrongRef(nsIMdbEnv *ev) { return (nsresult) Release(); } #endif mork_refs morkTable::CutStrongRef(morkEnv *ev) { return (mork_refs) Release(); } mork_u2 morkTable::AddTableGcUse(morkEnv* ev) { MORK_USED_1(ev); if ( mTable_GcUses < morkTable_kMaxTableGcUses ) // not already maxed out? ++mTable_GcUses; return mTable_GcUses; } mork_u2 morkTable::CutTableGcUse(morkEnv* ev) { if ( mTable_GcUses ) // any outstanding uses to cut? { if ( mTable_GcUses < morkTable_kMaxTableGcUses ) // not frozen at max? --mTable_GcUses; } else this->TableGcUsesUnderflowWarning(ev); return mTable_GcUses; } // table dirty handling more complex thatn morkNode::SetNodeDirty() etc. void morkTable::SetTableClean(morkEnv* ev) { if ( mTable_ChangeList.HasListMembers() ) { nsIMdbHeap* heap = mTable_Store->mPort_Heap; mTable_ChangeList.CutAndZapAllListMembers(ev, heap); // forget changes } mTable_ChangesCount = 0; mTable_Flags = 0; this->SetNodeClean(); } // notifications regarding table changes: void morkTable::NoteTableMoveRow(morkEnv* ev, morkRow* ioRow, mork_pos inPos) { nsIMdbHeap* heap = mTable_Store->mPort_Heap; if ( this->IsTableRewrite() || this->HasChangeOverflow() ) this->NoteTableSetAll(ev); else { morkTableChange* tableChange = new(*heap, ev) morkTableChange(ev, ioRow, inPos); if ( tableChange ) { if ( ev->Good() ) { mTable_ChangeList.PushTail(tableChange); ++mTable_ChangesCount; } else { tableChange->ZapOldNext(ev, heap); this->SetTableRewrite(); // just plan to write all table rows } } } } void morkTable::note_row_move(morkEnv* ev, morkRow* ioRow, mork_pos inNewPos) { if ( this->IsTableRewrite() || this->HasChangeOverflow() ) this->NoteTableSetAll(ev); else { nsIMdbHeap* heap = mTable_Store->mPort_Heap; morkTableChange* tableChange = new(*heap, ev) morkTableChange(ev, ioRow, inNewPos); if ( tableChange ) { if ( ev->Good() ) { mTable_ChangeList.PushTail(tableChange); ++mTable_ChangesCount; } else { tableChange->ZapOldNext(ev, heap); this->NoteTableSetAll(ev); } } } } void morkTable::note_row_change(morkEnv* ev, mork_change inChange, morkRow* ioRow) { if ( this->IsTableRewrite() || this->HasChangeOverflow() ) this->NoteTableSetAll(ev); else { nsIMdbHeap* heap = mTable_Store->mPort_Heap; morkTableChange* tableChange = new(*heap, ev) morkTableChange(ev, inChange, ioRow); if ( tableChange ) { if ( ev->Good() ) { mTable_ChangeList.PushTail(tableChange); ++mTable_ChangesCount; } else { tableChange->ZapOldNext(ev, heap); this->NoteTableSetAll(ev); } } } } void morkTable::NoteTableSetAll(morkEnv* ev) { if ( mTable_ChangeList.HasListMembers() ) { nsIMdbHeap* heap = mTable_Store->mPort_Heap; mTable_ChangeList.CutAndZapAllListMembers(ev, heap); // forget changes } mTable_ChangesCount = 0; this->SetTableRewrite(); } /*static*/ void morkTable::TableGcUsesUnderflowWarning(morkEnv* ev) { ev->NewWarning("mTable_GcUses underflow"); } /*static*/ void morkTable::NonTableTypeError(morkEnv* ev) { ev->NewError("non morkTable"); } /*static*/ void morkTable::NonTableTypeWarning(morkEnv* ev) { ev->NewWarning("non morkTable"); } /*static*/ void morkTable::NilRowSpaceError(morkEnv* ev) { ev->NewError("nil mTable_RowSpace"); } mork_bool morkTable::MaybeDirtySpaceStoreAndTable() { morkRowSpace* rowSpace = mTable_RowSpace; if ( rowSpace ) { morkStore* store = rowSpace->mSpace_Store; if ( store && store->mStore_CanDirty ) { store->SetStoreDirty(); rowSpace->mSpace_CanDirty = morkBool_kTrue; } if ( rowSpace->mSpace_CanDirty ) // first time being dirtied? { if ( this->IsTableClean() ) { mork_count rowCount = this->GetRowCount(); mork_count oneThird = rowCount / 4; // one third of rows if ( oneThird > 0x07FFF ) // more than half max u2? oneThird = 0x07FFF; mTable_ChangesMax = (mork_u2) oneThird; } this->SetTableDirty(); rowSpace->SetRowSpaceDirty(); return morkBool_kTrue; } } return morkBool_kFalse; } morkRow* morkTable::GetMetaRow(morkEnv* ev, const mdbOid* inOptionalMetaRowOid) { morkRow* outRow = mTable_MetaRow; if ( !outRow ) { morkStore* store = mTable_Store; mdbOid* oid = &mTable_MetaRowOid; if ( inOptionalMetaRowOid && !oid->mOid_Scope ) *oid = *inOptionalMetaRowOid; if ( oid->mOid_Scope ) // oid already recorded in table? outRow = store->OidToRow(ev, oid); else { outRow = store->NewRow(ev, morkStore_kMetaScope); if ( outRow ) // need to record new oid in table? *oid = outRow->mRow_Oid; } mTable_MetaRow = outRow; if ( outRow ) // need to note another use of this row? { outRow->AddRowGcUse(ev); this->SetTableNewMeta(); if ( this->IsTableClean() ) // catch dirty status of meta row? this->MaybeDirtySpaceStoreAndTable(); } } return outRow; } void morkTable::GetTableOid(morkEnv* ev, mdbOid* outOid) { morkRowSpace* space = mTable_RowSpace; if ( space ) { outOid->mOid_Scope = space->SpaceScope(); outOid->mOid_Id = this->TableId(); } else this->NilRowSpaceError(ev); } nsIMdbTable* morkTable::AcquireTableHandle(morkEnv* ev) { AddRef(); return this; } mork_pos morkTable::ArrayHasOid(morkEnv* ev, const mdbOid* inOid) { MORK_USED_1(ev); mork_count count = mTable_RowArray.mArray_Fill; mork_pos pos = -1; while ( ++pos < (mork_pos)count ) { morkRow* row = (morkRow*) mTable_RowArray.At(pos); MORK_ASSERT(row); if ( row && row->EqualOid(inOid) ) { return pos; } } return -1; } mork_bool morkTable::MapHasOid(morkEnv* ev, const mdbOid* inOid) { if ( mTable_RowMap ) return ( mTable_RowMap->GetOid(ev, inOid) != 0 ); else return ( ArrayHasOid(ev, inOid) >= 0 ); } void morkTable::build_row_map(morkEnv* ev) { morkRowMap* map = mTable_RowMap; if ( !map ) { mork_count count = mTable_RowArray.mArray_Fill + 3; nsIMdbHeap* heap = mTable_Store->mPort_Heap; map = new(*heap, ev) morkRowMap(ev, morkUsage::kHeap, heap, heap, count); if ( map ) { if ( ev->Good() ) { mTable_RowMap = map; // put strong ref here count = mTable_RowArray.mArray_Fill; mork_pos pos = -1; while ( ++pos < (mork_pos)count ) { morkRow* row = (morkRow*) mTable_RowArray.At(pos); if ( row && row->IsRow() ) map->AddRow(ev, row); else row->NonRowTypeError(ev); } } else map->CutStrongRef(ev); } } } morkRow* morkTable::find_member_row(morkEnv* ev, morkRow* ioRow) { if ( mTable_RowMap ) return mTable_RowMap->GetRow(ev, ioRow); else { mork_count count = mTable_RowArray.mArray_Fill; mork_pos pos = -1; while ( ++pos < (mork_pos)count ) { morkRow* row = (morkRow*) mTable_RowArray.At(pos); if ( row == ioRow ) return row; } } return (morkRow*) 0; } mork_pos morkTable::MoveRow(morkEnv* ev, morkRow* ioRow, // change row position mork_pos inHintFromPos, // suggested hint regarding start position mork_pos inToPos) // desired new position for row ioRow // MoveRow() returns the actual position of ioRow afterwards; this // position is -1 if and only if ioRow was not found as a member. { mork_pos outPos = -1; // means ioRow was not a table member mork_bool canDirty = ( this->IsTableClean() )? this->MaybeDirtySpaceStoreAndTable() : morkBool_kTrue; morkRow** rows = (morkRow**) mTable_RowArray.mArray_Slots; mork_count count = mTable_RowArray.mArray_Fill; if ( count && rows && ev->Good() ) // any members at all? no errors? { mork_pos lastPos = count - 1; // index of last row slot if ( inToPos > lastPos ) // beyond last used array slot? inToPos = lastPos; // put row into last available slot else if ( inToPos < 0 ) // before first usable slot? inToPos = 0; // put row in very first slow if ( inHintFromPos > lastPos ) // beyond last used array slot? inHintFromPos = lastPos; // seek row in last available slot else if ( inHintFromPos < 0 ) // before first usable slot? inHintFromPos = 0; // seek row in very first slow morkRow** fromSlot = 0; // becomes nonzero of ioRow is ever found morkRow** rowsEnd = rows + count; // one past last used array slot if ( inHintFromPos <= 0 ) // start of table? just scan for row? { morkRow** cursor = rows - 1; // before first array slot while ( ++cursor < rowsEnd ) { if ( *cursor == ioRow ) { fromSlot = cursor; break; // end while loop } } } else // search near the start position and work outwards { morkRow** lo = rows + inHintFromPos; // lowest search point morkRow** hi = lo; // highest search point starts at lowest point // Seek ioRow in spiral widening search below and above inHintFromPos. // This is faster when inHintFromPos is at all accurate, but is slower // than a straightforward scan when inHintFromPos is nearly random. while ( lo >= rows || hi < rowsEnd ) // keep searching? { if ( lo >= rows ) // low direction search still feasible? { if ( *lo == ioRow ) // actually found the row? { fromSlot = lo; break; // end while loop } --lo; // advance further lower } if ( hi < rowsEnd ) // high direction search still feasible? { if ( *hi == ioRow ) // actually found the row? { fromSlot = hi; break; // end while loop } ++hi; // advance further higher } } } if ( fromSlot ) // ioRow was found as a table member? { outPos = fromSlot - rows; // actual position where row was found if ( outPos != inToPos ) // actually need to move this row? { morkRow** toSlot = rows + inToPos; // slot where row must go ++mTable_RowArray.mArray_Seed; // we modify the array now: if ( fromSlot < toSlot ) // row is moving upwards? { morkRow** up = fromSlot; // leading pointer going upward while ( ++up <= toSlot ) // have not gone above destination? { *fromSlot = *up; // shift down one fromSlot = up; // shift trailing pointer up } } else // ( fromSlot > toSlot ) // row is moving downwards { morkRow** down = fromSlot; // leading pointer going downward while ( --down >= toSlot ) // have not gone below destination? { *fromSlot = *down; // shift up one fromSlot = down; // shift trailing pointer } } *toSlot = ioRow; outPos = inToPos; // okay, we actually moved the row here if ( canDirty ) this->note_row_move(ev, ioRow, inToPos); } } } return outPos; } mork_bool morkTable::AddRow(morkEnv* ev, morkRow* ioRow) { morkRow* row = this->find_member_row(ev, ioRow); if ( !row && ev->Good() ) { mork_bool canDirty = ( this->IsTableClean() )? this->MaybeDirtySpaceStoreAndTable() : morkBool_kTrue; mork_pos pos = mTable_RowArray.AppendSlot(ev, ioRow); if ( ev->Good() && pos >= 0 ) { ioRow->AddRowGcUse(ev); if ( mTable_RowMap ) { if ( mTable_RowMap->AddRow(ev, ioRow) ) { // okay, anything else? } else mTable_RowArray.CutSlot(ev, pos); } else if ( mTable_RowArray.mArray_Fill >= morkTable_kMakeRowMapThreshold ) this->build_row_map(ev); if ( canDirty && ev->Good() ) this->NoteTableAddRow(ev, ioRow); } } return ev->Good(); } mork_bool morkTable::CutRow(morkEnv* ev, morkRow* ioRow) { morkRow* row = this->find_member_row(ev, ioRow); if ( row ) { mork_bool canDirty = ( this->IsTableClean() )? this->MaybeDirtySpaceStoreAndTable() : morkBool_kTrue; mork_count count = mTable_RowArray.mArray_Fill; morkRow** rowSlots = (morkRow**) mTable_RowArray.mArray_Slots; if ( rowSlots ) // array has vector as expected? { mork_pos pos = -1; morkRow** end = rowSlots + count; morkRow** slot = rowSlots - 1; // prepare for preincrement: while ( ++slot < end ) // another slot to check? { if ( *slot == row ) // found the slot containing row? { pos = slot - rowSlots; // record absolute position break; // end while loop } } if ( pos >= 0 ) // need to cut if from the array? mTable_RowArray.CutSlot(ev, pos); else ev->NewWarning("row not found in array"); } else mTable_RowArray.NilSlotsAddressError(ev); if ( mTable_RowMap ) mTable_RowMap->CutRow(ev, ioRow); if ( canDirty ) this->NoteTableCutRow(ev, ioRow); if ( ioRow->CutRowGcUse(ev) == 0 ) ioRow->OnZeroRowGcUse(ev); } return ev->Good(); } mork_bool morkTable::CutAllRows(morkEnv* ev) { if ( this->MaybeDirtySpaceStoreAndTable() ) { this->SetTableRewrite(); // everything is dirty this->NoteTableSetAll(ev); } if ( ev->Good() ) { mTable_RowArray.CutAllSlots(ev); if ( mTable_RowMap ) { morkRowMapIter i(ev, mTable_RowMap); mork_change* c = 0; morkRow* r = 0; for ( c = i.FirstRow(ev, &r); c; c = i.NextRow(ev, &r) ) { if ( r ) { if ( r->CutRowGcUse(ev) == 0 ) r->OnZeroRowGcUse(ev); i.CutHereRow(ev, (morkRow**) 0); } else ev->NewWarning("nil row in table map"); } } } return ev->Good(); } morkTableRowCursor* morkTable::NewTableRowCursor(morkEnv* ev, mork_pos inRowPos) { morkTableRowCursor* outCursor = 0; if ( ev->Good() ) { nsIMdbHeap* heap = mTable_Store->mPort_Heap; morkTableRowCursor* cursor = new(*heap, ev) morkTableRowCursor(ev, morkUsage::kHeap, heap, this, inRowPos); if ( cursor ) { if ( ev->Good() ) outCursor = cursor; else cursor->CutStrongRef((nsIMdbEnv *) ev); } } return outCursor; } //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 morkTableChange::morkTableChange(morkEnv* ev, mork_change inChange, morkRow* ioRow) // use this constructor for inChange == morkChange_kAdd or morkChange_kCut : morkNext() , mTableChange_Row( ioRow ) , mTableChange_Pos( morkTableChange_kNone ) { if ( ioRow ) { if ( ioRow->IsRow() ) { if ( inChange == morkChange_kAdd ) mTableChange_Pos = morkTableChange_kAdd; else if ( inChange == morkChange_kCut ) mTableChange_Pos = morkTableChange_kCut; else this->UnknownChangeError(ev); } else ioRow->NonRowTypeError(ev); } else ev->NilPointerError(); } morkTableChange::morkTableChange(morkEnv* ev, morkRow* ioRow, mork_pos inPos) // use this constructor when the row is moved : morkNext() , mTableChange_Row( ioRow ) , mTableChange_Pos( inPos ) { if ( ioRow ) { if ( ioRow->IsRow() ) { if ( inPos < 0 ) this->NegativeMovePosError(ev); } else ioRow->NonRowTypeError(ev); } else ev->NilPointerError(); } void morkTableChange::UnknownChangeError(morkEnv* ev) const // morkChange_kAdd or morkChange_kCut { ev->NewError("mTableChange_Pos neither kAdd nor kCut"); } void morkTableChange::NegativeMovePosError(morkEnv* ev) const // move must be non-neg position { ev->NewError("negative mTableChange_Pos for row move"); } //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 morkTableMap::~morkTableMap() { } morkTableMap::morkTableMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) #ifdef MORK_BEAD_OVER_NODE_MAPS : morkBeadMap(ev, inUsage, ioHeap, ioSlotHeap) #else /*MORK_BEAD_OVER_NODE_MAPS*/ : morkNodeMap(ev, inUsage, ioHeap, ioSlotHeap) #endif /*MORK_BEAD_OVER_NODE_MAPS*/ { if ( ev->Good() ) mNode_Derived = morkDerived_kTableMap; } //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789