/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * 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/. * * This file incorporates work covered by the following license notice: * * 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 .
*/
void ODbaseTable::readHeader()
{
OSL_ENSURE(m_pFileStream,"No Stream available!"); if(!m_pFileStream) return;
m_pFileStream->RefreshBuffer(); // Make sure, that the header information actually is read again
m_pFileStream->Seek(STREAM_SEEK_TO_BEGIN);
if ( ( ( m_aHeader.headerLength - 1 ) / 32 - 1 ) <= 0 ) // number of fields
{ // no dBASE file
throwInvalidDbaseFormat();
} else
{ // Consistency check of the header:
m_aHeader.type = static_cast<DBFType>(nType); switch (m_aHeader.type)
{ case dBaseIII: case dBaseIV: case dBaseV: case VisualFoxPro: case VisualFoxProAuto: case dBaseFS: case dBaseFSMemo: case dBaseIVMemoSQL: case dBaseIIIMemo: case FoxProMemo:
m_pFileStream->SetEndian(SvStreamEndian::LITTLE); if( getConnection()->isTextEncodingDefaulted() &&
!dbfDecodeCharset(m_eEncoding, nType, m_aHeader.trailer[17]))
{
m_eEncoding = RTL_TEXTENCODING_IBM_850;
} break; case dBaseIVMemo:
m_pFileStream->SetEndian(SvStreamEndian::LITTLE); break; default:
{
throwInvalidDbaseFormat();
}
}
}
}
void ODbaseTable::fillColumns()
{
m_pFileStream->Seek(STREAM_SEEK_TO_BEGIN); if (!checkSeek(*m_pFileStream, 32))
{
SAL_WARN("connectivity.drivers", "ODbaseTable::fillColumns: bad offset!"); return;
}
if(!m_aColumns.is())
m_aColumns = new OSQLColumns(); else
m_aColumns->clear();
sal_Int32 i = 0; for (; i < nFieldCount; i++)
{
DBFColumn aDBFColumn;
m_pFileStream->ReadBytes(aDBFColumn.db_fnm, 11);
m_pFileStream->ReadUChar(aDBFColumn.db_typ);
m_pFileStream->ReadUInt32(aDBFColumn.db_adr);
m_pFileStream->ReadUChar(aDBFColumn.db_flng);
m_pFileStream->ReadUChar(aDBFColumn.db_dez);
m_pFileStream->ReadBytes(aDBFColumn.db_free2, 14); if (!m_pFileStream->good())
{
SAL_WARN("connectivity.drivers", "ODbaseTable::fillColumns: short read!"); break;
} if ( FIELD_DESCRIPTOR_TERMINATOR == aDBFColumn.db_fnm[0] ) // 0x0D stored as the Field Descriptor terminator. break;
OSL_ENSURE( m_pConnection->matchesExtension( aURL.getExtension() ), "ODbaseTable::ODbaseTable: invalid extension!"); // getEntry is expected to ensure the correct file name
// If the memo file isn't found, the data will be displayed anyhow. // However, updates can't be done // but the operation is executed
m_pMemoStream = createStream_simpleError( aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::READWRITE | StreamMode::NOCREATE | StreamMode::SHARE_DENYWRITE); if ( !m_pMemoStream )
{
m_pMemoStream = createStream_simpleError( aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::READ | StreamMode::NOCREATE | StreamMode::SHARE_DENYNONE);
} if (m_pMemoStream)
ReadMemoHeader();
}
if (m_pMemoStream)
{ // set the buffer exactly to the length of a record
nFileSize = m_pMemoStream->TellEnd();
m_pMemoStream->Seek(STREAM_SEEK_TO_BEGIN);
void ODbaseTable::ReadMemoHeader()
{
m_pMemoStream->SetEndian(SvStreamEndian::LITTLE);
m_pMemoStream->RefreshBuffer(); // make sure that the header information is actually read again
m_pMemoStream->Seek(0);
(*m_pMemoStream).ReadUInt32( m_aMemoHeader.db_next ); switch (m_aHeader.type)
{ case dBaseIIIMemo: // dBase III: fixed block size case dBaseIVMemo: // sometimes dBase3 is attached to dBase4 memo
m_pMemoStream->Seek(20);
(*m_pMemoStream).ReadUInt16( m_aMemoHeader.db_size ); if (m_aMemoHeader.db_size > 1 && m_aMemoHeader.db_size != 512) // 1 is also for dBase 3
m_aMemoHeader.db_typ = MemodBaseIV; elseif (m_aMemoHeader.db_size == 512)
{ // There are files using size specification, though they are dBase-files char sHeader[4];
m_pMemoStream->Seek(m_aMemoHeader.db_size);
m_pMemoStream->ReadBytes(sHeader, 4);
switch(nType)
{ case DataType::INTEGER: case DataType::DOUBLE: case DataType::TIMESTAMP: case DataType::DATE: case DataType::BIT: case DataType::LONGVARCHAR: case DataType::LONGVARBINARY:
nLen = m_aRealFieldLengths[i-1]; break; case DataType::DECIMAL:
nLen = SvDbaseConverter::ConvertPrecisionToDbase(nLen,m_aScales[i-1]); break; // the sign and the comma
case DataType::BINARY: case DataType::OTHER:
nByteOffset += nLen; continue;
}
// Is the variable bound? if ( !(*_rRow)[i]->isBound() )
{ // No - next field.
nByteOffset += nLen;
OSL_ENSURE( nByteOffset <= m_nBufferSize ,"ByteOffset > m_nBufferSize!"); continue;
} // if ( !(_rRow->get())[i]->isBound() ) if ( ( nByteOffset + nLen) > m_nBufferSize ) break; // length doesn't match buffer size.
if (nType == DataType::CHAR || nType == DataType::VARCHAR)
{
sal_Int32 nLastPos = -1; for (sal_Int32 k = 0; k < nLen; ++k)
{ if (pData[k] != ' ') // Record last non-empty position.
nLastPos = k;
} if (nLastPos < 0)
{ // Empty string. Skip it.
(*_rRow)[i]->setNull();
} else
{ // Commit the string
*(*_rRow)[i] = OUString(pData, static_cast<sal_Int32>(nLastPos+1), m_eEncoding);
}
} // if (nType == DataType::CHAR || nType == DataType::VARCHAR) elseif ( DataType::TIMESTAMP == nType )
{
sal_Int32 nDate = 0,nTime = 0; if (o3tl::make_unsigned(nLen) < 8)
{
SAL_WARN("connectivity.drivers", "short TIMESTAMP"); returnfalse;
}
memcpy(&nDate, pData, 4);
memcpy(&nTime, pData + 4, 4); if ( !nDate && !nTime )
{
(*_rRow)[i]->setNull();
} else
{
css::util::DateTime aDateTime;
lcl_CalDate(nDate,nTime,aDateTime);
*(*_rRow)[i] = aDateTime;
}
} elseif ( DataType::INTEGER == nType )
{
sal_Int32 nValue = 0; if (o3tl::make_unsigned(nLen) > sizeof(nValue)) returnfalse;
memcpy(&nValue, pData, nLen);
*(*_rRow)[i] = nValue;
} elseif ( DataType::DOUBLE == nType )
{ double d = 0.0; if (getBOOL((*aIter)->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISCURRENCY)))) // Currency is treated separately
{
sal_Int64 nValue = 0; if (o3tl::make_unsigned(nLen) > sizeof(nValue)) returnfalse;
memcpy(&nValue, pData, nLen);
if ( m_aScales[i-1] )
d = (nValue / pow(10.0,static_cast<int>(m_aScales[i-1]))); else
d = static_cast<double>(nValue);
} else
{ if (o3tl::make_unsigned(nLen) > sizeof(d)) returnfalse;
memcpy(&d, pData, nLen);
}
*(*_rRow)[i] = d;
} else
{
sal_Int32 nPos1 = -1, nPos2 = -1; // If the string contains Nul-characters, then convert them to blanks! for (sal_Int32 k = 0; k < nLen; k++)
{ if (pData[k] == '\0')
pData[k] = ' ';
if (pData[k] != ' ')
{ if (nPos1 < 0) // first non-empty char position.
nPos1 = k;
// last non-empty char position.
nPos2 = k;
}
}
if (nPos1 < 0)
{ // Empty string. Skip it.
nByteOffset += nLen;
(*_rRow)[i]->setNull(); // no values -> done continue;
}
if ( !m_pConnection->matchesExtension( aURL.getExtension() ) )
aURL.setExtension(m_pConnection->getExtension());
try
{
Content aContent(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),Reference<XCommandEnvironment>(), comphelper::getProcessComponentContext()); if (aContent.isDocument())
{ // Only if the file exists with length > 0 raise an error
std::unique_ptr<SvStream> pFileStream(createStream_simpleError( aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::READ));
if (pFileStream && pFileStream->TellEnd()) returnfalse;
}
} catch(const Exception&) // an exception is thrown when no file exists
{
}
bool bMemoFile = false;
bool bOk = CreateFile(aURL, bMemoFile);
FileClose();
if (!bOk)
{ try
{
Content aContent(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),Reference<XCommandEnvironment>(), comphelper::getProcessComponentContext());
aContent.executeCommand( u"delete"_ustr, css::uno::Any( true ) );
} catch(const Exception&) // an exception is thrown when no file exists
{
} returnfalse;
}
if (bMemoFile)
{
OUString aExt = aURL.getExtension();
aURL.setExtension(u"dbt"); // extension for memo file
void ODbaseTable::throwInvalidColumnType(TranslateId pErrorId, const OUString& _sColumnName)
{ try
{ // we have to drop the file because it is corrupted now
DropImpl();
} catch(const Exception&)
{
}
// creates in principle dBase IV file format bool ODbaseTable::CreateFile(const INetURLObject& aFile, bool& bCreateMemo)
{
bCreateMemo = false;
Date aDate( Date::SYSTEM ); // current date
try
{ const sal_Int32 nCount = xColumns->getCount(); for(sal_Int32 i=0;i<nCount;++i)
{
xColumns->getByIndex(i) >>= xCol;
OSL_ENSURE(xCol.is(),"This should be a column!");
switch (getINT32(xCol->getPropertyValue(sPropType)))
{ case DataType::DOUBLE: case DataType::INTEGER: case DataType::TIMESTAMP: case DataType::LONGVARBINARY:
nDbaseType = VisualFoxPro;
i = nCount; // no more columns need to be checked break;
} // switch (getINT32(xCol->getPropertyValue(sPropType)))
}
} catch ( const Exception& )
{ try
{ // we have to drop the file because it is corrupted now
DropImpl();
} catch(const Exception&) { } throw;
}
char aBuffer[21] = {}; // write buffer
m_pFileStream->Seek(0);
(*m_pFileStream).WriteUChar( nDbaseType ); // dBase format
(*m_pFileStream).WriteUChar( aDate.GetYearUnsigned() % 100 ); // current date
(*m_pFileStream).WriteUChar( aDate.GetMonth() );
(*m_pFileStream).WriteUChar( aDate.GetDay() );
(*m_pFileStream).WriteUInt32( 0 ); // number of data records
(*m_pFileStream).WriteUInt16( (m_xColumns->getCount()+1) * 32 + 1 ); // header information, // pColumns contains always an additional column
(*m_pFileStream).WriteUInt16( 0 ); // record length will be determined later
m_pFileStream->WriteBytes(aBuffer, 20);
switch (getINT32(xCol->getPropertyValue(sPropType)))
{ case DataType::CHAR: case DataType::VARCHAR:
cTyp = 'C'; break; case DataType::DOUBLE: if (getBOOL(xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISCURRENCY)))) // Currency will be treated separately
cTyp = 'Y'; else
cTyp = 'B'; break; case DataType::INTEGER:
cTyp = 'I'; break; case DataType::TINYINT: case DataType::SMALLINT: case DataType::BIGINT: case DataType::DECIMAL: case DataType::NUMERIC: case DataType::REAL:
cTyp = 'N'; // only dBase 3 format break; case DataType::TIMESTAMP:
cTyp = 'T'; break; case DataType::DATE:
cTyp = 'D'; break; case DataType::BIT:
cTyp = 'L'; break; case DataType::LONGVARBINARY:
bBinary = true;
[[fallthrough]]; case DataType::LONGVARCHAR:
cTyp = 'M'; break; default:
{
throwInvalidColumnType(STR_INVALID_COLUMN_TYPE, aName);
}
}
(*m_pFileStream).WriteUChar( FIELD_DESCRIPTOR_TERMINATOR ); // end of header
(*m_pFileStream).WriteChar( char(DBF_EOL) );
m_pFileStream->Seek(10);
(*m_pFileStream).WriteUInt16( nRecLength ); // set record length afterwards
if (bCreateMemo)
{
m_pFileStream->Seek(0); if (nDbaseType == VisualFoxPro)
(*m_pFileStream).WriteUChar( FoxProMemo ); else
(*m_pFileStream).WriteUChar( dBaseIIIMemo );
} // if (bCreateMemo)
} catch ( const Exception& )
{ try
{ // we have to drop the file because it is corrupted now
DropImpl();
} catch(const Exception&) { } throw;
} returntrue;
}
// as the inf file does not necessarily exist, we aren't allowed to use UCBContentHelper::Kill try
{
::ucbhelper::Content aDeleteContent( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), Reference< XCommandEnvironment >(), comphelper::getProcessComponentContext() );
aDeleteContent.executeCommand( u"delete"_ustr, Any( true ) );
} catch(const Exception&)
{ // silently ignore this...
}
}
} return bDropped;
}
bool ODbaseTable::DropImpl()
{
FileClose();
if(!m_xIndexes)
refreshIndexes(); // look for indexes which must be deleted as well
bool bDropped = Drop_Static(getEntry(m_pConnection,m_Name),HasMemoFields(),m_xIndexes.get()); if(!bDropped)
{// we couldn't drop the table so we have to reopen it
construct(); if(m_xColumns)
m_xColumns->refresh();
} return bDropped;
}
bool ODbaseTable::InsertRow(OValueRefVector& rRow, const Reference<XIndexAccess>& _xCols)
{ // fill buffer with blanks if (!AllocBuffer()) returnfalse;
if (HasMemoFields() && m_pMemoStream)
{
m_pMemoStream->Seek(STREAM_SEEK_TO_END);
nMemoFileSize = m_pMemoStream->Tell();
}
if (!WriteBuffer())
{
m_pFileStream->SetStreamSize(nFileSize); // restore old size
if (HasMemoFields() && m_pMemoStream)
m_pMemoStream->SetStreamSize(nMemoFileSize); // restore old size
m_nFilePos = nTempPos; // restore file position
} else
{
(*m_pFileStream).WriteChar( char(DBF_EOL) ); // write EOL // raise number of datasets in the header:
m_pFileStream->Seek( 4 );
(*m_pFileStream).WriteUInt32( m_aHeader.nbRecords + 1 );
m_pFileStream->Flush();
// raise number if successfully
m_aHeader.nbRecords++;
*rRow[0] = m_nFilePos; // set bookmark
m_nFilePos = nTempPos;
}
} else
m_nFilePos = nTempPos;
return bInsertRow;
}
bool ODbaseTable::UpdateRow(OValueRefVector& rRow, OValueRefRow& pOrgRow, const Reference<XIndexAccess>& _xCols)
{ // fill buffer with blanks if (!AllocBuffer()) returnfalse;
// position on desired record:
std::size_t nPos = m_aHeader.headerLength + static_cast<tools::Long>(m_nFilePos-1) * m_aHeader.recordLength;
m_pFileStream->Seek(nPos);
m_pFileStream->ReadBytes(m_pBuffer.get(), m_aHeader.recordLength);
std::size_t nMemoFileSize( 0 ); if (HasMemoFields() && m_pMemoStream)
{
m_pMemoStream->Seek(STREAM_SEEK_TO_END);
nMemoFileSize = m_pMemoStream->Tell();
} if (!UpdateBuffer(rRow, pOrgRow, _xCols, false) || !WriteBuffer())
{ if (HasMemoFields() && m_pMemoStream)
m_pMemoStream->SetStreamSize(nMemoFileSize); // restore old size
} else
{
m_pFileStream->Flush();
} returntrue;
}
bool ODbaseTable::DeleteRow(const OSQLColumns& _rCols)
{ // Set the Delete-Flag (be it set or not): // Position on desired record:
std::size_t nFilePos = m_aHeader.headerLength + static_cast<tools::Long>(m_nFilePos-1) * m_aHeader.recordLength;
m_pFileStream->Seek(nFilePos);
OValueRefRow aRow = new OValueRefVector(_rCols.size());
if (!fetchRow(aRow,_rCols,true)) returnfalse;
Reference<XPropertySet> xCol;
OUString aColName;
::comphelper::UStringMixEqual aCase(isCaseSensitive()); for (sal_Int32 i = 0; i < m_xColumns->getCount(); i++)
{
Reference<XPropertySet> xIndex = isUniqueByColumnName(i); if (xIndex.is())
{
xCol.set(m_xColumns->getByIndex(i), css::uno::UNO_QUERY);
OSL_ENSURE(xCol.is(),"ODbaseTable::DeleteRow column is null!"); if(xCol.is())
{
xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= aColName;
ODbaseIndex* pIndex = dynamic_cast<ODbaseIndex*>(xIndex.get());
assert(pIndex && "ODbaseTable::DeleteRow: No Index returned!");
Reference<XIndexAccess> xColumns(m_xColumns.get()); // first search a key that exist already in the table for (sal_Int32 i = 0; i < nColumnCount; ++i)
{
sal_Int32 nPos = i; if(_xCols != xColumns)
{
m_xColumns->getByIndex(i) >>= xCol;
OSL_ENSURE(xCol.is(),"ODbaseTable::UpdateBuffer column is null!");
xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= aColName;
++nPos;
xIndex = isUniqueByColumnName(i);
aIndexedCols[i] = xIndex; if (xIndex.is())
{ // first check if the value is different to the old one and when if it conform to the index if(pOrgRow.is() && (rRow[nPos]->getValue().isNull() || rRow[nPos] == (*pOrgRow)[nPos])) continue; else
{
ODbaseIndex* pIndex = dynamic_cast<ODbaseIndex*>(xIndex.get());
assert(pIndex && "ODbaseTable::UpdateBuffer: No Index returned!");
if (pIndex->Find(0,*rRow[nPos]))
{ // There is no unique value if ( aColName.isEmpty() )
{
m_xColumns->getByIndex(i) >>= xCol;
OSL_ENSURE(xCol.is(),"ODbaseTable::UpdateBuffer column is null!");
xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= aColName;
xCol.clear();
} // if ( !aColName.getLength() ) const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution(
STR_DUPLICATE_VALUE_IN_COLUMN
,"$columnname$", aColName
) );
::dbtools::throwGenericSQLException( sError, *this );
}
}
}
}
// when we are here there is no double key in the table
for (sal_Int32 i = 0; i < nColumnCount && nByteOffset <= m_nBufferSize ; ++i)
{ // Lengths for each data type:
assert(i >= 0);
OSL_ENSURE(o3tl::make_unsigned(i) < m_aPrecisions.size(),"Illegal index!");
sal_Int32 nLen = 0;
sal_Int32 nType = 0;
sal_Int32 nScale = 0; if ( o3tl::make_unsigned(i) < m_aPrecisions.size() )
{
nLen = m_aPrecisions[i];
nType = m_aTypes[i];
nScale = m_aScales[i];
} else
{
m_xColumns->getByIndex(i) >>= xCol; if ( xCol.is() )
{
xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION)) >>= nLen;
xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)) >>= nType;
xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE)) >>= nScale;
}
}
bool bSetZero = false; switch (nType)
{ case DataType::INTEGER: case DataType::DOUBLE: case DataType::TIMESTAMP:
bSetZero = true;
[[fallthrough]]; case DataType::LONGVARBINARY: case DataType::DATE: case DataType::BIT: case DataType::LONGVARCHAR:
nLen = m_aRealFieldLengths[i]; break; case DataType::DECIMAL:
nLen = SvDbaseConverter::ConvertPrecisionToDbase(nLen,nScale); break; // The sign and the comma default: break;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.