LibreOffice Module connectivity (master) 1
ETable.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
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>
34#include <com/sun/star/util/NumberFormat.hpp>
35#include <com/sun/star/util/NumberFormatter.hpp>
36#include <com/sun/star/util/NumberFormatsSupplier.hpp>
40#include <file/quotedstring.hxx>
41#include <file/FDriver.hxx>
44
45using namespace ::comphelper;
46using namespace connectivity;
47using namespace connectivity::flat;
48using namespace connectivity::file;
49using namespace ::cppu;
50using namespace ::com::sun::star::uno;
51using namespace ::com::sun::star::ucb;
52using namespace ::com::sun::star::beans;
53using namespace ::com::sun::star::sdbcx;
54using namespace ::com::sun::star::sdbc;
55using namespace ::com::sun::star::container;
56using namespace ::com::sun::star::lang;
57using namespace ::com::sun::star::util;
58using std::vector;
59using std::lower_bound;
60
61
62void OFlatTable::fillColumns(const css::lang::Locale& _aLocale)
63{
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);
69 bool bRead = true;
70
71 const OFlatConnection* const pConnection = getFlatConnection();
72 const bool bHasHeaderLine = pConnection->isHeaderLine();
73
74 QuotedTokenizedString aHeaderLine;
75 const sal_Int32 nPos = static_cast<sal_Int32>(m_pFileStream->Tell());
77 sal_Int32 rowNum(0);
78 if ( bHasHeaderLine )
79 {
80 bRead = readLine(&rowPos.second, &rowPos.first, true);
81 if(bRead)
82 aHeaderLine = m_aCurrentLine;
83 }
84 setRowPos(rowNum++, rowPos);
85
86 // read first row
87 if(bRead)
88 {
89 bRead = readLine(&rowPos.second, &rowPos.first);
90 if(bRead)
91 setRowPos(rowNum++, rowPos);
92 }
93
94 if ( !bHasHeaderLine || !aHeaderLine.Len())
95 {
96 // use first non-empty row as headerline because we need the number of columns
97 while(bRead && m_aCurrentLine.Len() == 0)
98 {
99 bRead = readLine(&rowPos.second, &rowPos.first);
100 if(bRead)
101 setRowPos(rowNum++, rowPos);
102 }
103 aHeaderLine = m_aCurrentLine;
104 }
105 // column count
106 const sal_Int32 nFieldCount = aHeaderLine.GetTokenCount(m_cFieldDelimiter,m_cStringDelimiter);
107
108 if(!m_aColumns.is())
109 m_aColumns = new OSQLColumns();
110 else
111 m_aColumns->clear();
112
113 m_aTypes.clear();
114 m_aPrecisions.clear();
115 m_aScales.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);
121
122 const bool bCase = m_pConnection->getMetaData()->supportsMixedCaseQuotedIdentifiers();
123 CharClass aCharClass( pConnection->getDriver()->getComponentContext(), LanguageTag( _aLocale));
124 // read description
125 const sal_Unicode cDecimalDelimiter = pConnection->getDecimalDelimiter();
126 const sal_Unicode cThousandDelimiter = pConnection->getThousandDelimiter();
128 vector<OUString> aColumnNames;
129 vector<OUString> aTypeNames;
130 aTypeNames.resize(nFieldCount);
131 const sal_Int32 nMaxRowsToScan = pConnection->getMaxRowsToScan();
132 sal_Int32 nRowCount = 0;
133
134 do
135 {
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++ )
140 {
141 if ( nRowCount == 0)
142 {
143 OUString aColumnName;
144 if ( bHasHeaderLine )
145 {
146 aColumnName = aHeaderLine.GetTokenSpecial(nStartPosHeaderLine,m_cFieldDelimiter,m_cStringDelimiter);
147 }
148 if ( aColumnName.isEmpty() )
149 {
150 aColumnName = "C" + OUString::number(i+1);
151 }
152 aColumnNames.push_back(aColumnName);
153 }
154 if(bRead)
155 {
156 impl_fillColumnInfo_nothrow(m_aCurrentLine, nStartPosFirstLine, nStartPosFirstLine2,
157 m_aTypes[i], m_aPrecisions[i], m_aScales[i], aTypeNames[i],
158 cDecimalDelimiter, cThousandDelimiter, aCharClass);
159 }
160 }
161 ++nRowCount;
162 bRead = readLine(&rowPos.second, &rowPos.first);
163 if(bRead)
164 setRowPos(rowNum++, rowPos);
165 }
166 while(nRowCount < nMaxRowsToScan && bRead);
167
168 for( sal_Int32 i = 0; i < nFieldCount; i++ )
169 {
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())
175 {
176 aAlias = aColumnNames[i] + OUString::number(++nExprCnt);
177 aFind = connectivity::find(m_aColumns->begin(),m_aColumns->end(),aAlias,aCase);
178 }
179
180 rtl::Reference<sdbcx::OColumn> pColumn = new sdbcx::OColumn(aAlias,aTypeNames[i],OUString(),OUString(),
181 ColumnValue::NULLABLE,
183 m_aScales[i],
184 m_aTypes[i],
185 false,
186 false,
187 false,
188 bCase,
190 m_aColumns->push_back(pColumn);
191 }
192
193 m_pFileStream->Seek(m_aRowPosToFilePos[0].second);
194}
195
196void 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)
199{
200 if ( io_nType != DataType::VARCHAR )
201 {
202 bool bNumeric = io_nType == DataType::SQLNULL || io_nType == DataType::DOUBLE || io_nType == DataType::DECIMAL || io_nType == DataType::INTEGER;
203 sal_Int32 nIndex = 0;
204
205 if ( bNumeric )
206 {
207 // first without fielddelimiter
208 OUString aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine,m_cFieldDelimiter);
209 if (aField.isEmpty() ||
210 (m_cStringDelimiter && m_cStringDelimiter == aField[0]))
211 {
212 bNumeric = false;
213 if ( m_cStringDelimiter != '\0' )
214 aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine2,m_cFieldDelimiter,m_cStringDelimiter);
215 else
216 nStartPosFirstLine2 = nStartPosFirstLine;
217 }
218 else
219 {
220 OUString aField2;
221 if ( m_cStringDelimiter != '\0' )
222 aField2 = aFirstLine.GetTokenSpecial(nStartPosFirstLine2,m_cFieldDelimiter,m_cStringDelimiter);
223 else
224 aField2 = aField;
225
226 if (aField2.isEmpty())
227 {
228 bNumeric = false;
229 }
230 else
231 {
232 bNumeric = true;
233 sal_Int32 nDot = 0;
234 sal_Int32 nDecimalDelCount = 0;
235 sal_Int32 nSpaceCount = 0;
236 for( sal_Int32 j = 0; j < aField2.getLength(); j++ )
237 {
238 const sal_Unicode c = aField2[j];
239 if ( j == nSpaceCount && m_cFieldDelimiter != 32 && c == 32 )
240 {
241 ++nSpaceCount;
242 continue;
243 }
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 != '-' ) ) )
249 {
250 bNumeric = false;
251 break;
252 }
253 if (cDecimalDelimiter && c == cDecimalDelimiter)
254 {
255 io_nPrecisions = 15; // we have a decimal value
256 io_nScales = 2;
257 ++nDecimalDelCount;
258 } // if (cDecimalDelimiter && c == cDecimalDelimiter)
259 if ( c == '.' )
260 ++nDot;
261 }
262
263 if (nDecimalDelCount > 1 || nDot > 1 ) // if there is more than one dot it isn't a number
264 bNumeric = false;
265 if (bNumeric && cThousandDelimiter)
266 {
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)
270 {
271 const sal_Unicode c = aValue[j];
272 // just digits, decimal- and thousands-delimiter?
273 if (c == cThousandDelimiter && j)
274 continue;
275 else
276 {
277 bNumeric = false;
278 break;
279 }
280 }
281 }
282
283 // now also check for a date field
284 if (!bNumeric)
285 {
286 try
287 {
288 nIndex = m_xNumberFormatter->detectNumberFormat(css::util::NumberFormat::ALL,aField2);
289 }
290 catch(Exception&)
291 {
292 }
293 }
294 }
295 }
296 }
297 else if ( io_nType == DataType::DATE || io_nType == DataType::TIMESTAMP || io_nType == DataType::TIME)
298 {
299 OUString aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine,m_cFieldDelimiter);
300 if (aField.isEmpty() ||
301 (m_cStringDelimiter && m_cStringDelimiter == aField[0]))
302 {
303 }
304 else
305 {
306 OUString aField2;
307 if ( m_cStringDelimiter != '\0' )
308 aField2 = aFirstLine.GetTokenSpecial(nStartPosFirstLine2,m_cFieldDelimiter,m_cStringDelimiter);
309 else
310 aField2 = aField;
311 if (!aField2.isEmpty() )
312 {
313 try
314 {
315 nIndex = m_xNumberFormatter->detectNumberFormat(css::util::NumberFormat::ALL,aField2);
316 }
317 catch(Exception&)
318 {
319 }
320 }
321 }
322 }
323
324 if (bNumeric)
325 {
326 if (cDecimalDelimiter)
327 {
328 if(io_nPrecisions)
329 {
330 io_nType = DataType::DECIMAL;
331 o_sTypeName = "DECIMAL";
332 }
333 else
334 {
335 io_nType = DataType::DOUBLE;
336 o_sTypeName = "DOUBLE";
337 }
338 }
339 else
340 {
341 io_nType = DataType::INTEGER;
342 io_nPrecisions = 0;
343 io_nScales = 0;
344 }
345 }
346 else
347 {
349 {
350 case css::util::NumberFormat::DATE:
351 io_nType = DataType::DATE;
352 o_sTypeName = "DATE";
353 break;
354 case css::util::NumberFormat::DATETIME:
355 io_nType = DataType::TIMESTAMP;
356 o_sTypeName = "TIMESTAMP";
357 break;
358 case css::util::NumberFormat::TIME:
359 io_nType = DataType::TIME;
360 o_sTypeName = "TIME";
361 break;
362 default:
363 io_nType = DataType::VARCHAR;
364 io_nPrecisions = 0; // nyi: Data can be longer!
365 io_nScales = 0;
366 o_sTypeName = "VARCHAR";
367 };
368 }
369 }
370 else
371 {
372 OUString aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine,m_cFieldDelimiter);
373 if (aField.isEmpty() ||
374 (m_cStringDelimiter && m_cStringDelimiter == aField[0]))
375 {
376 if ( m_cStringDelimiter != '\0' )
377 aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine2, m_cFieldDelimiter, m_cStringDelimiter);
378 else
379 nStartPosFirstLine2 = nStartPosFirstLine;
380 }
381 else
382 {
383 if ( m_cStringDelimiter != '\0' )
384 aFirstLine.GetTokenSpecial(nStartPosFirstLine2, m_cFieldDelimiter, m_cStringDelimiter);
385 }
386 }
387}
388
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,
396 Type,
397 Description,
398 SchemaName,
399 CatalogName)
400 ,m_nRowPos(0)
401 ,m_nMaxRowCount(0)
402 ,m_cStringDelimiter(_pConnection->getStringDelimiter())
403 ,m_cFieldDelimiter(_pConnection->getFieldDelimiter())
404 ,m_bNeedToReadLine(false)
405{
406
407}
408
410{
411 SvtSysLocale aLocale;
412 css::lang::Locale aAppLocale(aLocale.GetLanguageTag().getLocale());
413
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;
419
421 aURL.SetURL(getEntry());
422
423 if(aURL.getExtension() != m_pConnection->getExtension())
424 aURL.setExtension(m_pConnection->getExtension());
425
426 OUString aFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
427
428 m_pFileStream = createStream_simpleError( aFileName, StreamMode::READWRITE | StreamMode::NOCREATE | StreamMode::SHARE_DENYWRITE);
429
430 if(!m_pFileStream)
431 m_pFileStream = createStream_simpleError( aFileName, StreamMode::READ | StreamMode::NOCREATE | StreamMode::SHARE_DENYNONE);
432
433 if(!m_pFileStream)
434 return;
435
436 sal_uInt64 const nSize = m_pFileStream->remainingSize();
437
438 // Buffersize is dependent on the file-size
439 m_pFileStream->SetBufferSize(nSize > 1000000 ? 32768 :
440 nSize > 100000 ? 16384 :
441 nSize > 10000 ? 4096 : 1024);
442
443 fillColumns(aAppLocale);
444
446}
447
448OUString OFlatTable::getEntry() const
449{
450 OUString sURL;
451 try
452 {
453 Reference< XResultSet > xDir = m_pConnection->getDir()->getStaticResultSet();
454 Reference< XRow> xRow(xDir,UNO_QUERY);
455 OUString sName;
456 OUString sExt;
457
459 xDir->beforeFirst();
460 while(xDir->next())
461 {
462 sName = xRow->getString(1);
463 aURL.SetSmartProtocol(INetProtocol::File);
464 OUString sUrl = m_pConnection->getURL() + "/" + sName;
465 aURL.SetSmartURL( sUrl );
466
467 // cut the extension
468 sExt = aURL.getExtension();
469
470 // name and extension have to coincide
471 if ( m_pConnection->matchesExtension( sExt ) )
472 {
473 if ( !sExt.isEmpty() )
474 sName = sName.replaceAt(sName.getLength() - (sExt.getLength() + 1), sExt.getLength()+1, u"");
475 if ( sName == m_Name )
476 {
477 Reference< XContentAccess > xContentAccess( xDir, UNO_QUERY );
478 sURL = xContentAccess->queryContentIdentifierString();
479 break;
480 }
481 }
482 }
483 xDir->beforeFirst(); // move back to before first record
484 }
485 catch(const Exception&)
486 {
487 OSL_ASSERT(false);
488 }
489 return sURL;
490}
491
493{
494 ::osl::MutexGuard aGuard( m_aMutex );
495
496 ::std::vector< OUString> aVector;
497 aVector.reserve(m_aColumns->size());
498
499 for (auto const& column : *m_aColumns)
500 aVector.push_back(Reference< XNamed>(column,UNO_QUERY_THROW)->getName());
501
502 if(m_xColumns)
503 m_xColumns->reFill(aVector);
504 else
505 m_xColumns.reset(new OFlatColumns(this,m_aMutex,aVector));
506}
507
508
510{
511 OFileTable::disposing();
512 ::osl::MutexGuard aGuard(m_aMutex);
513 m_aColumns = nullptr;
514}
515
517{
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)
524 {
525 if(!(*pBegin == cppu::UnoType<XKeysSupplier>::get()||
526 *pBegin == cppu::UnoType<XRename>::get()||
530 {
531 aOwnTypes.push_back(*pBegin);
532 }
533 }
534 return Sequence< Type >(aOwnTypes.data(), aOwnTypes.size());
535}
536
537
538Any SAL_CALL OFlatTable::queryInterface( const Type & rType )
539{
540 if( rType == cppu::UnoType<XKeysSupplier>::get()||
542 rType == cppu::UnoType<XRename>::get()||
545 return Any();
546
548 return aRet;
549}
550
551
552bool OFlatTable::fetchRow(OValueRefRow& _rRow, const OSQLColumns & _rCols, bool bRetrieveData)
553{
554 *(*_rRow)[0] = m_nFilePos;
555
556 if (!bRetrieveData)
557 return true;
558
559 bool result = false;
560 if ( m_bNeedToReadLine )
561 {
563 TRowPositionInFile rowPos(0, 0);
564 if(readLine(&rowPos.second, &rowPos.first))
565 {
566 setRowPos(m_nRowPos, rowPos);
567 m_bNeedToReadLine = false;
568 result = true;
569 }
570 // else let run through so that we set _rRow to all NULL
571 }
572
573 const OFlatConnection * const pConnection = getFlatConnection();
574 const sal_Unicode cDecimalDelimiter = pConnection->getDecimalDelimiter();
575 const sal_Unicode cThousandDelimiter = pConnection->getThousandDelimiter();
576 // Fields:
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;
583 ++aIter, i++)
584 {
586
587 if (aStr.isEmpty())
588 {
589 (*_rRow)[i]->setNull();
590 }
591 else
592 {
593 sal_Int32 nType = m_aTypes[i-1];
594 switch(nType)
595 {
596 case DataType::TIMESTAMP:
597 case DataType::DATE:
598 case DataType::TIME:
599 {
600 try
601 {
602 double nRes = m_xNumberFormatter->convertStringToNumber(css::util::NumberFormat::ALL,aStr);
603
604 switch(nType)
605 {
606 case DataType::DATE:
608 break;
609 case DataType::TIMESTAMP:
611 break;
612 default:
614 }
615 }
616 catch(Exception&)
617 {
618 (*_rRow)[i]->setNull();
619 }
620 } break;
621 case DataType::DOUBLE:
622 case DataType::INTEGER:
623 case DataType::DECIMAL:
624 case DataType::NUMERIC:
625 {
626
627 OUString aStrConverted;
628 if ( DataType::INTEGER != nType )
629 {
630 OSL_ENSURE((cDecimalDelimiter && nType != DataType::INTEGER) ||
631 (!cDecimalDelimiter && nType == DataType::INTEGER),
632 "Wrong type");
633
634 OUStringBuffer aBuf(aStr.getLength());
635 // convert to Standard-Notation (DecimalPOINT without thousands-comma):
636 for (sal_Int32 j = 0; j < aStr.getLength(); ++j)
637 {
638 const sal_Unicode cChar = aStr[j];
639 if (cDecimalDelimiter && cChar == cDecimalDelimiter)
640 aBuf.append('.');
641 else if ( cChar == '.' ) // special case, if decimal separator isn't '.' we have to put the string after it
642 continue;
643 else if (cThousandDelimiter && cChar == cThousandDelimiter)
644 {
645 // leave out
646 }
647 else
648 aBuf.append(cChar);
649 } // for (j = 0; j < aStr.(); ++j)
650 aStrConverted = aBuf.makeStringAndClear();
651 } // if ( DataType::INTEGER != nType )
652 else
653 {
654 if ( cThousandDelimiter )
655 aStrConverted = aStr.replaceAll(OUStringChar(cThousandDelimiter), "");
656 else
657 aStrConverted = aStr;
658 }
659 const double nVal = ::rtl::math::stringToDouble(aStrConverted,'.',',');
660
661 // #99178# OJ
662 if ( DataType::DECIMAL == nType || DataType::NUMERIC == nType )
663 *(*_rRow)[i] = OUString::number(nVal);
664 else
665 *(*_rRow)[i] = nVal;
666 } break;
667
668 default:
669 {
670 // Copy Value as String in Row-Variable
671 *(*_rRow)[i] = ORowSetValue(aStr);
672 }
673 break;
674 } // switch(nType)
675 (*_rRow)[i]->setTypeKind(nType);
676 }
677 }
678 return result;
679}
680
681
683{
684 SAL_INFO( "connectivity.flat", "flat lionel@mamane.lu OFlatTable::refreshHeader" );
685}
686
687
688namespace
689{
690 template< typename Tp, typename Te> struct RangeBefore
691 {
692 bool operator() (const Tp &p, const Te &e)
693 {
694 assert(p.first <= p.second);
695 return p.second <= e;
696 }
697 };
698}
699
700bool OFlatTable::seekRow(IResultSetHelper::Movement eCursorPosition, sal_Int32 nOffset, sal_Int32& nCurPos)
701{
702 OSL_ENSURE(m_pFileStream,"OFlatTable::seekRow: FileStream is NULL!");
703
704
705 switch(eCursorPosition)
706 {
708 m_nRowPos = 0;
709 [[fallthrough]];
711 {
712 assert(m_nRowPos >= 0);
714 return false;
715 ++m_nRowPos;
717 {
718 m_bNeedToReadLine = true;
720 nCurPos = m_aRowPosToFilePos[m_nRowPos].second;
721 }
722 else
723 {
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);
730
731 m_nFilePos = lastRowPos.second;
733
734 TRowPositionInFile newRowPos;
735 if(!readLine(&newRowPos.second, &newRowPos.first))
736 {
738 return false;
739 }
740
741 nCurPos = newRowPos.second;
742 setRowPos(m_nRowPos, newRowPos);
743 }
744 }
745
746 break;
748 assert(m_nRowPos >= 0);
749
750 if(m_nRowPos == 0)
751 return false;
752
753 --m_nRowPos;
754 {
755 assert (m_nRowPos >= 0);
758 m_nFilePos = aPositions.first;
759 nCurPos = aPositions.second;
760 m_bNeedToReadLine = true;
761 }
762
763 break;
765 if (m_nMaxRowCount == 0)
766 {
767 while(seekRow(IResultSetHelper::NEXT, 1, nCurPos)) ; // run through after last row
768 }
769 // m_nMaxRowCount can still be zero, but now it means there a genuinely zero rows in the table
772 {
773 const sal_Int32 nNewRowPos = m_nRowPos + nOffset;
774 if (nNewRowPos < 0)
775 return false;
776 // ABSOLUTE will take care of case nNewRowPos > nMaxRowCount
777 return seekRow(IResultSetHelper::ABSOLUTE1, nNewRowPos, nCurPos);
778 }
780 {
781 if(nOffset < 0)
782 {
783 if (m_nMaxRowCount == 0)
784 {
785 if (!seekRow(IResultSetHelper::LAST, 0, nCurPos))
786 return false;
787 }
788 // m_nMaxRowCount can still be zero, but now it means there a genuinely zero rows in the table
789 nOffset = m_nMaxRowCount + nOffset;
790 }
791 if(nOffset < 0)
792 {
794 return false;
795 }
796 if(m_nMaxRowCount && nOffset > m_nMaxRowCount)
797 {
799 const TRowPositionInFile &lastRowPos(m_aRowPosToFilePos.back());
800 m_nFilePos = lastRowPos.second;
801 nCurPos = lastRowPos.second;
802 return false;
803 }
804
805 assert(m_nRowPos >=0);
807 assert(nOffset >= 0);
808 if(m_aRowPosToFilePos.size() > o3tl::make_unsigned(nOffset))
809 {
810 m_nFilePos = m_aRowPosToFilePos[nOffset].first;
811 nCurPos = m_aRowPosToFilePos[nOffset].second;
812 m_nRowPos = nOffset;
813 m_bNeedToReadLine = true;
814 }
815 else
816 {
817 assert(m_nRowPos < nOffset);
818 while(m_nRowPos < nOffset)
819 {
820 if(!seekRow(IResultSetHelper::NEXT, 1, nCurPos))
821 return false;
822 }
823 assert(m_nRowPos == nOffset);
824 }
825 }
826
827 break;
829 {
830 vector< TRowPositionInFile >::const_iterator aFind = lower_bound(m_aRowPosToFilePos.begin(),
831 m_aRowPosToFilePos.end(),
832 nOffset,
833 RangeBefore< TRowPositionInFile, sal_Int32 >());
834
835 if(aFind == m_aRowPosToFilePos.end() || aFind->first != nOffset)
836 //invalid bookmark
837 return false;
838
839 m_bNeedToReadLine = true;
840 m_nFilePos = aFind->first;
841 nCurPos = aFind->second;
842 m_nRowPos = aFind - m_aRowPosToFilePos.begin();
843 break;
844 }
845 }
846
847 return true;
848}
849
850
851bool OFlatTable::readLine(sal_Int32 * const pEndPos, sal_Int32 * const pStartPos, const bool nonEmpty)
852{
853 const rtl_TextEncoding nEncoding = m_pConnection->getTextEncoding();
855 do
856 {
857 if (pStartPos)
858 *pStartPos = static_cast<sal_Int32>(m_pFileStream->Tell());
859 m_pFileStream->ReadByteStringLine(m_aCurrentLine, nEncoding);
860 if (m_pFileStream->eof())
861 return false;
862
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;
867 while (true)
868 {
869 bool wasQuote = false;
870 const sal_Unicode *p = sLine.GetString().getStr() + nLastOffset;
871 while (*p)
872 {
873 if (isQuoted)
874 {
875 if (*p == m_cStringDelimiter)
876 wasQuote = !wasQuote;
877 else
878 {
879 if (wasQuote)
880 {
881 wasQuote = false;
882 isQuoted = false;
883 if (*p == m_cFieldDelimiter)
884 isFieldStarting = true;
885 }
886 }
887 }
888 else
889 {
890 if (isFieldStarting)
891 {
892 isFieldStarting = false;
893 if (*p == m_cStringDelimiter)
894 isQuoted = true;
895 else if (*p == m_cFieldDelimiter)
896 isFieldStarting = true;
897 }
898 else if (*p == m_cFieldDelimiter)
899 isFieldStarting = true;
900 }
901 ++p;
902 }
903
904 if (wasQuote)
905 isQuoted = false;
906
907 if (isQuoted)
908 {
909 nLastOffset = sLine.Len();
910 m_pFileStream->ReadByteStringLine(sLine,nEncoding);
911 if ( !m_pFileStream->eof() )
912 {
913 OUString aStr = m_aCurrentLine.GetString() + "\n" + sLine.GetString();
915 sLine = m_aCurrentLine;
916 }
917 else
918 break;
919 }
920 else
921 break;
922 }
923 }
924 while(nonEmpty && m_aCurrentLine.Len() == 0);
925
926 if(pEndPos)
927 *pEndPos = static_cast<sal_Int32>(m_pFileStream->Tell());
928 return true;
929}
930
931
932void OFlatTable::setRowPos(const vector<TRowPositionInFile>::size_type rowNum, const TRowPositionInFile &rowPos)
933{
934 assert(m_aRowPosToFilePos.size() >= rowNum);
935 if(m_aRowPosToFilePos.size() == rowNum)
936 m_aRowPosToFilePos.push_back(rowPos);
937 else
938 {
939 SAL_WARN_IF(m_aRowPosToFilePos[rowNum] != rowPos,
940 "connectivity.flat",
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;
944 }
945}
946
947/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static double toDouble(std::string_view rString)
Definition: DTable.cxx:1635
bool isDigit(const OUString &rStr, sal_Int32 nPos) const
const css::lang::Locale & getLocale(bool bResolveSystem=true) const
const LanguageTag & GetLanguageTag() const
rtl_TextEncoding getTextEncoding() const
Definition: TConnection.hxx:61
const OUString & getURL() const
Definition: TConnection.hxx:62
OUString GetTokenSpecial(sal_Int32 &nStartPos, sal_Unicode cTok, sal_Unicode cStrDel='\0') const
sal_Int32 GetTokenCount(sal_Unicode cTok, sal_Unicode cStrDel) const
void SetString(const OUString &aStr)
bool matchesExtension(const OUString &_rExt) const
Definition: FConnection.cxx:75
virtual css::uno::Reference< css::sdbc::XDatabaseMetaData > SAL_CALL getMetaData() override
css::uno::Reference< css::ucb::XDynamicResultSet > getDir() const
const OUString & getExtension() const
OFileDriver * getDriver() const
const css::uno::Reference< css::uno::XComponentContext > & getComponentContext() const
Definition: FDriver.hxx:66
OConnection * m_pConnection
Definition: FTable.hxx:36
const OUString & getSchema() const
Definition: FTable.hxx:83
OUString SAL_CALL getName() override
Definition: FTable.hxx:81
static std::unique_ptr< SvStream > createStream_simpleError(const OUString &_rFileName, StreamMode _eOpenMode)
Definition: FTable.cxx:155
::rtl::Reference< OSQLColumns > m_aColumns
Definition: FTable.hxx:38
std::unique_ptr< SvStream > m_pFileStream
Definition: FTable.hxx:37
sal_Unicode getThousandDelimiter() const
Definition: EConnection.hxx:47
sal_Unicode getDecimalDelimiter() const
Definition: EConnection.hxx:46
void setRowPos(std::vector< TRowPositionInFile >::size_type rowNum, const TRowPositionInFile &rowPos)
Definition: ETable.cxx:932
bool readLine(sal_Int32 *pEndPos, sal_Int32 *pStartPos, bool nonEmpty=false)
Definition: ETable.cxx:851
virtual bool seekRow(IResultSetHelper::Movement eCursorPosition, sal_Int32 nOffset, sal_Int32 &nCurPos) override
Definition: ETable.cxx:700
virtual css::uno::Any SAL_CALL queryInterface(const css::uno::Type &rType) override
Definition: ETable.cxx:538
virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override
Definition: ETable.cxx:516
virtual void refreshColumns() override
Definition: ETable.cxx:492
OFlatConnection * getFlatConnection()
Definition: ETable.hxx:61
virtual void refreshHeader() override
Definition: ETable.cxx:682
void fillColumns(const css::lang::Locale &_aLocale)
Definition: ETable.cxx:62
virtual void SAL_CALL disposing() override
Definition: ETable.cxx:509
QuotedTokenizedString m_aCurrentLine
Definition: ETable.hxx:46
virtual bool fetchRow(OValueRefRow &_rRow, const OSQLColumns &_rCols, bool bRetrieveData) override
Definition: ETable.cxx:552
std::vector< sal_Int32 > m_aPrecisions
Definition: ETable.hxx:44
sal_Unicode m_cStringDelimiter
Definition: ETable.hxx:51
css::util::Date m_aNullDate
Definition: ETable.hxx:48
css::uno::Reference< css::util::XNumberFormatter > m_xNumberFormatter
Definition: ETable.hxx:47
OFlatTable(sdbcx::OCollection *_pTables, OFlatConnection *_pConnection, const OUString &Name, const OUString &Type, const OUString &Description=OUString(), const OUString &SchemaName=OUString(), const OUString &CatalogName=OUString())
Definition: ETable.cxx:389
std::vector< sal_Int32 > m_aTypes
Definition: ETable.hxx:43
void impl_fillColumnInfo_nothrow(QuotedTokenizedString const &aFirstLine, sal_Int32 &nStartPosFirstLine, sal_Int32 &nStartPosFirstLine2, sal_Int32 &io_nType, sal_Int32 &io_nPrecisions, sal_Int32 &io_nScales, OUString &o_sTypeName, const sal_Unicode cDecimalDelimiter, const sal_Unicode cThousandDelimiter, const CharClass &aCharClass)
Definition: ETable.cxx:196
std::vector< TRowPositionInFile > m_aRowPosToFilePos
Definition: ETable.hxx:42
std::vector< sal_Int32 > m_aScales
Definition: ETable.hxx:45
void construct() override
Definition: ETable.cxx:409
OUString getEntry() const
Definition: ETable.cxx:448
std::unique_ptr< OCollection > m_xColumns
Definition: VTable.hxx:77
virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override
Definition: VTable.cxx:124
virtual css::uno::Any SAL_CALL queryInterface(const css::uno::Type &rType) override
Definition: VTable.cxx:109
mutable::osl::Mutex m_aMutex
int nCount
URL aURL
float u
OUString sName
sal_Int32 nIndex
void * p
sal_uInt16 nPos
#define SAL_WARN_IF(condition, area, stream)
#define SAL_INFO(area, stream)
aStr
aBuf
@ Exception
sal_Int16 getNumberFormatType(const css::uno::Reference< css::util::XNumberFormats > &xFormats, sal_Int32 nKey)
Type
std::pair< sal_Int32, sal_Int32 > TRowPositionInFile
Definition: ETable.hxx:32
ORefVector< css::uno::Reference< css::beans::XPropertySet > > OSQLColumns
Definition: CommonTools.hxx:95
OSQLColumns::const_iterator find(const OSQLColumns::const_iterator &first, const OSQLColumns::const_iterator &last, std::u16string_view _rVal, const ::comphelper::UStringMixEqual &_rCase)
Definition: dbtools.cxx:1958
OOO_DLLPUBLIC_DBTOOLS css::util::Date toDate(double dVal, const css::util::Date &_rNullDate=getStandardDate())
OOO_DLLPUBLIC_DBTOOLS css::util::Time toTime(double dVal, short nDigits=9)
OOO_DLLPUBLIC_DBTOOLS css::util::DateTime toDateTime(double dVal, const css::util::Date &_rNullDate=getStandardDate())
int i
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
std::basic_string_view< charT, traits > getToken(std::basic_string_view< charT, traits > sv, charT delimiter, std::size_t &position)
static bool isQuoted(std::string_view str)
QPRO_FUNC_TYPE nType
OUString Name
sal_uInt16 sal_Unicode
Any result
const SvXMLTokenMapEntry aTypes[]