| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_connectivity.hxx" |
| #include "dbase/dindexnode.hxx" |
| #include "connectivity/CommonTools.hxx" |
| #include <osl/thread.h> |
| #include "dbase/DIndex.hxx" |
| #include <tools/debug.hxx> |
| #include "diagnose_ex.h" |
| |
| #include <algorithm> |
| |
| |
| using namespace connectivity; |
| using namespace connectivity::dbase; |
| using namespace connectivity::file; |
| using namespace com::sun::star::sdbc; |
| // ----------------------------------------------------------------------------- |
| ONDXKey::ONDXKey(sal_uInt32 nRec) |
| :nRecord(nRec) |
| { |
| } |
| // ----------------------------------------------------------------------------- |
| ONDXKey::ONDXKey(const ORowSetValue& rVal, sal_Int32 eType, sal_uInt32 nRec) |
| : ONDXKey_BASE(eType) |
| , nRecord(nRec) |
| , xValue(rVal) |
| { |
| } |
| // ----------------------------------------------------------------------------- |
| ONDXKey::ONDXKey(const rtl::OUString& aStr, sal_uInt32 nRec) |
| : ONDXKey_BASE(::com::sun::star::sdbc::DataType::VARCHAR) |
| ,nRecord(nRec) |
| { |
| if (aStr.getLength()) |
| { |
| xValue = aStr; |
| xValue.setBound(sal_True); |
| } |
| } |
| // ----------------------------------------------------------------------------- |
| |
| ONDXKey::ONDXKey(double aVal, sal_uInt32 nRec) |
| : ONDXKey_BASE(::com::sun::star::sdbc::DataType::DOUBLE) |
| ,nRecord(nRec) |
| ,xValue(aVal) |
| { |
| } |
| // ----------------------------------------------------------------------------- |
| |
| //================================================================== |
| // Index Seite |
| //================================================================== |
| ONDXPage::ONDXPage(ODbaseIndex& rInd, sal_uInt32 nPos, ONDXPage* pParent) |
| :nPagePos(nPos) |
| ,bModified(sal_False) |
| ,nCount(0) |
| ,aParent(pParent) |
| ,rIndex(rInd) |
| ,ppNodes(NULL) |
| { |
| sal_uInt16 nT = rIndex.getHeader().db_maxkeys; |
| ppNodes = new ONDXNode[nT]; |
| } |
| |
| //------------------------------------------------------------------ |
| ONDXPage::~ONDXPage() |
| { |
| delete[] ppNodes; |
| // delete aParent; |
| // delete aChild; |
| } |
| //------------------------------------------------------------------ |
| void ONDXPage::QueryDelete() |
| { |
| // Ablegen im GarbageCollector |
| if (IsModified() && rIndex.m_pFileStream) |
| (*rIndex.m_pFileStream) << *this; |
| |
| bModified = sal_False; |
| if (rIndex.UseCollector()) |
| { |
| if (aChild.Is()) |
| aChild->Release(sal_False); |
| |
| for (sal_uInt16 i = 0; i < rIndex.getHeader().db_maxkeys;i++) |
| { |
| if (ppNodes[i].GetChild().Is()) |
| ppNodes[i].GetChild()->Release(sal_False); |
| |
| ppNodes[i] = ONDXNode(); |
| } |
| RestoreNoDelete(); |
| |
| nCount = 0; |
| aParent.Clear(); |
| rIndex.Collect(this); |
| } |
| else |
| SvRefBase::QueryDelete(); |
| } |
| //------------------------------------------------------------------ |
| ONDXPagePtr& ONDXPage::GetChild(ODbaseIndex* pIndex) |
| { |
| if (!aChild.Is() && pIndex) |
| { |
| aChild = rIndex.CreatePage(aChild.GetPagePos(),this,aChild.HasPage()); |
| } |
| return aChild; |
| } |
| |
| //------------------------------------------------------------------ |
| sal_uInt16 ONDXPage::FindPos(const ONDXKey& rKey) const |
| { |
| // sucht nach Platz fuer den vorgegeben key auf einer Seite |
| sal_uInt16 i = 0; |
| while (i < nCount && rKey > ((*this)[i]).GetKey()) |
| i++; |
| |
| return i; |
| } |
| |
| //------------------------------------------------------------------ |
| sal_Bool ONDXPage::Find(const ONDXKey& rKey) |
| { |
| // sucht den vorgegeben key |
| // Besonderheit: gelangt der Algorithmus ans Ende |
| // wird immer die aktuelle Seite und die Knotenposition vermerkt |
| // auf die die Bedingung <= zutrifft |
| // dieses findet beim Insert besondere Beachtung |
| sal_uInt16 i = 0; |
| while (i < nCount && rKey > ((*this)[i]).GetKey()) |
| i++; |
| |
| sal_Bool bResult = sal_False; |
| |
| if (!IsLeaf()) |
| { |
| // weiter absteigen |
| ONDXPagePtr aPage = (i==0) ? GetChild(&rIndex) : ((*this)[i-1]).GetChild(&rIndex, this); |
| bResult = aPage.Is() && aPage->Find(rKey); |
| } |
| else if (i == nCount) |
| { |
| rIndex.m_aCurLeaf = this; |
| rIndex.m_nCurNode = i - 1; |
| bResult = sal_False; |
| } |
| else |
| { |
| bResult = rKey == ((*this)[i]).GetKey(); |
| rIndex.m_aCurLeaf = this; |
| rIndex.m_nCurNode = bResult ? i : i - 1; |
| } |
| return bResult; |
| } |
| |
| //------------------------------------------------------------------ |
| sal_Bool ONDXPage::Insert(ONDXNode& rNode, sal_uInt32 nRowsLeft) |
| { |
| // beim Erzeugen eines Index koennen auch mehrere Knoten eingefuegt werden |
| // diese sin dann aufsteigend sortiert |
| sal_Bool bAppend = nRowsLeft > 0; |
| if (IsFull()) |
| { |
| sal_Bool bResult = sal_True; |
| ONDXNode aSplitNode; |
| if (bAppend) |
| aSplitNode = rNode; |
| else |
| { |
| // merken des letzten Knotens |
| aSplitNode = (*this)[nCount-1]; |
| if(rNode.GetKey() <= aSplitNode.GetKey()) |
| { |
| |
| // und damit habe ich im folgenden praktisch eine Node weniger |
| if (IsLeaf() && this == &rIndex.m_aCurLeaf) |
| { |
| // geht davon aus, dass der Knoten, auf dem die Bedingung (<=) |
| // zutrifft, als m_nCurNode gesetzt ist |
| --nCount; // (sonst bekomme ich u.U. Assertions und GPFs - 60593) |
| bResult = Insert(rIndex.m_nCurNode + 1, rNode); |
| } |
| else // Position unbekannt |
| { |
| sal_uInt16 nPos = NODE_NOTFOUND; |
| while (++nPos < nCount && rNode.GetKey() > ((*this)[nPos]).GetKey()) ; |
| |
| --nCount; // (sonst bekomme ich u.U. Assertions und GPFs - 60593) |
| bResult = Insert(nPos, rNode); |
| } |
| |
| // konnte der neue Knoten eingefuegt werden |
| if (!bResult) |
| { |
| nCount++; |
| aSplitNode = rNode; |
| } |
| } |
| else |
| aSplitNode = rNode; |
| } |
| |
| sal_uInt32 nNewPagePos = rIndex.GetPageCount(); |
| sal_uInt32 nNewPageCount = nNewPagePos + 1; |
| |
| // Herausgeloesten Knoten beim Vater einfuegen |
| if (!HasParent()) |
| { |
| // Kein Vater, dann neue Wurzel |
| ONDXPagePtr aNewRoot = rIndex.CreatePage(nNewPagePos + 1); |
| aNewRoot->SetChild(this); |
| |
| rIndex.m_aRoot = aNewRoot; |
| rIndex.SetRootPos(nNewPagePos + 1); |
| rIndex.SetPageCount(++nNewPageCount); |
| } |
| |
| // neues blatt erzeugen und Seite aufteilen |
| ONDXPagePtr aNewPage = rIndex.CreatePage(nNewPagePos,aParent); |
| rIndex.SetPageCount(nNewPageCount); |
| |
| // wieviele Knoten weren noch eingefuegt |
| // kommen noch ausreichend, dann koennen die Seiten bis zum Rand vollgestopft werden |
| |
| ONDXNode aInnerNode; |
| if (!IsLeaf() || nRowsLeft < (sal_uInt32)(rIndex.GetMaxNodes() / 2)) |
| aInnerNode = Split(*aNewPage); |
| else |
| { |
| aInnerNode = (*this)[nCount - 1]; |
| //aInnerNode = aSplitNode; |
| |
| // Knoten zeigt auf neue Seite |
| aInnerNode.SetChild(aNewPage); |
| |
| // innere Knoten haben keine Recordnummer |
| if (rIndex.isUnique()) |
| aInnerNode.GetKey().ResetRecord(); |
| |
| // neue Seite zeigt nun auf Seite des herausgeloesten Knoten |
| if (!IsLeaf()) |
| aNewPage->SetChild(aInnerNode.GetChild()); |
| } |
| |
| aNewPage->Append(aSplitNode); |
| ONDXPagePtr aTempParent = aParent; |
| if (IsLeaf()) |
| { |
| rIndex.m_aCurLeaf = aNewPage; |
| rIndex.m_nCurNode = rIndex.m_aCurLeaf->Count() - 1; |
| |
| // Freigeben nicht benoetigter Seiten, danach besteht keine Referenz |
| // mehr auf die Seite, danach kann 'this' nicht mehr gueltig sein!!! |
| ReleaseFull(); |
| } |
| |
| // Einfuegen des herausgeloesten Knotens |
| return aTempParent->Insert(aInnerNode); |
| } |
| else // Seite einfach weiter auffuellen |
| { |
| if (bAppend) |
| { |
| if (IsLeaf()) |
| rIndex.m_nCurNode = nCount - 1; |
| return Append(rNode); |
| } |
| else |
| { |
| sal_uInt16 nNodePos = FindPos(rNode.GetKey()); |
| if (IsLeaf()) |
| rIndex.m_nCurNode = nNodePos; |
| |
| return Insert(nNodePos, rNode); |
| } |
| } |
| } |
| |
| //------------------------------------------------------------------ |
| sal_Bool ONDXPage::Insert(sal_uInt16 nPos, ONDXNode& rNode) |
| { |
| sal_uInt16 nMaxCount = rIndex.getHeader().db_maxkeys; |
| if (nPos >= nMaxCount) |
| return sal_False; |
| |
| if (nCount) |
| { |
| ++nCount; |
| // nach rechts verschieben |
| for (sal_uInt16 i = std::min((sal_uInt16)(nMaxCount-1), (sal_uInt16)(nCount-1)); nPos < i; --i) |
| (*this)[i] = (*this)[i-1]; |
| } |
| else |
| if (nCount < nMaxCount) |
| nCount++; |
| |
| // einfuegen an der Position |
| ONDXNode& rInsertNode = (*this)[nPos]; |
| rInsertNode = rNode; |
| if (rInsertNode.GetChild().Is()) |
| { |
| rInsertNode.GetChild()->SetParent(this); |
| rNode.GetChild()->SetParent(this); |
| } |
| |
| bModified = sal_True; |
| |
| return sal_True; |
| } |
| |
| //------------------------------------------------------------------ |
| sal_Bool ONDXPage::Append(ONDXNode& rNode) |
| { |
| DBG_ASSERT(!IsFull(), "kein Append moeglich"); |
| return Insert(nCount, rNode); |
| } |
| //------------------------------------------------------------------ |
| void ONDXPage::Release(sal_Bool bSave) |
| { |
| // freigeben der Pages |
| if (aChild.Is()) |
| aChild->Release(bSave); |
| |
| // Pointer freigeben |
| aChild.Clear(); |
| |
| for (sal_uInt16 i = 0; i < rIndex.getHeader().db_maxkeys;i++) |
| { |
| if (ppNodes[i].GetChild()) |
| ppNodes[i].GetChild()->Release(bSave); |
| |
| ppNodes[i].GetChild().Clear(); |
| } |
| aParent = NULL; |
| } |
| //------------------------------------------------------------------ |
| void ONDXPage::ReleaseFull(sal_Bool bSave) |
| { |
| ONDXPagePtr aTempParent = aParent; |
| Release(bSave); |
| |
| if (aTempParent.Is()) |
| { |
| // Freigeben nicht benoetigter Seiten, danach besteht keine Referenz |
| // mehr auf die Seite, danach kann 'this' nicht mehr gueltig sein!!! |
| sal_uInt16 nParentPos = aTempParent->Search(this); |
| if (nParentPos != NODE_NOTFOUND) |
| (*aTempParent)[nParentPos].GetChild().Clear(); |
| else |
| aTempParent->GetChild().Clear(); |
| } |
| } |
| //------------------------------------------------------------------ |
| sal_Bool ONDXPage::Delete(sal_uInt16 nNodePos) |
| { |
| if (IsLeaf()) |
| { |
| // Letztes Element wird geloescht |
| if (nNodePos == (nCount - 1)) |
| { |
| ONDXNode aNode = (*this)[nNodePos]; |
| |
| // beim Parent muss nun der KeyValue ausgetauscht werden |
| if (HasParent()) |
| aParent->SearchAndReplace(aNode.GetKey(), |
| (*this)[nNodePos-1].GetKey()); |
| } |
| } |
| |
| // Loeschen des Knoten |
| Remove(nNodePos); |
| |
| // Unterlauf |
| if (HasParent() && nCount < (rIndex.GetMaxNodes() / 2)) |
| { |
| // Feststellen, welcher Knoten auf die Seite zeigt |
| sal_uInt16 nParentNodePos = aParent->Search(this); |
| // letzte Element auf Vaterseite |
| // -> zusammenlegen mit vorletzter Seite |
| if (nParentNodePos == (aParent->Count() - 1)) |
| { |
| if (!nParentNodePos) |
| // zusammenlegen mit linken nachbarn |
| Merge(nParentNodePos,aParent->GetChild(&rIndex)); |
| else |
| Merge(nParentNodePos,(*aParent)[nParentNodePos-1].GetChild(&rIndex,aParent)); |
| } |
| // sonst Seite mit naechster Seite zusammenlegen |
| else |
| { |
| // zusammenlegen mit rechten nachbarn |
| Merge(nParentNodePos + 1,((*aParent)[nParentNodePos + 1].GetChild(&rIndex,aParent))); |
| nParentNodePos++; |
| } |
| if (HasParent() && !(*aParent)[nParentNodePos].HasChild()) |
| aParent->Delete(nParentNodePos); |
| /* |
| // letzte Element auf Vaterseite |
| // -> zusammenlegen mit vorletzter Seite |
| if (nParentNodePos == (aParent->Count() - 1)) |
| { |
| if (!nParentNodePos) |
| // zusammenlegen mit linken nachbarn |
| Merge(nParentNodePos,aParent->GetChild(&rIndex)); |
| else |
| Merge(nParentNodePos,(*aParent)[nParentNodePos-1].GetChild(&rIndex,aParent)); |
| } |
| // sonst Seite mit naechster Seite zusammenlegen |
| else if(nParentNodePos != NODE_NOTFOUND) |
| { |
| // zusammenlegen mit rechten nachbarn |
| Merge(nParentNodePos + 1,((*aParent)[nParentNodePos + 1].GetChild(&rIndex,aParent))); |
| nParentNodePos++; |
| } |
| else // Sonderbehandlung |
| { |
| // Page ist aChild Page vom Parent => erste Page aus ppNodes an aChild anhaengen |
| Merge(0,(*aParent)[0].GetChild(&rIndex,aParent)); |
| nParentNodePos = 0; |
| } |
| |
| if (HasParent() && !(*aParent)[nParentNodePos].HasChild()) |
| aParent->Delete(nParentNodePos); |
| */ |
| |
| } |
| else if (IsRoot()) |
| // Sicherstellen das die Position der Wurzel festgehalten wird |
| rIndex.SetRootPos(nPagePos); |
| return sal_True; |
| } |
| |
| |
| //------------------------------------------------------------------ |
| ONDXNode ONDXPage::Split(ONDXPage& rPage) |
| { |
| DBG_ASSERT(IsFull(), "Falsches Splitting"); |
| /* Aufteilen einer Seite auf zwei |
| Blatt: |
| Seite 1 behaelt (n - (n/2)) |
| Seite 2 erhaelt (n/2) |
| Knoten n/2 wird dupliziert |
| Innerer Knoten: |
| Seite 1 behaelt (n+1)/2 |
| Seite 2 erhaelt (n/2-1) |
| Knoten ((n+1)/2 + 1) : wird herausgenommen |
| */ |
| ONDXNode aResultNode; |
| if (IsLeaf()) |
| { |
| for (sal_uInt16 i = (nCount - (nCount / 2)), j = 0 ; i < nCount; i++) |
| rPage.Insert(j++,(*this)[i]); |
| |
| // dieser Knoten enthaelt einen Schluessel der noch einmal im Tree vorkommt |
| // und ersetzt werden muss |
| ONDXNode aLastNode = (*this)[nCount - 1]; |
| nCount = nCount - (nCount / 2); |
| aResultNode = (*this)[nCount - 1]; |
| |
| if (HasParent()) |
| aParent->SearchAndReplace(aLastNode.GetKey(), |
| aResultNode.GetKey()); |
| } |
| else |
| { |
| for (sal_uInt16 i = (nCount + 1) / 2 + 1, j = 0 ; i < nCount; i++) |
| rPage.Insert(j++,(*this)[i]); |
| |
| aResultNode = (*this)[(nCount + 1) / 2]; |
| nCount = (nCount + 1) / 2; |
| |
| // neue Seite zeigt nun auf Seite des herausgeloesten Knoten |
| rPage.SetChild(aResultNode.GetChild()); |
| } |
| // Knoten zeigt auf neue Seite |
| aResultNode.SetChild(&rPage); |
| |
| // innere Knoten haben keine Recordnummer |
| if (rIndex.isUnique()) |
| aResultNode.GetKey().ResetRecord(); |
| bModified = sal_True; |
| return aResultNode; |
| } |
| |
| //------------------------------------------------------------------ |
| void ONDXPage::Merge(sal_uInt16 nParentNodePos, ONDXPagePtr xPage) |
| { |
| DBG_ASSERT(HasParent(), "kein Vater vorhanden"); |
| DBG_ASSERT(nParentNodePos != NODE_NOTFOUND, "Falscher Indexaufbau"); |
| |
| /* Zusammenlegen zweier Seiten */ |
| ONDXNode aResultNode; |
| sal_uInt16 nMaxNodes = rIndex.GetMaxNodes(), |
| nMaxNodes_2 = nMaxNodes / 2; |
| |
| // Feststellen ob Seite rechter oder linker Nachbar |
| sal_Bool bRight = ((*xPage)[0].GetKey() > (*this)[0].GetKey()); // sal_True, wenn xPage die rechte Seite ist |
| sal_uInt16 nNewCount = (*xPage).Count() + Count(); |
| |
| if (IsLeaf()) |
| { |
| // Bedingung fuers zusammenlegen |
| if (nNewCount < (nMaxNodes_2 * 2)) |
| { |
| sal_uInt16 nLastNode = bRight ? Count() - 1 : xPage->Count() - 1; |
| if (bRight) |
| { |
| DBG_ASSERT(&xPage != this,"xPage und THIS duerfen nicht gleich sein: Endlosschleife"); |
| // alle Knoten aus xPage auf den linken Knoten verschieben (anhaengen) |
| while (xPage->Count()) |
| { |
| Append((*xPage)[0]); |
| xPage->Remove(0); |
| } |
| } |
| else |
| { |
| DBG_ASSERT(&xPage != this,"xPage und THIS duerfen nicht gleich sein: Endlosschleife"); |
| // xPage ist die linke Page und THIS die rechte |
| while (xPage->Count()) |
| { |
| Insert(0,(*xPage)[xPage->Count()-1]); |
| xPage->Remove(xPage->Count()-1); |
| } |
| // alte Position von xPage beim Parent mit this ersetzen |
| if (nParentNodePos) |
| (*aParent)[nParentNodePos-1].SetChild(this,aParent); |
| else // oder als rechten Knoten setzen |
| aParent->SetChild(this); |
| aParent->SetModified(sal_True); |
| |
| } |
| |
| // Child beziehung beim Vaterknoten aufheben |
| (*aParent)[nParentNodePos].SetChild(); |
| // Austauschen des KnotenWertes, nur wenn geaenderte Page |
| // die linke ist, ansonsten werde |
| |
| if(aParent->IsRoot() && aParent->Count() == 1) |
| { |
| (*aParent)[0].SetChild(); |
| aParent->ReleaseFull(); |
| aParent = NULL; |
| rIndex.SetRootPos(nPagePos); |
| rIndex.m_aRoot = this; |
| SetModified(sal_True); |
| } |
| else |
| aParent->SearchAndReplace((*this)[nLastNode].GetKey(),(*this)[nCount-1].GetKey()); |
| |
| xPage->SetModified(sal_False); |
| xPage->ReleaseFull(); // wird nicht mehr benoetigt |
| } |
| // Ausgleichen der Elemente nNewCount >= (nMaxNodes_2 * 2) |
| else |
| { |
| if (bRight) |
| { |
| // alle Knoten aus xPage auf den linken Knoten verschieben (anhaengen) |
| ONDXNode aReplaceNode = (*this)[nCount - 1]; |
| while (nCount < nMaxNodes_2) |
| { |
| Append((*xPage)[0]); |
| xPage->Remove(0); |
| } |
| // Austauschen des KnotenWertes: Setzt alten letzten Wert durch den letzten von xPage |
| aParent->SearchAndReplace(aReplaceNode.GetKey(),(*this)[nCount-1].GetKey()); |
| } |
| else |
| { |
| // alle Knoten aus this vor die xPage Knoten einfuegen |
| ONDXNode aReplaceNode = (*this)[nCount - 1]; |
| while (xPage->Count() < nMaxNodes_2) |
| { |
| xPage->Insert(0,(*this)[nCount-1]); |
| Remove(nCount-1); |
| } |
| // Austauschen des KnotenWertes |
| aParent->SearchAndReplace(aReplaceNode.GetKey(),(*this)[Count()-1].GetKey()); |
| } |
| } |
| } |
| else // !IsLeaf() |
| { |
| // Bedingung fuers zusammenlegen |
| if (nNewCount < nMaxNodes_2 * 2) |
| { |
| if (bRight) |
| { |
| DBG_ASSERT(&xPage != this,"xPage und THIS duerfen nicht gleich sein: Endlosschleife"); |
| // Vaterknoten wird mit integriert |
| // erhaelt zunaechst Child von xPage |
| (*aParent)[nParentNodePos].SetChild(xPage->GetChild(),aParent); |
| Append((*aParent)[nParentNodePos]); |
| for (sal_uInt16 i = 0 ; i < xPage->Count(); i++) |
| Append((*xPage)[i]); |
| } |
| else |
| { |
| DBG_ASSERT(&xPage != this,"xPage und THIS duerfen nicht gleich sein: Endlosschleife"); |
| // Vaterknoten wird mit integriert |
| // erhaelt zunaechst Child |
| (*aParent)[nParentNodePos].SetChild(GetChild(),aParent); // Parent merkt sich mein Child |
| Insert(0,(*aParent)[nParentNodePos]); // Node vom Parent bei mir einfuegen |
| while (xPage->Count()) |
| { |
| Insert(0,(*xPage)[xPage->Count()-1]); |
| xPage->Remove(xPage->Count()-1); |
| } |
| SetChild(xPage->GetChild()); |
| |
| if (nParentNodePos) |
| (*aParent)[nParentNodePos-1].SetChild(this,aParent); |
| else |
| aParent->SetChild(this); |
| } |
| |
| // danach wird der Vaterknoten zurueckgesetzt |
| (*aParent)[nParentNodePos].SetChild(); |
| aParent->SetModified(sal_True); |
| |
| if(aParent->IsRoot() && aParent->Count() == 1) |
| { |
| (*aParent).SetChild(); |
| aParent->ReleaseFull(); |
| aParent = NULL; |
| rIndex.SetRootPos(nPagePos); |
| rIndex.m_aRoot = this; |
| SetModified(sal_True); |
| } |
| else if(nParentNodePos) |
| // Austauschen des KnotenWertes |
| // beim Append wird der Bereich erweitert, beim INsert verweist der alte Knoten von xPage auf this |
| // deshalb muss der Knoten auch hier aktualisiert werden |
| aParent->SearchAndReplace((*aParent)[nParentNodePos-1].GetKey(),(*aParent)[nParentNodePos].GetKey()); |
| |
| xPage->SetModified(sal_False); |
| xPage->ReleaseFull(); |
| } |
| // Ausgleichen der Elemente |
| else |
| { |
| if (bRight) |
| { |
| while (nCount < nMaxNodes_2) |
| { |
| (*aParent)[nParentNodePos].SetChild(xPage->GetChild(),aParent); |
| Append((*aParent)[nParentNodePos]); |
| (*aParent)[nParentNodePos] = (*xPage)[0]; |
| xPage->Remove(0); |
| } |
| xPage->SetChild((*aParent)[nParentNodePos].GetChild()); |
| (*aParent)[nParentNodePos].SetChild(xPage,aParent); |
| } |
| else |
| { |
| while (nCount < nMaxNodes_2) |
| { |
| (*aParent)[nParentNodePos].SetChild(GetChild(),aParent); |
| Insert(0,(*aParent)[nParentNodePos]); |
| (*aParent)[nParentNodePos] = (*xPage)[xPage->Count()-1]; |
| xPage->Remove(xPage->Count()-1); |
| } |
| SetChild((*aParent)[nParentNodePos].GetChild()); |
| (*aParent)[nParentNodePos].SetChild(this,aParent); |
| |
| } |
| aParent->SetModified(sal_True); |
| } |
| } |
| } |
| //================================================================== |
| // ONDXNode |
| //================================================================== |
| |
| //------------------------------------------------------------------ |
| void ONDXNode::Read(SvStream &rStream, ODbaseIndex& rIndex) |
| { |
| rStream >> aKey.nRecord; // schluessel |
| |
| if (rIndex.getHeader().db_keytype) |
| { |
| double aDbl; |
| rStream >> aDbl; |
| aKey = ONDXKey(aDbl,aKey.nRecord); |
| } |
| else |
| { |
| ByteString aBuf; |
| sal_uInt16 nLen = rIndex.getHeader().db_keylen; |
| char* pStr = aBuf.AllocBuffer(nLen+1); |
| |
| rStream.Read(pStr,nLen); |
| pStr[nLen] = 0; |
| aBuf.ReleaseBufferAccess(); |
| aBuf.EraseTrailingChars(); |
| |
| // aKey = ONDXKey((aBuf,rIndex.GetDBFConnection()->GetCharacterSet()) ,aKey.nRecord); |
| aKey = ONDXKey(::rtl::OUString(aBuf.GetBuffer(),aBuf.Len(),rIndex.m_pTable->getConnection()->getTextEncoding()) ,aKey.nRecord); |
| } |
| rStream >> aChild; |
| } |
| |
| union NodeData |
| { |
| double aDbl; |
| char aData[128]; |
| } aNodeData; |
| //------------------------------------------------------------------ |
| void ONDXNode::Write(SvStream &rStream, const ONDXPage& rPage) const |
| { |
| const ODbaseIndex& rIndex = rPage.GetIndex(); |
| if (!rIndex.isUnique() || rPage.IsLeaf()) |
| rStream << (sal_uInt32)aKey.nRecord; // schluessel |
| else |
| rStream << (sal_uInt32)0; // schluessel |
| |
| if (rIndex.getHeader().db_keytype) // double |
| { |
| if (aKey.getValue().isNull()) |
| { |
| memset(aNodeData.aData,0,rIndex.getHeader().db_keylen); |
| rStream.Write((sal_uInt8*)aNodeData.aData,rIndex.getHeader().db_keylen); |
| } |
| else |
| rStream << (double) aKey.getValue(); |
| } |
| else |
| { |
| memset(aNodeData.aData,0x20,rIndex.getHeader().db_keylen); |
| if (!aKey.getValue().isNull()) |
| { |
| ::rtl::OUString sValue = aKey.getValue(); |
| ByteString aText(sValue.getStr(), rIndex.m_pTable->getConnection()->getTextEncoding()); |
| strncpy(aNodeData.aData,aText.GetBuffer(),std::min(rIndex.getHeader().db_keylen, aText.Len())); |
| } |
| rStream.Write((sal_uInt8*)aNodeData.aData,rIndex.getHeader().db_keylen); |
| } |
| rStream << aChild; |
| } |
| |
| |
| //------------------------------------------------------------------ |
| ONDXPagePtr& ONDXNode::GetChild(ODbaseIndex* pIndex, ONDXPage* pParent) |
| { |
| if (!aChild.Is() && pIndex) |
| { |
| aChild = pIndex->CreatePage(aChild.GetPagePos(),pParent,aChild.HasPage()); |
| } |
| return aChild; |
| } |
| |
| //================================================================== |
| // ONDXKey |
| //================================================================== |
| //------------------------------------------------------------------ |
| sal_Bool ONDXKey::IsText(sal_Int32 eType) |
| { |
| return eType == DataType::VARCHAR || eType == DataType::CHAR; |
| } |
| |
| //------------------------------------------------------------------ |
| StringCompare ONDXKey::Compare(const ONDXKey& rKey) const |
| { |
| // DBG_ASSERT(is(), "Falscher Indexzugriff"); |
| StringCompare eResult; |
| |
| if (getValue().isNull()) |
| { |
| if (rKey.getValue().isNull() || (rKey.IsText(getDBType()) && !rKey.getValue().getString().getLength())) |
| eResult = COMPARE_EQUAL; |
| else |
| eResult = COMPARE_LESS; |
| } |
| else if (rKey.getValue().isNull()) |
| { |
| if (getValue().isNull() || (IsText(getDBType()) && !getValue().getString().getLength())) |
| eResult = COMPARE_EQUAL; |
| else |
| eResult = COMPARE_GREATER; |
| } |
| else if (IsText(getDBType())) |
| { |
| sal_Int32 nRes = getValue().getString().compareTo(rKey.getValue()); |
| eResult = (nRes > 0) ? COMPARE_GREATER : (nRes == 0) ? COMPARE_EQUAL : COMPARE_LESS; |
| } |
| else |
| { |
| double m = getValue(),n = rKey.getValue(); |
| eResult = (m > n) ? COMPARE_GREATER : (n == m) ? COMPARE_EQUAL : COMPARE_LESS; |
| } |
| |
| // Record vergleich, wenn Index !Unique |
| if (eResult == COMPARE_EQUAL && nRecord && rKey.nRecord) |
| eResult = (nRecord > rKey.nRecord) ? COMPARE_GREATER : |
| (nRecord == rKey.nRecord) ? COMPARE_EQUAL : COMPARE_LESS; |
| |
| return eResult; |
| } |
| // ----------------------------------------------------------------------------- |
| void ONDXKey::setValue(const ORowSetValue& _rVal) |
| { |
| xValue = _rVal; |
| } |
| // ----------------------------------------------------------------------------- |
| const ORowSetValue& ONDXKey::getValue() const |
| { |
| return xValue; |
| } |
| // ----------------------------------------------------------------------------- |
| SvStream& connectivity::dbase::operator >> (SvStream &rStream, ONDXPagePtr& rPage) |
| { |
| rStream >> rPage.nPagePos; |
| return rStream; |
| } |
| // ----------------------------------------------------------------------------- |
| SvStream& connectivity::dbase::operator << (SvStream &rStream, const ONDXPagePtr& rPage) |
| { |
| rStream << rPage.nPagePos; |
| return rStream; |
| } |
| // ----------------------------------------------------------------------------- |
| //================================================================== |
| // ONDXPagePtr |
| //================================================================== |
| //------------------------------------------------------------------ |
| ONDXPagePtr::ONDXPagePtr(const ONDXPagePtr& rRef) |
| :ONDXPageRef(rRef) |
| ,nPagePos(rRef.nPagePos) |
| { |
| } |
| |
| //------------------------------------------------------------------ |
| ONDXPagePtr::ONDXPagePtr(ONDXPage* pRefPage) |
| :ONDXPageRef(pRefPage) |
| ,nPagePos(0) |
| { |
| if (pRefPage) |
| nPagePos = pRefPage->GetPagePos(); |
| } |
| //------------------------------------------------------------------ |
| ONDXPagePtr& ONDXPagePtr::operator=(const ONDXPagePtr& rRef) |
| { |
| ONDXPageRef::operator=(rRef); |
| nPagePos = rRef.nPagePos; |
| return *this; |
| } |
| |
| //------------------------------------------------------------------ |
| ONDXPagePtr& ONDXPagePtr::operator= (ONDXPage* pRef) |
| { |
| ONDXPageRef::operator=(pRef); |
| nPagePos = (pRef) ? pRef->GetPagePos() : 0; |
| return *this; |
| } |
| // ----------------------------------------------------------------------------- |
| static sal_uInt32 nValue; |
| //------------------------------------------------------------------ |
| SvStream& connectivity::dbase::operator >> (SvStream &rStream, ONDXPage& rPage) |
| { |
| rStream.Seek(rPage.GetPagePos() * PAGE_SIZE); |
| rStream >> nValue >> rPage.aChild; |
| rPage.nCount = sal_uInt16(nValue); |
| |
| // DBG_ASSERT(rPage.nCount && rPage.nCount < rPage.GetIndex().GetMaxNodes(), "Falscher Count"); |
| for (sal_uInt16 i = 0; i < rPage.nCount; i++) |
| rPage[i].Read(rStream, rPage.GetIndex()); |
| return rStream; |
| } |
| |
| //------------------------------------------------------------------ |
| SvStream& connectivity::dbase::operator << (SvStream &rStream, const ONDXPage& rPage) |
| { |
| // Seite existiert noch nicht |
| sal_uIntPtr nSize = (rPage.GetPagePos() + 1) * PAGE_SIZE; |
| if (nSize > rStream.Seek(STREAM_SEEK_TO_END)) |
| { |
| rStream.SetStreamSize(nSize); |
| rStream.Seek(rPage.GetPagePos() * PAGE_SIZE); |
| |
| char aEmptyData[PAGE_SIZE]; |
| memset(aEmptyData,0x00,PAGE_SIZE); |
| rStream.Write((sal_uInt8*)aEmptyData,PAGE_SIZE); |
| } |
| sal_uIntPtr nCurrentPos = rStream.Seek(rPage.GetPagePos() * PAGE_SIZE); |
| OSL_UNUSED( nCurrentPos ); |
| |
| nValue = rPage.nCount; |
| rStream << nValue << rPage.aChild; |
| |
| sal_uInt16 i = 0; |
| for (; i < rPage.nCount; i++) |
| rPage[i].Write(rStream, rPage); |
| |
| // check if we have to fill the stream with '\0' |
| if(i < rPage.rIndex.getHeader().db_maxkeys) |
| { |
| sal_uIntPtr nTell = rStream.Tell() % PAGE_SIZE; |
| sal_uInt16 nBufferSize = rStream.GetBufferSize(); |
| sal_uIntPtr nRemainSize = nBufferSize - nTell; |
| if ( nRemainSize <= nBufferSize ) |
| { |
| char* pEmptyData = new char[nRemainSize]; |
| memset(pEmptyData,0x00,nRemainSize); |
| rStream.Write((sal_uInt8*)pEmptyData,nRemainSize); |
| rStream.Seek(nTell); |
| delete [] pEmptyData; |
| } |
| } |
| return rStream; |
| } |
| // ----------------------------------------------------------------------------- |
| #if OSL_DEBUG_LEVEL > 1 |
| //------------------------------------------------------------------ |
| void ONDXPage::PrintPage() |
| { |
| DBG_TRACE4("\nSDB: -----------Page: %d Parent: %d Count: %d Child: %d-----", |
| nPagePos, HasParent() ? aParent->GetPagePos() : 0 ,nCount, aChild.GetPagePos()); |
| |
| for (sal_uInt16 i = 0; i < nCount; i++) |
| { |
| ONDXNode rNode = (*this)[i]; |
| ONDXKey& rKey = rNode.GetKey(); |
| if (!IsLeaf()) |
| rNode.GetChild(&rIndex, this); |
| |
| if (rKey.getValue().isNull()) |
| { |
| DBG_TRACE2("SDB: [%d,NULL,%d]",rKey.GetRecord(), rNode.GetChild().GetPagePos()); |
| } |
| else if (rIndex.getHeader().db_keytype) |
| { |
| DBG_TRACE3("SDB: [%d,%f,%d]",rKey.GetRecord(), rKey.getValue().getDouble(),rNode.GetChild().GetPagePos()); |
| } |
| else |
| { |
| DBG_TRACE3("SDB: [%d,%s,%d]",rKey.GetRecord(), (const char* )ByteString(rKey.getValue().getString().getStr(), rIndex.m_pTable->getConnection()->getTextEncoding()).GetBuffer(),rNode.GetChild().GetPagePos()); |
| } |
| } |
| DBG_TRACE("SDB: -----------------------------------------------\n"); |
| if (!IsLeaf()) |
| { |
| #if OSL_DEBUG_LEVEL > 1 |
| GetChild(&rIndex)->PrintPage(); |
| for (sal_uInt16 i = 0; i < nCount; i++) |
| { |
| ONDXNode rNode = (*this)[i]; |
| rNode.GetChild(&rIndex,this)->PrintPage(); |
| } |
| #endif |
| } |
| DBG_TRACE("SDB: ===============================================\n"); |
| } |
| #endif |
| // ----------------------------------------------------------------------------- |
| sal_Bool ONDXPage::IsFull() const |
| { |
| return Count() == rIndex.getHeader().db_maxkeys; |
| } |
| // ----------------------------------------------------------------------------- |
| //------------------------------------------------------------------ |
| sal_uInt16 ONDXPage::Search(const ONDXKey& rSearch) |
| { |
| // binare Suche spaeter |
| sal_uInt16 i = NODE_NOTFOUND; |
| while (++i < Count()) |
| if ((*this)[i].GetKey() == rSearch) |
| break; |
| |
| return (i < Count()) ? i : NODE_NOTFOUND; |
| } |
| |
| //------------------------------------------------------------------ |
| sal_uInt16 ONDXPage::Search(const ONDXPage* pPage) |
| { |
| sal_uInt16 i = NODE_NOTFOUND; |
| while (++i < Count()) |
| if (((*this)[i]).GetChild() == pPage) |
| break; |
| |
| // wenn nicht gefunden, dann wird davon ausgegangen, dass die Seite selbst |
| // auf die Page zeigt |
| return (i < Count()) ? i : NODE_NOTFOUND; |
| } |
| // ----------------------------------------------------------------------------- |
| // laeuft rekursiv |
| void ONDXPage::SearchAndReplace(const ONDXKey& rSearch, |
| ONDXKey& rReplace) |
| { |
| OSL_ENSURE(rSearch != rReplace,"Invalid here:rSearch == rReplace"); |
| if (rSearch != rReplace) |
| { |
| sal_uInt16 nPos = NODE_NOTFOUND; |
| ONDXPage* pPage = this; |
| |
| while (pPage && (nPos = pPage->Search(rSearch)) == NODE_NOTFOUND) |
| pPage = pPage->aParent; |
| |
| if (pPage) |
| { |
| (*pPage)[nPos].GetKey() = rReplace; |
| pPage->SetModified(sal_True); |
| } |
| } |
| } |
| // ----------------------------------------------------------------------------- |
| ONDXNode& ONDXPage::operator[] (sal_uInt16 nPos) |
| { |
| DBG_ASSERT(nCount > nPos, "falscher Indexzugriff"); |
| return ppNodes[nPos]; |
| } |
| |
| //------------------------------------------------------------------ |
| const ONDXNode& ONDXPage::operator[] (sal_uInt16 nPos) const |
| { |
| DBG_ASSERT(nCount > nPos, "falscher Indexzugriff"); |
| return ppNodes[nPos]; |
| } |
| // ----------------------------------------------------------------------------- |
| void ONDXPage::Remove(sal_uInt16 nPos) |
| { |
| DBG_ASSERT(nCount > nPos, "falscher Indexzugriff"); |
| |
| for (sal_uInt16 i = nPos; i < (nCount-1); i++) |
| (*this)[i] = (*this)[i+1]; |
| |
| nCount--; |
| bModified = sal_True; |
| } |
| // ----------------------------------------------------------------------------- |
| |