Version 4.3.0.0.beta1, tag libreoffice-4.3.0.0.beta1
[LibreOffice.git] / connectivity / source / drivers / dbase / DIndex.cxx
blob05a8088e1652a8772ba82092d383a8094f1b3fcb
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 <com/sun/star/lang/DisposedException.hpp>
23 #include "connectivity/sdbcx/VColumn.hxx"
24 #include <comphelper/sequence.hxx>
25 #include "dbase/DTable.hxx"
26 #include "dbase/DIndexIter.hxx"
27 #include <tools/config.hxx>
28 #include "connectivity/CommonTools.hxx"
29 #include <com/sun/star/sdbc/XResultSetMetaData.hpp>
30 #include <com/sun/star/sdbc/XResultSet.hpp>
31 #include <com/sun/star/sdbcx/XRowLocate.hpp>
32 #include <com/sun/star/sdbc/XRow.hpp>
33 #include <comphelper/extract.hxx>
34 #include <unotools/localfilehelper.hxx>
35 #include <unotools/ucbhelper.hxx>
36 #include <comphelper/types.hxx>
37 #include <connectivity/dbexception.hxx>
38 #include "dbase/DResultSet.hxx"
39 #include "diagnose_ex.h"
40 #include "resource/dbase_res.hrc"
41 #include <unotools/sharedunocomponent.hxx>
43 using namespace ::comphelper;
45 using namespace connectivity;
46 using namespace utl;
47 using namespace ::cppu;
48 using namespace connectivity::file;
49 using namespace connectivity::sdbcx;
50 using namespace connectivity::dbase;
51 using namespace com::sun::star::sdbc;
52 using namespace com::sun::star::sdbcx;
53 using namespace com::sun::star::uno;
54 using namespace com::sun::star::beans;
55 using namespace com::sun::star::lang;
57 IMPLEMENT_SERVICE_INFO(ODbaseIndex,"com.sun.star.sdbcx.driver.dbase.Index","com.sun.star.sdbcx.Index");
59 ODbaseIndex::ODbaseIndex(ODbaseTable* _pTable)
60 : OIndex(true/*_pTable->getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers()*/)
61 , m_pFileStream(NULL)
62 , m_nCurNode(NODE_NOTFOUND)
63 , m_nPageCount(0)
64 , m_nRootPage(0)
65 , m_pTable(_pTable)
66 , m_bUseCollector(false)
68 memset(&m_aHeader, 0, sizeof(m_aHeader));
69 construct();
72 ODbaseIndex::ODbaseIndex( ODbaseTable* _pTable,
73 const NDXHeader& _rHeader,
74 const OUString& _rName)
75 : OIndex(_rName, OUString(), _rHeader.db_unique, false, false, true)
76 , m_pFileStream(NULL)
77 , m_aHeader(_rHeader)
78 , m_nCurNode(NODE_NOTFOUND)
79 , m_nPageCount(0)
80 , m_nRootPage(0)
81 , m_pTable(_pTable)
82 , m_bUseCollector(false)
84 construct();
87 ODbaseIndex::~ODbaseIndex()
89 closeImpl();
92 void ODbaseIndex::refreshColumns()
94 ::osl::MutexGuard aGuard( m_aMutex );
96 TStringVector aVector;
97 if(!isNew())
99 OSL_ENSURE(m_pFileStream,"FileStream is not opened!");
100 OSL_ENSURE(m_aHeader.db_name[0] != '\0',"Invalid name for the column!");
101 aVector.push_back(OUString::createFromAscii(m_aHeader.db_name));
104 if(m_pColumns)
105 m_pColumns->reFill(aVector);
106 else
107 m_pColumns = new ODbaseIndexColumns(this,m_aMutex,aVector);
110 Sequence< sal_Int8 > ODbaseIndex::getUnoTunnelImplementationId()
112 static ::cppu::OImplementationId * pId = 0;
113 if (! pId)
115 ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
116 if (! pId)
118 static ::cppu::OImplementationId aId;
119 pId = &aId;
122 return pId->getImplementationId();
125 // XUnoTunnel
127 sal_Int64 ODbaseIndex::getSomething( const Sequence< sal_Int8 > & rId ) throw (RuntimeException, std::exception)
129 return (rId.getLength() == 16 && 0 == memcmp(getUnoTunnelImplementationId().getConstArray(), rId.getConstArray(), 16 ) )
130 ? reinterpret_cast< sal_Int64 >( this )
131 : ODbaseIndex_BASE::getSomething(rId);
134 ONDXPagePtr ODbaseIndex::getRoot()
136 openIndexFile();
137 if (!m_aRoot.Is())
139 m_nRootPage = m_aHeader.db_rootpage;
140 m_nPageCount = m_aHeader.db_pagecount;
141 m_aRoot = CreatePage(m_nRootPage,NULL,true);
143 return m_aRoot;
146 bool ODbaseIndex::openIndexFile()
148 if(!m_pFileStream)
150 OUString sFile = getCompletePath();
151 if(UCBContentHelper::Exists(sFile))
153 m_pFileStream = OFileTable::createStream_simpleError(sFile, STREAM_READWRITE | STREAM_NOCREATE | STREAM_SHARE_DENYWRITE);
154 if (!m_pFileStream)
155 m_pFileStream = OFileTable::createStream_simpleError(sFile,STREAM_READ | STREAM_NOCREATE | STREAM_SHARE_DENYNONE);
156 if(m_pFileStream)
158 m_pFileStream->SetNumberFormatInt(NUMBERFORMAT_INT_LITTLEENDIAN);
159 m_pFileStream->SetBufferSize(DINDEX_PAGE_SIZE);
160 (*m_pFileStream) >> *this;
163 if(!m_pFileStream)
165 const OUString sError( m_pTable->getConnection()->getResources().getResourceStringWithSubstitution(
166 STR_COULD_NOT_LOAD_FILE,
167 "$filename$", sFile
168 ) );
169 ::dbtools::throwGenericSQLException( sError, *this );
173 return m_pFileStream != NULL;
176 OIndexIterator* ODbaseIndex::createIterator(OBoolOperator* pOp,
177 const OOperand* pOperand)
179 openIndexFile();
180 return new OIndexIterator(this, pOp, pOperand);
183 bool ODbaseIndex::ConvertToKey(ONDXKey* rKey, sal_uInt32 nRec, const ORowSetValue& rValue)
185 OSL_ENSURE(m_pFileStream,"FileStream is not opened!");
186 // Search a specific value in Index
187 // If the Index is unique, the key doesn't matter
190 if (m_aHeader.db_keytype == 0)
192 *rKey = ONDXKey(rValue.getString(), nRec );
194 else
196 if (rValue.isNull())
197 *rKey = ONDXKey(rValue.getDouble(), DataType::DOUBLE, nRec );
198 else
199 *rKey = ONDXKey(rValue.getDouble(), nRec );
202 catch (Exception&)
204 OSL_ASSERT(false);
205 return false;
207 return true;
211 bool ODbaseIndex::Find(sal_uInt32 nRec, const ORowSetValue& rValue)
213 openIndexFile();
214 OSL_ENSURE(m_pFileStream,"FileStream is not opened!");
215 // Search a specific value in Index
216 // If the Index is unique, the key doesn't matter
217 ONDXKey aKey;
218 return ConvertToKey(&aKey, nRec, rValue) && getRoot()->Find(aKey);
222 bool ODbaseIndex::Insert(sal_uInt32 nRec, const ORowSetValue& rValue)
224 openIndexFile();
225 OSL_ENSURE(m_pFileStream,"FileStream is not opened!");
226 ONDXKey aKey;
228 // Does the value already exist
229 // Use Find() always to determine the actual leaf
230 if (!ConvertToKey(&aKey, nRec, rValue) || (getRoot()->Find(aKey) && isUnique()))
231 return false;
233 ONDXNode aNewNode(aKey);
235 // insert in the current leaf
236 if (!m_aCurLeaf.Is())
237 return false;
239 bool bResult = m_aCurLeaf->Insert(aNewNode);
240 Release(bResult);
242 return bResult;
246 bool ODbaseIndex::Update(sal_uInt32 nRec, const ORowSetValue& rOldValue,
247 const ORowSetValue& rNewValue)
249 openIndexFile();
250 OSL_ENSURE(m_pFileStream,"FileStream is not opened!");
251 ONDXKey aKey;
252 if (!ConvertToKey(&aKey, nRec, rNewValue) || (isUnique() && getRoot()->Find(aKey)))
253 return false;
254 else
255 return Delete(nRec, rOldValue) && Insert(nRec,rNewValue);
259 bool ODbaseIndex::Delete(sal_uInt32 nRec, const ORowSetValue& rValue)
261 openIndexFile();
262 OSL_ENSURE(m_pFileStream,"FileStream is not opened!");
263 // Does the value already exist
264 // Always use Find() to determine the actual leaf
265 ONDXKey aKey;
266 if (!ConvertToKey(&aKey, nRec, rValue) || !getRoot()->Find(aKey))
267 return false;
269 ONDXNode aNewNode(aKey);
271 // insert in the current leaf
272 if (!m_aCurLeaf.Is())
273 return false;
274 #if OSL_DEBUG_LEVEL > 1
275 m_aRoot->PrintPage();
276 #endif
278 return m_aCurLeaf->Delete(m_nCurNode);
281 void ODbaseIndex::Collect(ONDXPage* pPage)
283 if (pPage)
284 m_aCollector.push_back(pPage);
287 void ODbaseIndex::Release(bool bSave)
289 // Release the Index-recources
290 m_bUseCollector = false;
292 if (m_aCurLeaf.Is())
294 m_aCurLeaf->Release(bSave);
295 m_aCurLeaf.Clear();
298 // Release the root
299 if (m_aRoot.Is())
301 m_aRoot->Release(bSave);
302 m_aRoot.Clear();
304 // Release all references, before the FileStream will be closed
305 for (sal_uIntPtr i = 0; i < m_aCollector.size(); i++)
306 m_aCollector[i]->QueryDelete();
308 m_aCollector.clear();
310 // Header modified?
311 if (bSave && (m_aHeader.db_rootpage != m_nRootPage ||
312 m_aHeader.db_pagecount != m_nPageCount))
314 m_aHeader.db_rootpage = m_nRootPage;
315 m_aHeader.db_pagecount = m_nPageCount;
316 WriteODbaseIndex( *m_pFileStream, *this );
318 m_nRootPage = m_nPageCount = 0;
319 m_nCurNode = NODE_NOTFOUND;
321 closeImpl();
324 void ODbaseIndex::closeImpl()
326 if(m_pFileStream)
328 delete m_pFileStream;
329 m_pFileStream = NULL;
333 ONDXPage* ODbaseIndex::CreatePage(sal_uInt32 nPagePos, ONDXPage* pParent, bool bLoad)
335 OSL_ENSURE(m_pFileStream,"FileStream is not opened!");
337 ONDXPage* pPage;
338 if ( !m_aCollector.empty() )
340 pPage = *(m_aCollector.rbegin());
341 m_aCollector.pop_back();
342 pPage->SetPagePos(nPagePos);
343 pPage->SetParent(pParent);
345 else
346 pPage = new ONDXPage(*this, nPagePos, pParent);
348 if (bLoad)
349 (*m_pFileStream) >> *pPage;
351 return pPage;
355 SvStream& connectivity::dbase::operator >> (SvStream &rStream, ODbaseIndex& rIndex)
357 rStream.Seek(0);
358 rStream.Read(&rIndex.m_aHeader,DINDEX_PAGE_SIZE);
360 rIndex.m_nRootPage = rIndex.m_aHeader.db_rootpage;
361 rIndex.m_nPageCount = rIndex.m_aHeader.db_pagecount;
362 return rStream;
365 SvStream& connectivity::dbase::WriteODbaseIndex(SvStream &rStream, ODbaseIndex& rIndex)
367 rStream.Seek(0);
368 OSL_VERIFY_EQUALS( rStream.Write(&rIndex.m_aHeader,DINDEX_PAGE_SIZE), DINDEX_PAGE_SIZE, "Write not successful: Wrong header size for dbase index!");
369 return rStream;
372 OUString ODbaseIndex::getCompletePath()
374 OUString sDir = m_pTable->getConnection()->getURL() +
375 OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DELIMITER) +
376 m_Name + ".ndx";
377 return sDir;
380 void ODbaseIndex::createINFEntry()
382 // synchronize inf-file
383 const OUString sEntry(m_Name + ".ndx");
385 OUString sCfgFile(m_pTable->getConnection()->getURL() +
386 OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DELIMITER) +
387 m_pTable->getName() +
388 ".inf");
390 OUString sPhysicalPath;
391 LocalFileHelper::ConvertURLToPhysicalName(sCfgFile, sPhysicalPath);
393 Config aInfFile(sPhysicalPath);
394 aInfFile.SetGroup(dBASE_III_GROUP);
396 sal_uInt16 nSuffix = aInfFile.GetKeyCount();
397 OString aNewEntry,aKeyName;
398 bool bCase = isCaseSensitive();
399 while (aNewEntry.isEmpty())
401 aNewEntry = OString("NDX");
402 aNewEntry += OString::number(++nSuffix);
403 for (sal_uInt16 i = 0; i < aInfFile.GetKeyCount(); i++)
405 aKeyName = aInfFile.GetKeyName(i);
406 if (bCase ? aKeyName.equals(aNewEntry) : aKeyName.equalsIgnoreAsciiCase(aNewEntry))
408 aNewEntry = OString();
409 break;
413 aInfFile.WriteKey(aNewEntry, OUStringToOString(sEntry, m_pTable->getConnection()->getTextEncoding()));
416 bool ODbaseIndex::DropImpl()
418 closeImpl();
420 OUString sPath = getCompletePath();
421 if(UCBContentHelper::Exists(sPath))
423 if(!UCBContentHelper::Kill(sPath))
424 m_pTable->getConnection()->throwGenericSQLException(STR_COULD_NOT_DELETE_INDEX,*m_pTable);
427 // synchronize inf-file
428 OUString sCfgFile = m_pTable->getConnection()->getURL() +
429 OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DELIMITER) +
430 m_pTable->getName() + ".inf";
432 OUString sPhysicalPath;
433 OSL_VERIFY_RES( LocalFileHelper::ConvertURLToPhysicalName(sCfgFile, sPhysicalPath),
434 "Can not convert Config Filename into Physical Name!");
436 Config aInfFile(sPhysicalPath);
437 aInfFile.SetGroup(dBASE_III_GROUP);
438 sal_uInt16 nKeyCnt = aInfFile.GetKeyCount();
439 OString aKeyName;
440 OUString sEntry = m_Name + ".ndx";
442 // delete entries from the inf file
443 for (sal_uInt16 nKey = 0; nKey < nKeyCnt; nKey++)
445 // References the Key to an Index-file?
446 aKeyName = aInfFile.GetKeyName( nKey );
447 if (aKeyName.copy(0,3) == "NDX")
449 if(sEntry == OStringToOUString(aInfFile.ReadKey(aKeyName),m_pTable->getConnection()->getTextEncoding()))
451 aInfFile.DeleteKey(aKeyName);
452 break;
456 return true;
459 void ODbaseIndex::impl_killFileAndthrowError_throw(sal_uInt16 _nErrorId,const OUString& _sFile)
461 closeImpl();
462 if(UCBContentHelper::Exists(_sFile))
463 UCBContentHelper::Kill(_sFile);
464 m_pTable->getConnection()->throwGenericSQLException(_nErrorId,*this);
467 bool ODbaseIndex::CreateImpl()
469 // Create the Index
470 const OUString sFile = getCompletePath();
471 if(UCBContentHelper::Exists(sFile))
473 const OUString sError( m_pTable->getConnection()->getResources().getResourceStringWithSubstitution(
474 STR_COULD_NOT_CREATE_INDEX_NAME,
475 "$filename$", sFile
476 ) );
477 ::dbtools::throwGenericSQLException( sError, *this );
479 // Index comprises only one column
480 if (m_pColumns->getCount() > 1)
481 m_pTable->getConnection()->throwGenericSQLException(STR_ONL_ONE_COLUMN_PER_INDEX,*this);
483 Reference<XFastPropertySet> xCol(m_pColumns->getByIndex(0),UNO_QUERY);
485 // Is the column already indexed?
486 if ( !xCol.is() )
487 ::dbtools::throwFunctionSequenceException(*this);
489 // create the index file
490 m_pFileStream = OFileTable::createStream_simpleError(sFile,STREAM_READWRITE | STREAM_SHARE_DENYWRITE | STREAM_TRUNC);
491 if (!m_pFileStream)
493 const OUString sError( m_pTable->getConnection()->getResources().getResourceStringWithSubstitution(
494 STR_COULD_NOT_LOAD_FILE,
495 "$filename$", sFile
496 ) );
497 ::dbtools::throwGenericSQLException( sError, *this );
500 m_pFileStream->SetNumberFormatInt(NUMBERFORMAT_INT_LITTLEENDIAN);
501 m_pFileStream->SetBufferSize(DINDEX_PAGE_SIZE);
503 // firstly the result must be sorted
504 utl::SharedUNOComponent<XStatement> xStmt;
505 utl::SharedUNOComponent<XResultSet> xSet;
506 OUString aName;
509 xStmt.set( m_pTable->getConnection()->createStatement(), UNO_SET_THROW);
511 aName = getString(xCol->getFastPropertyValue(PROPERTY_ID_NAME));
513 const OUString aQuote(m_pTable->getConnection()->getMetaData()->getIdentifierQuoteString());
514 OUString aStatement( "SELECT " + aQuote + aName + aQuote +" FROM " + aQuote + m_pTable->getName() + aQuote + " ORDER BY " + aQuote + aName + aQuote);
516 xSet.set( xStmt->executeQuery(aStatement),UNO_SET_THROW );
518 catch(const Exception& )
520 impl_killFileAndthrowError_throw(STR_COULD_NOT_CREATE_INDEX,sFile);
522 if (!xSet.is())
524 impl_killFileAndthrowError_throw(STR_COULD_NOT_CREATE_INDEX,sFile);
527 // Set the header info
528 memset(&m_aHeader,0,sizeof(m_aHeader));
529 sal_Int32 nType = 0;
530 ::rtl::Reference<OSQLColumns> aCols = m_pTable->getTableColumns();
531 const Reference< XPropertySet > xTableCol(*find(aCols->get().begin(),aCols->get().end(),aName,::comphelper::UStringMixEqual(isCaseSensitive())));
533 xTableCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)) >>= nType;
535 m_aHeader.db_keytype = (nType == DataType::VARCHAR || nType == DataType::CHAR) ? 0 : 1;
536 m_aHeader.db_keylen = (m_aHeader.db_keytype) ? 8 : (sal_uInt16)getINT32(xTableCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION)));
537 m_aHeader.db_keylen = (( m_aHeader.db_keylen - 1) / 4 + 1) * 4;
538 m_aHeader.db_maxkeys = (DINDEX_PAGE_SIZE - 4) / (8 + m_aHeader.db_keylen);
539 if ( m_aHeader.db_maxkeys < 3 )
541 impl_killFileAndthrowError_throw(STR_COULD_NOT_CREATE_INDEX_KEYSIZE,sFile);
544 m_pFileStream->SetStreamSize(DINDEX_PAGE_SIZE);
546 OString aCol(OUStringToOString(aName, m_pTable->getConnection()->getTextEncoding()));
547 strncpy(m_aHeader.db_name, aCol.getStr(), std::min<size_t>(sizeof(m_aHeader.db_name), aCol.getLength()));
548 m_aHeader.db_unique = m_IsUnique ? 1: 0;
549 m_aHeader.db_keyrec = m_aHeader.db_keylen + 8;
551 // modifications of the header are detected by differences between
552 // the HeaderInfo and nRootPage or nPageCount respectively
553 m_nRootPage = 1;
554 m_nPageCount = 2;
556 m_aCurLeaf = m_aRoot = CreatePage(m_nRootPage);
557 m_aRoot->SetModified(true);
559 m_bUseCollector = true;
561 sal_Int32 nRowsLeft = 0;
562 Reference<XRow> xRow(xSet,UNO_QUERY);
564 if(xSet->last())
566 Reference< XUnoTunnel> xTunnel(xSet, UNO_QUERY_THROW);
567 ODbaseResultSet* pDbaseRes = reinterpret_cast< ODbaseResultSet* >( xTunnel->getSomething(ODbaseResultSet::getUnoTunnelImplementationId()) );
568 assert(pDbaseRes); //"No dbase resultset found? What's going on here!
569 Reference<XRowLocate> xRowLocate(xSet,UNO_QUERY);
570 nRowsLeft = xSet->getRow();
572 xSet->beforeFirst();
573 ORowSetValue atmpValue=ORowSetValue();
574 ONDXKey aKey(atmpValue, nType, 0);
575 ONDXKey aInsertKey(atmpValue, nType, 0);
576 // Create the index structure
577 while (xSet->next())
579 ORowSetValue aValue(m_aHeader.db_keytype ? ORowSetValue(xRow->getDouble(1)) : ORowSetValue(xRow->getString(1)));
580 // checking for duplicate entries
581 if (m_IsUnique && m_nCurNode != NODE_NOTFOUND)
583 aKey.setValue(aValue);
584 if (aKey == (*m_aCurLeaf)[m_nCurNode].GetKey())
586 impl_killFileAndthrowError_throw(STR_COULD_NOT_CREATE_INDEX_NOT_UNIQUE,sFile);
589 aInsertKey.setValue(aValue);
590 aInsertKey.setRecord(pDbaseRes->getCurrentFilePos());
592 ONDXNode aNewNode(aInsertKey);
593 if (!m_aCurLeaf->Insert(aNewNode, --nRowsLeft))
594 break;
598 if(nRowsLeft)
600 impl_killFileAndthrowError_throw(STR_COULD_NOT_CREATE_INDEX,sFile);
602 Release();
603 createINFEntry();
604 return true;
608 void SAL_CALL ODbaseIndex::acquire() throw()
610 ODbaseIndex_BASE::acquire();
613 void SAL_CALL ODbaseIndex::release() throw()
615 ODbaseIndex_BASE::release();
621 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */