Use correct object
[LibreOffice.git] / connectivity / source / drivers / dbase / DIndex.cxx
blob1c32440e6b89354898d87c68e407c5a01f668c0d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <dbase/DIndex.hxx>
21 #include <dbase/DIndexColumns.hxx>
22 #include <dbase/DTable.hxx>
23 #include <dbase/DIndexIter.hxx>
24 #include <osl/file.hxx>
25 #include <sal/log.hxx>
26 #include <tools/config.hxx>
27 #include <connectivity/CommonTools.hxx>
28 #include <com/sun/star/sdbc/XResultSet.hpp>
29 #include <com/sun/star/sdbc/XRow.hpp>
30 #include <unotools/ucbhelper.hxx>
31 #include <comphelper/servicehelper.hxx>
32 #include <comphelper/types.hxx>
33 #include <connectivity/dbexception.hxx>
34 #include <dbase/DResultSet.hxx>
35 #include <strings.hrc>
36 #include <unotools/sharedunocomponent.hxx>
38 using namespace ::comphelper;
40 using namespace connectivity;
41 using namespace utl;
42 using namespace ::cppu;
43 using namespace connectivity::file;
44 using namespace connectivity::dbase;
45 using namespace com::sun::star::sdbc;
46 using namespace com::sun::star::uno;
47 using namespace com::sun::star::beans;
49 IMPLEMENT_SERVICE_INFO(ODbaseIndex,u"com.sun.star.sdbcx.driver.dbase.Index"_ustr,u"com.sun.star.sdbcx.Index"_ustr);
51 ODbaseIndex::ODbaseIndex(ODbaseTable* _pTable)
52 : OIndex(true/*_pTable->getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers()*/)
53 , m_nCurNode(NODE_NOTFOUND)
54 , m_nPageCount(0)
55 , m_nRootPage(0)
56 , m_pTable(_pTable)
57 , m_bUseCollector(false)
59 construct();
62 ODbaseIndex::ODbaseIndex( ODbaseTable* _pTable,
63 const NDXHeader& _rHeader,
64 const OUString& _rName)
65 : OIndex(_rName, OUString(), _rHeader.db_unique, false, false, true)
66 , m_aHeader(_rHeader)
67 , m_nCurNode(NODE_NOTFOUND)
68 , m_nPageCount(0)
69 , m_nRootPage(0)
70 , m_pTable(_pTable)
71 , m_bUseCollector(false)
73 construct();
76 ODbaseIndex::~ODbaseIndex()
78 closeImpl();
81 void ODbaseIndex::refreshColumns()
83 ::osl::MutexGuard aGuard( m_aMutex );
85 ::std::vector< OUString> aVector;
86 if(!isNew())
88 OSL_ENSURE(m_pFileStream,"FileStream is not opened!");
89 OSL_ENSURE(m_aHeader.db_name[0] != '\0',"Invalid name for the column!");
90 aVector.push_back(OUString::createFromAscii(m_aHeader.db_name));
93 if(m_pColumns)
94 m_pColumns->reFill(aVector);
95 else
96 m_pColumns.reset(new ODbaseIndexColumns(this,m_aMutex,aVector));
99 ONDXPagePtr const & ODbaseIndex::getRoot()
101 openIndexFile();
102 if (!m_aRoot.Is())
104 m_nRootPage = m_aHeader.db_rootpage;
105 m_nPageCount = m_aHeader.db_pagecount;
106 m_aRoot = CreatePage(m_nRootPage,nullptr,true);
108 return m_aRoot;
111 void ODbaseIndex::openIndexFile()
113 if(m_pFileStream)
114 return;
116 OUString sFile = getCompletePath();
117 if(UCBContentHelper::Exists(sFile))
119 m_pFileStream = OFileTable::createStream_simpleError(sFile, StreamMode::READWRITE | StreamMode::NOCREATE | StreamMode::SHARE_DENYWRITE);
120 if (!m_pFileStream)
121 m_pFileStream = OFileTable::createStream_simpleError(sFile, StreamMode::READ | StreamMode::NOCREATE | StreamMode::SHARE_DENYNONE);
122 if(m_pFileStream)
124 m_pFileStream->SetEndian(SvStreamEndian::LITTLE);
125 m_pFileStream->SetBufferSize(DINDEX_PAGE_SIZE);
126 (*m_pFileStream) >> *this;
129 if(!m_pFileStream)
131 const OUString sError( m_pTable->getConnection()->getResources().getResourceStringWithSubstitution(
132 STR_COULD_NOT_LOAD_FILE,
133 "$filename$", sFile
134 ) );
135 ::dbtools::throwGenericSQLException( sError, *this );
139 std::unique_ptr<OIndexIterator> ODbaseIndex::createIterator()
141 openIndexFile();
142 return std::make_unique<OIndexIterator>(this);
145 bool ODbaseIndex::ConvertToKey(ONDXKey* rKey, sal_uInt32 nRec, const ORowSetValue& rValue)
147 OSL_ENSURE(m_pFileStream,"FileStream is not opened!");
148 // Search a specific value in Index
149 // If the Index is unique, the key doesn't matter
152 if (m_aHeader.db_keytype == 0)
154 *rKey = ONDXKey(rValue.getString(), nRec );
156 else
158 if (rValue.isNull())
159 *rKey = ONDXKey(rValue.getDouble(), DataType::DOUBLE, nRec );
160 else
161 *rKey = ONDXKey(rValue.getDouble(), nRec );
164 catch (Exception&)
166 OSL_ASSERT(false);
167 return false;
169 return true;
173 bool ODbaseIndex::Find(sal_uInt32 nRec, const ORowSetValue& rValue)
175 openIndexFile();
176 OSL_ENSURE(m_pFileStream,"FileStream is not opened!");
177 // Search a specific value in Index
178 // If the Index is unique, the key doesn't matter
179 ONDXKey aKey;
180 return ConvertToKey(&aKey, nRec, rValue) && getRoot()->Find(aKey);
184 bool ODbaseIndex::Insert(sal_uInt32 nRec, const ORowSetValue& rValue)
186 openIndexFile();
187 OSL_ENSURE(m_pFileStream,"FileStream is not opened!");
188 ONDXKey aKey;
190 // Does the value already exist
191 // Use Find() always to determine the actual leaf
192 if (!ConvertToKey(&aKey, nRec, rValue) || (getRoot()->Find(aKey) && isUnique()))
193 return false;
195 ONDXNode aNewNode(aKey);
197 // insert in the current leaf
198 if (!m_aCurLeaf.Is())
199 return false;
201 bool bResult = m_aCurLeaf->Insert(aNewNode);
202 Release(bResult);
204 return bResult;
208 bool ODbaseIndex::Update(sal_uInt32 nRec, const ORowSetValue& rOldValue,
209 const ORowSetValue& rNewValue)
211 openIndexFile();
212 OSL_ENSURE(m_pFileStream,"FileStream is not opened!");
213 ONDXKey aKey;
214 if (!ConvertToKey(&aKey, nRec, rNewValue) || (isUnique() && getRoot()->Find(aKey)))
215 return false;
216 else
217 return Delete(nRec, rOldValue) && Insert(nRec,rNewValue);
221 bool ODbaseIndex::Delete(sal_uInt32 nRec, const ORowSetValue& rValue)
223 openIndexFile();
224 OSL_ENSURE(m_pFileStream,"FileStream is not opened!");
225 // Does the value already exist
226 // Always use Find() to determine the actual leaf
227 ONDXKey aKey;
228 if (!ConvertToKey(&aKey, nRec, rValue) || !getRoot()->Find(aKey))
229 return false;
231 // insert in the current leaf
232 if (!m_aCurLeaf.Is())
233 return false;
234 #if OSL_DEBUG_LEVEL > 1
235 m_aRoot->PrintPage();
236 #endif
238 m_aCurLeaf->Delete(m_nCurNode);
239 return true;
242 void ODbaseIndex::Collect(ONDXPage* pPage)
244 if (pPage)
245 m_aCollector.push_back(pPage);
248 void ODbaseIndex::Release(bool bSave)
250 // Release the Index-resources
251 m_bUseCollector = false;
253 if (m_aCurLeaf.Is())
255 m_aCurLeaf->Release(bSave);
256 m_aCurLeaf.Clear();
259 // Release the root
260 if (m_aRoot.Is())
262 m_aRoot->Release(bSave);
263 m_aRoot.Clear();
265 // Release all references, before the FileStream will be closed
266 for (auto& i : m_aCollector)
267 i->QueryDelete();
269 m_aCollector.clear();
271 // Header modified?
272 if (bSave && (m_aHeader.db_rootpage != m_nRootPage ||
273 m_aHeader.db_pagecount != m_nPageCount))
275 m_aHeader.db_rootpage = m_nRootPage;
276 m_aHeader.db_pagecount = m_nPageCount;
277 WriteODbaseIndex( *m_pFileStream, *this );
279 m_nRootPage = m_nPageCount = 0;
280 m_nCurNode = NODE_NOTFOUND;
282 closeImpl();
285 void ODbaseIndex::closeImpl()
287 m_pFileStream.reset();
290 ONDXPage* ODbaseIndex::CreatePage(sal_uInt32 nPagePos, ONDXPage* pParent, bool bLoad)
292 OSL_ENSURE(m_pFileStream,"FileStream is not opened!");
294 ONDXPage* pPage;
295 if ( !m_aCollector.empty() )
297 pPage = *(m_aCollector.rbegin());
298 m_aCollector.pop_back();
299 pPage->SetPagePos(nPagePos);
300 pPage->SetParent(pParent);
302 else
303 pPage = new ONDXPage(*this, nPagePos, pParent);
305 if (bLoad)
306 (*m_pFileStream) >> *pPage;
308 return pPage;
311 void connectivity::dbase::ReadHeader(
312 SvStream & rStream, ODbaseIndex::NDXHeader & rHeader)
314 #if !defined(NDEBUG)
315 sal_uInt64 const nOldPos(rStream.Tell());
316 #endif
317 rStream.ReadUInt32(rHeader.db_rootpage);
318 rStream.ReadUInt32(rHeader.db_pagecount);
319 rStream.ReadBytes(&rHeader.db_free, 4);
320 rStream.ReadUInt16(rHeader.db_keylen);
321 rStream.ReadUInt16(rHeader.db_maxkeys);
322 rStream.ReadUInt16(rHeader.db_keytype);
323 rStream.ReadUInt16(rHeader.db_keyrec);
324 rStream.ReadBytes(&rHeader.db_free1, 3);
325 rStream.ReadUChar(rHeader.db_unique);
326 rStream.ReadBytes(&rHeader.db_name, 488);
327 assert(rStream.GetError() || rStream.Tell() == nOldPos + DINDEX_PAGE_SIZE);
330 SvStream& connectivity::dbase::operator >> (SvStream &rStream, ODbaseIndex& rIndex)
332 rStream.Seek(0);
333 ReadHeader(rStream, rIndex.m_aHeader);
335 rIndex.m_nRootPage = rIndex.m_aHeader.db_rootpage;
336 rIndex.m_nPageCount = rIndex.m_aHeader.db_pagecount;
337 return rStream;
340 SvStream& connectivity::dbase::WriteODbaseIndex(SvStream &rStream, const ODbaseIndex& rIndex)
342 rStream.Seek(0);
343 rStream.WriteUInt32(rIndex.m_aHeader.db_rootpage);
344 rStream.WriteUInt32(rIndex.m_aHeader.db_pagecount);
345 rStream.WriteBytes(&rIndex.m_aHeader.db_free, 4);
346 rStream.WriteUInt16(rIndex.m_aHeader.db_keylen);
347 rStream.WriteUInt16(rIndex.m_aHeader.db_maxkeys);
348 rStream.WriteUInt16(rIndex.m_aHeader.db_keytype);
349 rStream.WriteUInt16(rIndex.m_aHeader.db_keyrec);
350 rStream.WriteBytes(&rIndex.m_aHeader.db_free1, 3);
351 rStream.WriteUChar(rIndex.m_aHeader.db_unique);
352 rStream.WriteBytes(&rIndex.m_aHeader.db_name, 488);
353 assert(rStream.GetError() || rStream.Tell() == DINDEX_PAGE_SIZE);
354 SAL_WARN_IF(rStream.GetError(), "connectivity.dbase", "write error");
355 return rStream;
358 OUString ODbaseIndex::getCompletePath() const
360 OUString sDir = m_pTable->getConnection()->getURL() +
361 OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DELIMITER) +
362 m_Name + ".ndx";
363 return sDir;
366 void ODbaseIndex::createINFEntry()
368 // synchronize inf-file
369 const OUString sEntry(m_Name + ".ndx");
371 OUString sCfgFile(m_pTable->getConnection()->getURL() +
372 OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DELIMITER) +
373 m_pTable->getName() +
374 ".inf");
376 OUString sPhysicalPath;
377 osl::FileBase::getSystemPathFromFileURL(sCfgFile, sPhysicalPath);
379 Config aInfFile(sPhysicalPath);
380 aInfFile.SetGroup(dBASE_III_GROUP);
382 sal_uInt16 nSuffix = aInfFile.GetKeyCount();
383 OString aNewEntry,aKeyName;
384 bool bCase = isCaseSensitive();
385 while (aNewEntry.isEmpty())
387 aNewEntry = "NDX" + OString::number(++nSuffix);
388 for (sal_uInt16 i = 0; i < aInfFile.GetKeyCount(); i++)
390 aKeyName = aInfFile.GetKeyName(i);
391 if (bCase ? aKeyName == aNewEntry : aKeyName.equalsIgnoreAsciiCase(aNewEntry))
393 aNewEntry.clear();
394 break;
398 aInfFile.WriteKey(aNewEntry, OUStringToOString(sEntry, m_pTable->getConnection()->getTextEncoding()));
401 void ODbaseIndex::DropImpl()
403 closeImpl();
405 OUString sPath = getCompletePath();
406 if(UCBContentHelper::Exists(sPath))
408 if(!UCBContentHelper::Kill(sPath))
409 m_pTable->getConnection()->throwGenericSQLException(STR_COULD_NOT_DELETE_INDEX,*m_pTable);
412 // synchronize inf-file
413 OUString sCfgFile = m_pTable->getConnection()->getURL() +
414 OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DELIMITER) +
415 m_pTable->getName() + ".inf";
417 OUString sPhysicalPath;
418 OSL_VERIFY( osl::FileBase::getSystemPathFromFileURL(sCfgFile, sPhysicalPath)
419 == osl::FileBase::E_None );
421 Config aInfFile(sPhysicalPath);
422 aInfFile.SetGroup(dBASE_III_GROUP);
423 sal_uInt16 nKeyCnt = aInfFile.GetKeyCount();
424 OString aKeyName;
425 OUString sEntry = m_Name + ".ndx";
427 // delete entries from the inf file
428 for (sal_uInt16 nKey = 0; nKey < nKeyCnt; nKey++)
430 // References the Key to an Index-file?
431 aKeyName = aInfFile.GetKeyName( nKey );
432 if (aKeyName.startsWith("NDX"))
434 if(sEntry == OStringToOUString(aInfFile.ReadKey(aKeyName),m_pTable->getConnection()->getTextEncoding()))
436 aInfFile.DeleteKey(aKeyName);
437 break;
443 void ODbaseIndex::impl_killFileAndthrowError_throw(TranslateId pErrorId, const OUString& _sFile)
445 closeImpl();
446 if(UCBContentHelper::Exists(_sFile))
447 UCBContentHelper::Kill(_sFile);
448 m_pTable->getConnection()->throwGenericSQLException(pErrorId, *this);
451 void ODbaseIndex::CreateImpl()
453 // Create the Index
454 const OUString sFile = getCompletePath();
455 if(UCBContentHelper::Exists(sFile))
457 const OUString sError( m_pTable->getConnection()->getResources().getResourceStringWithSubstitution(
458 STR_COULD_NOT_CREATE_INDEX_NAME,
459 "$filename$", sFile
460 ) );
461 ::dbtools::throwGenericSQLException( sError, *this );
463 // Index comprises only one column
464 if (m_pColumns->getCount() > 1)
465 m_pTable->getConnection()->throwGenericSQLException(STR_ONL_ONE_COLUMN_PER_INDEX,*this);
467 Reference<XFastPropertySet> xCol(m_pColumns->getByIndex(0),UNO_QUERY);
469 // Is the column already indexed?
470 if ( !xCol.is() )
471 ::dbtools::throwFunctionSequenceException(*this);
473 // create the index file
474 m_pFileStream = OFileTable::createStream_simpleError(sFile,StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE | StreamMode::TRUNC);
475 if (!m_pFileStream)
477 const OUString sError( m_pTable->getConnection()->getResources().getResourceStringWithSubstitution(
478 STR_COULD_NOT_LOAD_FILE,
479 "$filename$", sFile
480 ) );
481 ::dbtools::throwGenericSQLException( sError, *this );
484 m_pFileStream->SetEndian(SvStreamEndian::LITTLE);
485 m_pFileStream->SetBufferSize(DINDEX_PAGE_SIZE);
487 // firstly the result must be sorted
488 utl::SharedUNOComponent<XStatement> xStmt;
489 utl::SharedUNOComponent<XResultSet> xSet;
490 OUString aName;
493 xStmt.set( m_pTable->getConnection()->createStatement(), UNO_SET_THROW);
495 aName = getString(xCol->getFastPropertyValue(PROPERTY_ID_NAME));
497 const OUString aQuote(m_pTable->getConnection()->getMetaData()->getIdentifierQuoteString());
498 OUString aStatement( "SELECT " + aQuote + aName + aQuote +" FROM " + aQuote + m_pTable->getName() + aQuote + " ORDER BY " + aQuote + aName + aQuote);
500 xSet.set( xStmt->executeQuery(aStatement),UNO_SET_THROW );
502 catch(const Exception& )
504 impl_killFileAndthrowError_throw(STR_COULD_NOT_CREATE_INDEX,sFile);
506 if (!xSet.is())
508 impl_killFileAndthrowError_throw(STR_COULD_NOT_CREATE_INDEX,sFile);
511 // Set the header info
512 memset(&m_aHeader,0,sizeof(m_aHeader));
513 sal_Int32 nType = 0;
514 ::rtl::Reference<OSQLColumns> aCols = m_pTable->getTableColumns();
515 const Reference< XPropertySet > xTableCol(*find(aCols->begin(),aCols->end(),aName,::comphelper::UStringMixEqual(isCaseSensitive())));
517 xTableCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)) >>= nType;
519 m_aHeader.db_keytype = (nType == DataType::VARCHAR || nType == DataType::CHAR) ? 0 : 1;
520 m_aHeader.db_keylen = (m_aHeader.db_keytype) ? 8 : static_cast<sal_uInt16>(getINT32(xTableCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION))));
521 m_aHeader.db_keylen = (( m_aHeader.db_keylen - 1) / 4 + 1) * 4;
522 m_aHeader.db_maxkeys = (DINDEX_PAGE_SIZE - 4) / (8 + m_aHeader.db_keylen);
523 if ( m_aHeader.db_maxkeys < 3 )
525 impl_killFileAndthrowError_throw(STR_COULD_NOT_CREATE_INDEX_KEYSIZE,sFile);
528 m_pFileStream->SetStreamSize(DINDEX_PAGE_SIZE);
530 OString aCol(OUStringToOString(aName, m_pTable->getConnection()->getTextEncoding()));
531 strncpy(m_aHeader.db_name, aCol.getStr(), std::min<size_t>(sizeof(m_aHeader.db_name), aCol.getLength()));
532 m_aHeader.db_unique = m_IsUnique ? 1: 0;
533 m_aHeader.db_keyrec = m_aHeader.db_keylen + 8;
535 // modifications of the header are detected by differences between
536 // the HeaderInfo and nRootPage or nPageCount respectively
537 m_nRootPage = 1;
538 m_nPageCount = 2;
540 m_aCurLeaf = m_aRoot = CreatePage(m_nRootPage);
541 m_aRoot->SetModified(true);
543 m_bUseCollector = true;
545 sal_Int32 nRowsLeft = 0;
546 Reference<XRow> xRow(xSet,UNO_QUERY);
548 if(xSet->last())
550 ODbaseResultSet* pDbaseRes = dynamic_cast<ODbaseResultSet*>(xSet.getTyped().get());
551 assert(pDbaseRes); //"No dbase resultset found? What's going on here!
552 nRowsLeft = xSet->getRow();
554 xSet->beforeFirst();
555 ONDXKey aKey(ORowSetValue(), nType, 0);
556 ONDXKey aInsertKey(ORowSetValue(), nType, 0);
557 // Create the index structure
558 while (xSet->next())
560 ORowSetValue aValue(m_aHeader.db_keytype ? ORowSetValue(xRow->getDouble(1)) : ORowSetValue(xRow->getString(1)));
561 // checking for duplicate entries
562 if (m_IsUnique && m_nCurNode != NODE_NOTFOUND)
564 aKey.setValue(aValue);
565 if (aKey == (*m_aCurLeaf)[m_nCurNode].GetKey())
567 impl_killFileAndthrowError_throw(STR_COULD_NOT_CREATE_INDEX_NOT_UNIQUE,sFile);
570 aInsertKey.setValue(aValue);
571 aInsertKey.setRecord(pDbaseRes->getCurrentFilePos());
573 ONDXNode aNewNode(aInsertKey);
574 if (!m_aCurLeaf->Insert(aNewNode, --nRowsLeft))
575 break;
579 if(nRowsLeft)
581 impl_killFileAndthrowError_throw(STR_COULD_NOT_CREATE_INDEX,sFile);
583 Release();
584 createINFEntry();
588 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */