1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <flat/ETable.hxx>
21 #include <com/sun/star/sdbc/ColumnValue.hpp>
22 #include <com/sun/star/sdbc/DataType.hpp>
23 #include <com/sun/star/sdbc/XRow.hpp>
24 #include <com/sun/star/ucb/XContentAccess.hpp>
25 #include <flat/EConnection.hxx>
26 #include <flat/EColumns.hxx>
27 #include <o3tl/safeint.hxx>
28 #include <rtl/math.hxx>
29 #include <sal/log.hxx>
30 #include <tools/urlobj.hxx>
31 #include <cppuhelper/queryinterface.hxx>
32 #include <comphelper/numbers.hxx>
33 #include <comphelper/servicehelper.hxx>
34 #include <com/sun/star/util/NumberFormat.hpp>
35 #include <com/sun/star/util/NumberFormatter.hpp>
36 #include <com/sun/star/util/NumberFormatsSupplier.hpp>
37 #include <i18nlangtag/languagetag.hxx>
38 #include <connectivity/dbconversion.hxx>
39 #include <connectivity/sdbcx/VColumn.hxx>
40 #include <file/quotedstring.hxx>
41 #include <file/FDriver.hxx>
42 #include <unotools/syslocale.hxx>
43 #include <unotools/charclass.hxx>
45 using namespace ::comphelper
;
46 using namespace connectivity
;
47 using namespace connectivity::flat
;
48 using namespace connectivity::file
;
49 using namespace ::cppu
;
50 using namespace ::com::sun::star::uno
;
51 using namespace ::com::sun::star::ucb
;
52 using namespace ::com::sun::star::beans
;
53 using namespace ::com::sun::star::sdbcx
;
54 using namespace ::com::sun::star::sdbc
;
55 using namespace ::com::sun::star::container
;
56 using namespace ::com::sun::star::lang
;
57 using namespace ::com::sun::star::util
;
59 using std::lower_bound
;
62 void OFlatTable::fillColumns(const css::lang::Locale
& _aLocale
)
64 m_bNeedToReadLine
= true; // we overwrite m_aCurrentLine, seek the stream, ...
65 m_pFileStream
->Seek(0);
66 // tdf#123055 - start to read unicode text in order to avoid the BOM
67 m_pFileStream
->StartReadingUnicodeText(RTL_TEXTENCODING_DONTKNOW
);
68 m_aCurrentLine
= QuotedTokenizedString();
71 const OFlatConnection
* const pConnection
= getFlatConnection();
72 const bool bHasHeaderLine
= pConnection
->isHeaderLine();
74 QuotedTokenizedString aHeaderLine
;
75 const sal_Int32 nPos
= static_cast<sal_Int32
>(m_pFileStream
->Tell());
76 TRowPositionInFile
rowPos(nPos
, nPos
);
80 bRead
= readLine(&rowPos
.second
, &rowPos
.first
, true);
82 aHeaderLine
= m_aCurrentLine
;
84 setRowPos(rowNum
++, rowPos
);
89 bRead
= readLine(&rowPos
.second
, &rowPos
.first
);
91 setRowPos(rowNum
++, rowPos
);
94 if ( !bHasHeaderLine
|| !aHeaderLine
.Len())
96 // use first non-empty row as headerline because we need the number of columns
97 while(bRead
&& m_aCurrentLine
.Len() == 0)
99 bRead
= readLine(&rowPos
.second
, &rowPos
.first
);
101 setRowPos(rowNum
++, rowPos
);
103 aHeaderLine
= m_aCurrentLine
;
106 const sal_Int32 nFieldCount
= aHeaderLine
.GetTokenCount(m_cFieldDelimiter
,m_cStringDelimiter
);
109 m_aColumns
= new OSQLColumns();
114 m_aPrecisions
.clear();
116 // reserve some space
117 m_aColumns
->reserve(nFieldCount
+1);
118 m_aTypes
.assign(nFieldCount
+1,DataType::SQLNULL
);
119 m_aPrecisions
.assign(nFieldCount
+1,-1);
120 m_aScales
.assign(nFieldCount
+1,-1);
122 const bool bCase
= m_pConnection
->getMetaData()->supportsMixedCaseQuotedIdentifiers();
123 CharClass
aCharClass( pConnection
->getDriver()->getComponentContext(), LanguageTag( _aLocale
));
125 const sal_Unicode cDecimalDelimiter
= pConnection
->getDecimalDelimiter();
126 const sal_Unicode cThousandDelimiter
= pConnection
->getThousandDelimiter();
127 ::comphelper::UStringMixEqual
aCase(bCase
);
128 vector
<OUString
> aColumnNames
;
129 vector
<OUString
> aTypeNames
;
130 aTypeNames
.resize(nFieldCount
);
131 const sal_Int32 nMaxRowsToScan
= pConnection
->getMaxRowsToScan();
132 sal_Int32 nRowCount
= 0;
136 sal_Int32 nStartPosHeaderLine
= 0; // use for efficient way to get the tokens
137 sal_Int32 nStartPosFirstLine
= 0; // use for efficient way to get the tokens
138 sal_Int32 nStartPosFirstLine2
= 0;
139 for( sal_Int32 i
= 0; i
< nFieldCount
; i
++ )
143 OUString aColumnName
;
144 if ( bHasHeaderLine
)
146 aColumnName
= aHeaderLine
.GetTokenSpecial(nStartPosHeaderLine
,m_cFieldDelimiter
,m_cStringDelimiter
);
148 if ( aColumnName
.isEmpty() )
150 aColumnName
= "C" + OUString::number(i
+1);
152 aColumnNames
.push_back(aColumnName
);
156 impl_fillColumnInfo_nothrow(m_aCurrentLine
, nStartPosFirstLine
, nStartPosFirstLine2
,
157 m_aTypes
[i
], m_aPrecisions
[i
], m_aScales
[i
], aTypeNames
[i
],
158 cDecimalDelimiter
, cThousandDelimiter
, aCharClass
);
162 bRead
= readLine(&rowPos
.second
, &rowPos
.first
);
164 setRowPos(rowNum
++, rowPos
);
166 while(nRowCount
< nMaxRowsToScan
&& bRead
);
168 for( sal_Int32 i
= 0; i
< nFieldCount
; i
++ )
170 // check if the columname already exists
171 OUString
aAlias(aColumnNames
[i
]);
172 OSQLColumns::const_iterator aFind
= connectivity::find(m_aColumns
->begin(),m_aColumns
->end(),aAlias
,aCase
);
173 sal_Int32 nExprCnt
= 0;
174 while(aFind
!= m_aColumns
->end())
176 aAlias
= aColumnNames
[i
] + OUString::number(++nExprCnt
);
177 aFind
= connectivity::find(m_aColumns
->begin(),m_aColumns
->end(),aAlias
,aCase
);
180 rtl::Reference
<sdbcx::OColumn
> pColumn
= new sdbcx::OColumn(aAlias
,aTypeNames
[i
],OUString(),OUString(),
181 ColumnValue::NULLABLE
,
189 m_CatalogName
, getSchema(), getName());
190 m_aColumns
->push_back(pColumn
);
193 m_pFileStream
->Seek(m_aRowPosToFilePos
[0].second
);
196 void OFlatTable::impl_fillColumnInfo_nothrow(QuotedTokenizedString
const & aFirstLine
, sal_Int32
& nStartPosFirstLine
, sal_Int32
& nStartPosFirstLine2
,
197 sal_Int32
& io_nType
, sal_Int32
& io_nPrecisions
, sal_Int32
& io_nScales
, OUString
& o_sTypeName
,
198 const sal_Unicode cDecimalDelimiter
, const sal_Unicode cThousandDelimiter
, const CharClass
& aCharClass
)
200 if ( io_nType
!= DataType::VARCHAR
)
202 bool bNumeric
= io_nType
== DataType::SQLNULL
|| io_nType
== DataType::DOUBLE
|| io_nType
== DataType::DECIMAL
|| io_nType
== DataType::INTEGER
;
203 sal_Int32 nIndex
= 0;
207 // first without fielddelimiter
208 OUString aField
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine
,m_cFieldDelimiter
);
209 if (aField
.isEmpty() ||
210 (m_cStringDelimiter
&& m_cStringDelimiter
== aField
[0]))
213 if ( m_cStringDelimiter
!= '\0' )
214 aField
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine2
,m_cFieldDelimiter
,m_cStringDelimiter
);
216 nStartPosFirstLine2
= nStartPosFirstLine
;
221 if ( m_cStringDelimiter
!= '\0' )
222 aField2
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine2
,m_cFieldDelimiter
,m_cStringDelimiter
);
226 if (aField2
.isEmpty())
234 sal_Int32 nDecimalDelCount
= 0;
235 sal_Int32 nSpaceCount
= 0;
236 for( sal_Int32 j
= 0; j
< aField2
.getLength(); j
++ )
238 const sal_Unicode c
= aField2
[j
];
239 if ( j
== nSpaceCount
&& m_cFieldDelimiter
!= 32 && c
== 32 )
244 // just digits, decimal- and thousands-delimiter?
245 if ( ( !cDecimalDelimiter
|| c
!= cDecimalDelimiter
) &&
246 ( !cThousandDelimiter
|| c
!= cThousandDelimiter
) &&
247 !aCharClass
.isDigit(aField2
,j
) &&
248 ( j
!= 0 || (c
!= '+' && c
!= '-' ) ) )
253 if (cDecimalDelimiter
&& c
== cDecimalDelimiter
)
255 io_nPrecisions
= 15; // we have a decimal value
258 } // if (cDecimalDelimiter && c == cDecimalDelimiter)
263 if (nDecimalDelCount
> 1 || nDot
> 1 ) // if there is more than one dot it isn't a number
265 if (bNumeric
&& cThousandDelimiter
)
267 // Is the delimiter correct?
268 const std::u16string_view aValue
= o3tl::getToken(aField2
, 0, cDecimalDelimiter
);
269 for( sal_Int32 j
= static_cast<sal_Int32
>(aValue
.size()) - 4; j
>= 0; j
-= 4)
271 const sal_Unicode c
= aValue
[j
];
272 // just digits, decimal- and thousands-delimiter?
273 if (c
== cThousandDelimiter
&& j
)
283 // now also check for a date field
288 nIndex
= m_xNumberFormatter
->detectNumberFormat(css::util::NumberFormat::ALL
,aField2
);
297 else if ( io_nType
== DataType::DATE
|| io_nType
== DataType::TIMESTAMP
|| io_nType
== DataType::TIME
)
299 OUString aField
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine
,m_cFieldDelimiter
);
300 if (aField
.isEmpty() ||
301 (m_cStringDelimiter
&& m_cStringDelimiter
== aField
[0]))
307 if ( m_cStringDelimiter
!= '\0' )
308 aField2
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine2
,m_cFieldDelimiter
,m_cStringDelimiter
);
311 if (!aField2
.isEmpty() )
315 nIndex
= m_xNumberFormatter
->detectNumberFormat(css::util::NumberFormat::ALL
,aField2
);
326 if (cDecimalDelimiter
)
330 io_nType
= DataType::DECIMAL
;
331 o_sTypeName
= "DECIMAL";
335 io_nType
= DataType::DOUBLE
;
336 o_sTypeName
= "DOUBLE";
341 io_nType
= DataType::INTEGER
;
348 switch (comphelper::getNumberFormatType(m_xNumberFormatter
,nIndex
))
350 case css::util::NumberFormat::DATE
:
351 io_nType
= DataType::DATE
;
352 o_sTypeName
= "DATE";
354 case css::util::NumberFormat::DATETIME
:
355 io_nType
= DataType::TIMESTAMP
;
356 o_sTypeName
= "TIMESTAMP";
358 case css::util::NumberFormat::TIME
:
359 io_nType
= DataType::TIME
;
360 o_sTypeName
= "TIME";
363 io_nType
= DataType::VARCHAR
;
364 io_nPrecisions
= 0; // nyi: Data can be longer!
366 o_sTypeName
= "VARCHAR";
372 OUString aField
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine
,m_cFieldDelimiter
);
373 if (aField
.isEmpty() ||
374 (m_cStringDelimiter
&& m_cStringDelimiter
== aField
[0]))
376 if ( m_cStringDelimiter
!= '\0' )
377 aField
= aFirstLine
.GetTokenSpecial(nStartPosFirstLine2
, m_cFieldDelimiter
, m_cStringDelimiter
);
379 nStartPosFirstLine2
= nStartPosFirstLine
;
383 if ( m_cStringDelimiter
!= '\0' )
384 aFirstLine
.GetTokenSpecial(nStartPosFirstLine2
, m_cFieldDelimiter
, m_cStringDelimiter
);
389 OFlatTable::OFlatTable(sdbcx::OCollection
* _pTables
,OFlatConnection
* _pConnection
,
390 const OUString
& Name
,
391 const OUString
& Type
,
392 const OUString
& Description
,
393 const OUString
& SchemaName
,
394 const OUString
& CatalogName
395 ) : OFlatTable_BASE(_pTables
,_pConnection
,Name
,
402 ,m_cStringDelimiter(_pConnection
->getStringDelimiter())
403 ,m_cFieldDelimiter(_pConnection
->getFieldDelimiter())
404 ,m_bNeedToReadLine(false)
409 void OFlatTable::construct()
411 SvtSysLocale aLocale
;
412 css::lang::Locale
aAppLocale(aLocale
.GetLanguageTag().getLocale());
414 Reference
< XNumberFormatsSupplier
> xSupplier
= NumberFormatsSupplier::createWithLocale( m_pConnection
->getDriver()->getComponentContext(), aAppLocale
);
415 m_xNumberFormatter
.set( NumberFormatter::create( m_pConnection
->getDriver()->getComponentContext()), UNO_QUERY_THROW
);
416 m_xNumberFormatter
->attachNumberFormatsSupplier(xSupplier
);
417 Reference
<XPropertySet
> xProp
= xSupplier
->getNumberFormatSettings();
418 xProp
->getPropertyValue("NullDate") >>= m_aNullDate
;
421 aURL
.SetURL(getEntry());
423 if(aURL
.getExtension() != m_pConnection
->getExtension())
424 aURL
.setExtension(m_pConnection
->getExtension());
426 OUString aFileName
= aURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
428 m_pFileStream
= createStream_simpleError( aFileName
, StreamMode::READWRITE
| StreamMode::NOCREATE
| StreamMode::SHARE_DENYWRITE
);
431 m_pFileStream
= createStream_simpleError( aFileName
, StreamMode::READ
| StreamMode::NOCREATE
| StreamMode::SHARE_DENYNONE
);
436 sal_uInt64
const nSize
= m_pFileStream
->remainingSize();
438 // Buffersize is dependent on the file-size
439 m_pFileStream
->SetBufferSize(nSize
> 1000000 ? 32768 :
440 nSize
> 100000 ? 16384 :
441 nSize
> 10000 ? 4096 : 1024);
443 fillColumns(aAppLocale
);
448 OUString
OFlatTable::getEntry() const
453 Reference
< XResultSet
> xDir
= m_pConnection
->getDir()->getStaticResultSet();
454 Reference
< XRow
> xRow(xDir
,UNO_QUERY
);
462 sName
= xRow
->getString(1);
463 aURL
.SetSmartProtocol(INetProtocol::File
);
464 OUString sUrl
= m_pConnection
->getURL() + "/" + sName
;
465 aURL
.SetSmartURL( sUrl
);
468 sExt
= aURL
.getExtension();
470 // name and extension have to coincide
471 if ( m_pConnection
->matchesExtension( sExt
) )
473 if ( !sExt
.isEmpty() )
474 sName
= sName
.replaceAt(sName
.getLength() - (sExt
.getLength() + 1), sExt
.getLength()+1, u
"");
475 if ( sName
== m_Name
)
477 Reference
< XContentAccess
> xContentAccess( xDir
, UNO_QUERY
);
478 sURL
= xContentAccess
->queryContentIdentifierString();
483 xDir
->beforeFirst(); // move back to before first record
485 catch(const Exception
&)
492 void OFlatTable::refreshColumns()
494 ::osl::MutexGuard
aGuard( m_aMutex
);
496 ::std::vector
< OUString
> aVector
;
497 aVector
.reserve(m_aColumns
->size());
499 for (auto const& column
: *m_aColumns
)
500 aVector
.push_back(Reference
< XNamed
>(column
,UNO_QUERY_THROW
)->getName());
503 m_xColumns
->reFill(aVector
);
505 m_xColumns
.reset(new OFlatColumns(this,m_aMutex
,aVector
));
509 void SAL_CALL
OFlatTable::disposing()
511 OFileTable::disposing();
512 ::osl::MutexGuard
aGuard(m_aMutex
);
513 m_aColumns
= nullptr;
516 Sequence
< Type
> SAL_CALL
OFlatTable::getTypes( )
518 Sequence
< Type
> aTypes
= OTable_TYPEDEF::getTypes();
519 vector
<Type
> aOwnTypes
;
520 aOwnTypes
.reserve(aTypes
.getLength());
521 const Type
* pBegin
= aTypes
.getConstArray();
522 const Type
* pEnd
= pBegin
+ aTypes
.getLength();
523 for(;pBegin
!= pEnd
;++pBegin
)
525 if(!(*pBegin
== cppu::UnoType
<XKeysSupplier
>::get()||
526 *pBegin
== cppu::UnoType
<XRename
>::get()||
527 *pBegin
== cppu::UnoType
<XIndexesSupplier
>::get()||
528 *pBegin
== cppu::UnoType
<XAlterTable
>::get()||
529 *pBegin
== cppu::UnoType
<XDataDescriptorFactory
>::get()))
531 aOwnTypes
.push_back(*pBegin
);
534 return Sequence
< Type
>(aOwnTypes
.data(), aOwnTypes
.size());
538 Any SAL_CALL
OFlatTable::queryInterface( const Type
& rType
)
540 if( rType
== cppu::UnoType
<XKeysSupplier
>::get()||
541 rType
== cppu::UnoType
<XIndexesSupplier
>::get()||
542 rType
== cppu::UnoType
<XRename
>::get()||
543 rType
== cppu::UnoType
<XAlterTable
>::get()||
544 rType
== cppu::UnoType
<XDataDescriptorFactory
>::get())
547 Any aRet
= OTable_TYPEDEF::queryInterface(rType
);
552 bool OFlatTable::fetchRow(OValueRefRow
& _rRow
, const OSQLColumns
& _rCols
, bool bRetrieveData
)
554 *(*_rRow
)[0] = m_nFilePos
;
560 if ( m_bNeedToReadLine
)
562 m_pFileStream
->Seek(m_nFilePos
);
563 TRowPositionInFile
rowPos(0, 0);
564 if(readLine(&rowPos
.second
, &rowPos
.first
))
566 setRowPos(m_nRowPos
, rowPos
);
567 m_bNeedToReadLine
= false;
570 // else let run through so that we set _rRow to all NULL
573 const OFlatConnection
* const pConnection
= getFlatConnection();
574 const sal_Unicode cDecimalDelimiter
= pConnection
->getDecimalDelimiter();
575 const sal_Unicode cThousandDelimiter
= pConnection
->getThousandDelimiter();
577 sal_Int32 nStartPos
= 0;
578 OSQLColumns::const_iterator aIter
= _rCols
.begin();
579 OSQLColumns::const_iterator aEnd
= _rCols
.end();
580 const OValueRefVector::size_type nCount
= _rRow
->size();
581 for (OValueRefVector::size_type i
= 1;
582 aIter
!= aEnd
&& i
< nCount
;
585 OUString aStr
= m_aCurrentLine
.GetTokenSpecial(nStartPos
,m_cFieldDelimiter
,m_cStringDelimiter
);
589 (*_rRow
)[i
]->setNull();
593 sal_Int32 nType
= m_aTypes
[i
-1];
596 case DataType::TIMESTAMP
:
602 double nRes
= m_xNumberFormatter
->convertStringToNumber(css::util::NumberFormat::ALL
,aStr
);
607 *(*_rRow
)[i
] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDate(nRes
,m_aNullDate
));
609 case DataType::TIMESTAMP
:
610 *(*_rRow
)[i
] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDateTime(nRes
,m_aNullDate
));
613 *(*_rRow
)[i
] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toTime(nRes
));
618 (*_rRow
)[i
]->setNull();
621 case DataType::DOUBLE
:
622 case DataType::INTEGER
:
623 case DataType::DECIMAL
:
624 case DataType::NUMERIC
:
627 OUString aStrConverted
;
628 if ( DataType::INTEGER
!= nType
)
630 OSL_ENSURE((cDecimalDelimiter
&& nType
!= DataType::INTEGER
) ||
631 (!cDecimalDelimiter
&& nType
== DataType::INTEGER
),
634 OUStringBuffer
aBuf(aStr
.getLength());
635 // convert to Standard-Notation (DecimalPOINT without thousands-comma):
636 for (sal_Int32 j
= 0; j
< aStr
.getLength(); ++j
)
638 const sal_Unicode cChar
= aStr
[j
];
639 if (cDecimalDelimiter
&& cChar
== cDecimalDelimiter
)
641 else if ( cChar
== '.' ) // special case, if decimal separator isn't '.' we have to put the string after it
643 else if (cThousandDelimiter
&& cChar
== cThousandDelimiter
)
649 } // for (j = 0; j < aStr.(); ++j)
650 aStrConverted
= aBuf
.makeStringAndClear();
651 } // if ( DataType::INTEGER != nType )
654 if ( cThousandDelimiter
)
655 aStrConverted
= aStr
.replaceAll(OUStringChar(cThousandDelimiter
), "");
657 aStrConverted
= aStr
;
659 const double nVal
= ::rtl::math::stringToDouble(aStrConverted
,'.',',');
662 if ( DataType::DECIMAL
== nType
|| DataType::NUMERIC
== nType
)
663 *(*_rRow
)[i
] = OUString::number(nVal
);
670 // Copy Value as String in Row-Variable
671 *(*_rRow
)[i
] = ORowSetValue(aStr
);
675 (*_rRow
)[i
]->setTypeKind(nType
);
682 void OFlatTable::refreshHeader()
684 SAL_INFO( "connectivity.flat", "flat lionel@mamane.lu OFlatTable::refreshHeader" );
690 template< typename Tp
, typename Te
> struct RangeBefore
692 bool operator() (const Tp
&p
, const Te
&e
)
694 assert(p
.first
<= p
.second
);
695 return p
.second
<= e
;
700 bool OFlatTable::seekRow(IResultSetHelper::Movement eCursorPosition
, sal_Int32 nOffset
, sal_Int32
& nCurPos
)
702 OSL_ENSURE(m_pFileStream
,"OFlatTable::seekRow: FileStream is NULL!");
705 switch(eCursorPosition
)
707 case IResultSetHelper::FIRST
:
710 case IResultSetHelper::NEXT
:
712 assert(m_nRowPos
>= 0);
713 if(m_nMaxRowCount
!= 0 && m_nRowPos
> m_nMaxRowCount
)
716 if(m_aRowPosToFilePos
.size() > o3tl::make_unsigned(m_nRowPos
))
718 m_bNeedToReadLine
= true;
719 m_nFilePos
= m_aRowPosToFilePos
[m_nRowPos
].first
;
720 nCurPos
= m_aRowPosToFilePos
[m_nRowPos
].second
;
724 assert(m_aRowPosToFilePos
.size() == static_cast< vector
< TRowPositionInFile
>::size_type
>(m_nRowPos
));
725 const TRowPositionInFile
&lastRowPos(m_aRowPosToFilePos
.back());
726 // Our ResultSet is allowed to disagree with us only
727 // on the position of the first line
728 // (because of the special case of the header...)
729 assert(m_nRowPos
== 1 || nCurPos
== lastRowPos
.second
);
731 m_nFilePos
= lastRowPos
.second
;
732 m_pFileStream
->Seek(m_nFilePos
);
734 TRowPositionInFile newRowPos
;
735 if(!readLine(&newRowPos
.second
, &newRowPos
.first
))
737 m_nMaxRowCount
= m_nRowPos
- 1;
741 nCurPos
= newRowPos
.second
;
742 setRowPos(m_nRowPos
, newRowPos
);
747 case IResultSetHelper::PRIOR
:
748 assert(m_nRowPos
>= 0);
755 assert (m_nRowPos
>= 0);
756 assert(m_aRowPosToFilePos
.size() >= o3tl::make_unsigned(m_nRowPos
));
757 const TRowPositionInFile
&aPositions(m_aRowPosToFilePos
[m_nRowPos
]);
758 m_nFilePos
= aPositions
.first
;
759 nCurPos
= aPositions
.second
;
760 m_bNeedToReadLine
= true;
764 case IResultSetHelper::LAST
:
765 if (m_nMaxRowCount
== 0)
767 while(seekRow(IResultSetHelper::NEXT
, 1, nCurPos
)) ; // run through after last row
769 // m_nMaxRowCount can still be zero, but now it means there a genuinely zero rows in the table
770 return seekRow(IResultSetHelper::ABSOLUTE1
, m_nMaxRowCount
, nCurPos
);
771 case IResultSetHelper::RELATIVE1
:
773 const sal_Int32 nNewRowPos
= m_nRowPos
+ nOffset
;
776 // ABSOLUTE will take care of case nNewRowPos > nMaxRowCount
777 return seekRow(IResultSetHelper::ABSOLUTE1
, nNewRowPos
, nCurPos
);
779 case IResultSetHelper::ABSOLUTE1
:
783 if (m_nMaxRowCount
== 0)
785 if (!seekRow(IResultSetHelper::LAST
, 0, nCurPos
))
788 // m_nMaxRowCount can still be zero, but now it means there a genuinely zero rows in the table
789 nOffset
= m_nMaxRowCount
+ nOffset
;
793 seekRow(IResultSetHelper::ABSOLUTE1
, 0, nCurPos
);
796 if(m_nMaxRowCount
&& nOffset
> m_nMaxRowCount
)
798 m_nRowPos
= m_nMaxRowCount
+ 1;
799 const TRowPositionInFile
&lastRowPos(m_aRowPosToFilePos
.back());
800 m_nFilePos
= lastRowPos
.second
;
801 nCurPos
= lastRowPos
.second
;
805 assert(m_nRowPos
>=0);
806 assert(m_aRowPosToFilePos
.size() > o3tl::make_unsigned(m_nRowPos
));
807 assert(nOffset
>= 0);
808 if(m_aRowPosToFilePos
.size() > o3tl::make_unsigned(nOffset
))
810 m_nFilePos
= m_aRowPosToFilePos
[nOffset
].first
;
811 nCurPos
= m_aRowPosToFilePos
[nOffset
].second
;
813 m_bNeedToReadLine
= true;
817 assert(m_nRowPos
< nOffset
);
818 while(m_nRowPos
< nOffset
)
820 if(!seekRow(IResultSetHelper::NEXT
, 1, nCurPos
))
823 assert(m_nRowPos
== nOffset
);
828 case IResultSetHelper::BOOKMARK
:
830 vector
< TRowPositionInFile
>::const_iterator aFind
= lower_bound(m_aRowPosToFilePos
.begin(),
831 m_aRowPosToFilePos
.end(),
833 RangeBefore
< TRowPositionInFile
, sal_Int32
>());
835 if(aFind
== m_aRowPosToFilePos
.end() || aFind
->first
!= nOffset
)
839 m_bNeedToReadLine
= true;
840 m_nFilePos
= aFind
->first
;
841 nCurPos
= aFind
->second
;
842 m_nRowPos
= aFind
- m_aRowPosToFilePos
.begin();
851 bool OFlatTable::readLine(sal_Int32
* const pEndPos
, sal_Int32
* const pStartPos
, const bool nonEmpty
)
853 const rtl_TextEncoding nEncoding
= m_pConnection
->getTextEncoding();
854 m_aCurrentLine
= QuotedTokenizedString();
858 *pStartPos
= static_cast<sal_Int32
>(m_pFileStream
->Tell());
859 m_pFileStream
->ReadByteStringLine(m_aCurrentLine
, nEncoding
);
860 if (m_pFileStream
->eof())
863 QuotedTokenizedString sLine
= m_aCurrentLine
; // check if the string continues on next line
864 sal_Int32 nLastOffset
= 0;
865 bool isQuoted
= false;
866 bool isFieldStarting
= true;
869 bool wasQuote
= false;
870 const sal_Unicode
*p
= sLine
.GetString().getStr() + nLastOffset
;
875 if (*p
== m_cStringDelimiter
)
876 wasQuote
= !wasQuote
;
883 if (*p
== m_cFieldDelimiter
)
884 isFieldStarting
= true;
892 isFieldStarting
= false;
893 if (*p
== m_cStringDelimiter
)
895 else if (*p
== m_cFieldDelimiter
)
896 isFieldStarting
= true;
898 else if (*p
== m_cFieldDelimiter
)
899 isFieldStarting
= true;
909 nLastOffset
= sLine
.Len();
910 m_pFileStream
->ReadByteStringLine(sLine
,nEncoding
);
911 if ( !m_pFileStream
->eof() )
913 OUString aStr
= m_aCurrentLine
.GetString() + "\n" + sLine
.GetString();
914 m_aCurrentLine
.SetString(aStr
);
915 sLine
= m_aCurrentLine
;
924 while(nonEmpty
&& m_aCurrentLine
.Len() == 0);
927 *pEndPos
= static_cast<sal_Int32
>(m_pFileStream
->Tell());
932 void OFlatTable::setRowPos(const vector
<TRowPositionInFile
>::size_type rowNum
, const TRowPositionInFile
&rowPos
)
934 assert(m_aRowPosToFilePos
.size() >= rowNum
);
935 if(m_aRowPosToFilePos
.size() == rowNum
)
936 m_aRowPosToFilePos
.push_back(rowPos
);
939 SAL_WARN_IF(m_aRowPosToFilePos
[rowNum
] != rowPos
,
941 "Setting position for row " << rowNum
<< " to (" << rowPos
.first
<< ", " << rowPos
.second
<< "), "
942 "but already had different position (" << m_aRowPosToFilePos
[rowNum
].first
<< ", " << m_aRowPosToFilePos
[rowNum
].second
<< ")");
943 m_aRowPosToFilePos
[rowNum
] = rowPos
;
947 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */