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 <dbase/DTable.hxx>
21 #include <com/sun/star/container/ElementExistException.hpp>
22 #include <com/sun/star/sdbc/ColumnValue.hpp>
23 #include <com/sun/star/sdbc/DataType.hpp>
24 #include <com/sun/star/ucb/XContentAccess.hpp>
25 #include <com/sun/star/sdbc/XRow.hpp>
26 #include <o3tl/safeint.hxx>
27 #include <svl/converter.hxx>
28 #include <dbase/DConnection.hxx>
29 #include <dbase/DColumns.hxx>
30 #include <tools/config.hxx>
31 #include <tools/diagnose_ex.h>
32 #include <dbase/DIndex.hxx>
33 #include <dbase/DIndexes.hxx>
34 #include <comphelper/processfactory.hxx>
35 #include <rtl/math.hxx>
36 #include <ucbhelper/content.hxx>
37 #include <com/sun/star/ucb/ContentCreationException.hpp>
38 #include <connectivity/dbexception.hxx>
39 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
40 #include <comphelper/property.hxx>
41 #include <comphelper/servicehelper.hxx>
42 #include <comphelper/string.hxx>
43 #include <unotools/tempfile.hxx>
44 #include <unotools/ucbhelper.hxx>
45 #include <comphelper/types.hxx>
46 #include <cppuhelper/typeprovider.hxx>
47 #include <cppuhelper/exc_hlp.hxx>
48 #include <cppuhelper/queryinterface.hxx>
49 #include <connectivity/dbtools.hxx>
50 #include <connectivity/FValue.hxx>
51 #include <connectivity/dbconversion.hxx>
52 #include <connectivity/sdbcx/VColumn.hxx>
53 #include <strings.hrc>
54 #include <rtl/strbuf.hxx>
55 #include <sal/log.hxx>
56 #include <tools/date.hxx>
57 
58 #include <algorithm>
59 #include <cassert>
60 #include <memory>
61 #include <string_view>
62 
63 using namespace ::comphelper;
64 using namespace connectivity;
65 using namespace connectivity::sdbcx;
66 using namespace connectivity::dbase;
67 using namespace connectivity::file;
68 using namespace ::ucbhelper;
69 using namespace ::utl;
70 using namespace ::cppu;
71 using namespace ::dbtools;
72 using namespace ::com::sun::star::uno;
73 using namespace ::com::sun::star::ucb;
74 using namespace ::com::sun::star::beans;
75 using namespace ::com::sun::star::sdbcx;
76 using namespace ::com::sun::star::sdbc;
77 using namespace ::com::sun::star::container;
78 using namespace ::com::sun::star::lang;
79 using namespace ::com::sun::star::i18n;
80 
81 // stored as the Field Descriptor terminator
82 #define FIELD_DESCRIPTOR_TERMINATOR 0x0D
83 #define DBF_EOL                     0x1A
84 
85 namespace
86 {
lcl_getFileSize(SvStream & _rStream)87 std::size_t lcl_getFileSize(SvStream& _rStream)
88 {
89     std::size_t nFileSize = 0;
90     _rStream.Seek(STREAM_SEEK_TO_END);
91     _rStream.SeekRel(-1);
92     char cEOL;
93     _rStream.ReadChar( cEOL );
94     nFileSize = _rStream.Tell();
95     if ( cEOL == DBF_EOL )
96         nFileSize -= 1;
97     return nFileSize;
98 }
99 /**
100     calculates the Julian date
101 */
lcl_CalcJulDate(sal_Int32 & _nJulianDate,sal_Int32 & _nJulianTime,const css::util::DateTime & rDateTime)102 void lcl_CalcJulDate(sal_Int32& _nJulianDate,sal_Int32& _nJulianTime, const css::util::DateTime& rDateTime)
103 {
104     css::util::DateTime aDateTime = rDateTime;
105     // weird: months fix
106     if (aDateTime.Month > 12)
107     {
108         aDateTime.Month--;
109         sal_uInt16 delta = rDateTime.Month / 12;
110         aDateTime.Year += delta;
111         aDateTime.Month -= delta * 12;
112         aDateTime.Month++;
113     }
114 
115     _nJulianTime = ((aDateTime.Hours*3600000)+(aDateTime.Minutes*60000)+(aDateTime.Seconds*1000)+(aDateTime.NanoSeconds/1000000));
116     /* conversion factors */
117     sal_uInt16 iy0;
118     sal_uInt16 im0;
119     if ( aDateTime.Month <= 2 )
120     {
121         iy0 = aDateTime.Year - 1;
122         im0 = aDateTime.Month + 12;
123     }
124     else
125     {
126         iy0 = aDateTime.Year;
127         im0 = aDateTime.Month;
128     }
129     sal_Int32 ia = iy0 / 100;
130     sal_Int32 ib = 2 - ia + (ia >> 2);
131     /* calculate julian date    */
132     if ( aDateTime.Year <= 0 )
133     {
134         _nJulianDate = static_cast<sal_Int32>((365.25 * iy0) - 0.75)
135             + static_cast<sal_Int32>(30.6001 * (im0 + 1) )
136             + aDateTime.Day + 1720994;
137     } // if ( rDateTime.Year <= 0 )
138     else
139     {
140         _nJulianDate = static_cast<sal_Int32>(365.25 * iy0)
141             + static_cast<sal_Int32>(30.6001 * (im0 + 1))
142             + aDateTime.Day + 1720994;
143     }
144     double JD = _nJulianDate + 0.5;
145     _nJulianDate = static_cast<sal_Int32>( JD + 0.5);
146     const double gyr = aDateTime.Year + (0.01 * aDateTime.Month) + (0.0001 * aDateTime.Day);
147     if ( gyr >= 1582.1015 ) /* on or after 15 October 1582  */
148         _nJulianDate += ib;
149 }
150 
151 /**
152     calculates date time from the Julian Date
153 */
lcl_CalDate(sal_Int32 _nJulianDate,sal_Int32 _nJulianTime,css::util::DateTime & _rDateTime)154 void lcl_CalDate(sal_Int32 _nJulianDate,sal_Int32 _nJulianTime,css::util::DateTime& _rDateTime)
155 {
156     if ( _nJulianDate )
157     {
158         sal_Int32 ka = _nJulianDate;
159         if ( _nJulianDate >= 2299161 )
160         {
161             sal_Int32 ialp = static_cast<sal_Int32>( (static_cast<double>(_nJulianDate) - 1867216.25 ) / 36524.25 );
162             ka = _nJulianDate + 1 + ialp - ( ialp >> 2 );
163         }
164         sal_Int32 kb = ka + 1524;
165         sal_Int32 kc =  static_cast<sal_Int32>( (static_cast<double>(kb) - 122.1 ) / 365.25 );
166         sal_Int32 kd = static_cast<sal_Int32>(static_cast<double>(kc) * 365.25);
167         sal_Int32 ke = static_cast<sal_Int32>(static_cast<double>( kb - kd ) / 30.6001 );
168         _rDateTime.Day = static_cast<sal_uInt16>(kb - kd - static_cast<sal_Int32>( static_cast<double>(ke) * 30.6001 ));
169         if ( ke > 13 )
170             _rDateTime.Month = static_cast<sal_uInt16>(ke - 13);
171         else
172             _rDateTime.Month = static_cast<sal_uInt16>(ke - 1);
173         if ( (_rDateTime.Month == 2) && (_rDateTime.Day > 28) )
174             _rDateTime.Day = 29;
175         if ( (_rDateTime.Month == 2) && (_rDateTime.Day == 29) && (ke == 3) )
176             _rDateTime.Year = static_cast<sal_uInt16>(kc - 4716);
177         else if ( _rDateTime.Month > 2 )
178             _rDateTime.Year = static_cast<sal_uInt16>(kc - 4716);
179         else
180             _rDateTime.Year = static_cast<sal_uInt16>(kc - 4715);
181     }
182 
183     if ( _nJulianTime )
184     {
185         double d_s = _nJulianTime / 1000.0;
186         double d_m = d_s / 60.0;
187         double d_h  = d_m / 60.0;
188         _rDateTime.Hours = static_cast<sal_uInt16>(d_h);
189         _rDateTime.Minutes = static_cast<sal_uInt16>((d_h - static_cast<double>(_rDateTime.Hours)) * 60.0);
190         _rDateTime.Seconds = static_cast<sal_uInt16>(((d_m - static_cast<double>(_rDateTime.Minutes)) * 60.0)
191                 - (static_cast<double>(_rDateTime.Hours) * 3600.0));
192     }
193 }
194 
195 }
196 
197 
readHeader()198 void ODbaseTable::readHeader()
199 {
200     OSL_ENSURE(m_pFileStream,"No Stream available!");
201     if(!m_pFileStream)
202         return;
203     m_pFileStream->RefreshBuffer(); // Make sure, that the header information actually is read again
204     m_pFileStream->Seek(STREAM_SEEK_TO_BEGIN);
205 
206     sal_uInt8 nType=0;
207     m_pFileStream->ReadUChar( nType );
208     if(ERRCODE_NONE != m_pFileStream->GetErrorCode())
209         throwInvalidDbaseFormat();
210 
211     m_pFileStream->ReadBytes(m_aHeader.dateElems, 3);
212     if(ERRCODE_NONE != m_pFileStream->GetErrorCode())
213         throwInvalidDbaseFormat();
214 
215     m_pFileStream->ReadUInt32( m_aHeader.nbRecords);
216     if(ERRCODE_NONE != m_pFileStream->GetErrorCode())
217         throwInvalidDbaseFormat();
218 
219     m_pFileStream->ReadUInt16( m_aHeader.headerLength);
220     if(ERRCODE_NONE != m_pFileStream->GetErrorCode())
221         throwInvalidDbaseFormat();
222 
223     m_pFileStream->ReadUInt16( m_aHeader.recordLength);
224     if(ERRCODE_NONE != m_pFileStream->GetErrorCode())
225         throwInvalidDbaseFormat();
226     if (m_aHeader.recordLength == 0)
227         throwInvalidDbaseFormat();
228 
229     m_pFileStream->ReadBytes(m_aHeader.trailer, 20);
230     if(ERRCODE_NONE != m_pFileStream->GetErrorCode())
231         throwInvalidDbaseFormat();
232 
233 
234     if ( ( ( m_aHeader.headerLength - 1 ) / 32 - 1 ) <= 0 ) // number of fields
235     {
236         // no dBASE file
237         throwInvalidDbaseFormat();
238     }
239     else
240     {
241         // Consistency check of the header:
242         m_aHeader.type = static_cast<DBFType>(nType);
243         switch (m_aHeader.type)
244         {
245             case dBaseIII:
246             case dBaseIV:
247             case dBaseV:
248             case VisualFoxPro:
249             case VisualFoxProAuto:
250             case dBaseFS:
251             case dBaseFSMemo:
252             case dBaseIVMemoSQL:
253             case dBaseIIIMemo:
254             case FoxProMemo:
255                 m_pFileStream->SetEndian(SvStreamEndian::LITTLE);
256                 if( getConnection()->isTextEncodingDefaulted() &&
257                    !dbfDecodeCharset(m_eEncoding, nType, m_aHeader.trailer[17]))
258                 {
259                     m_eEncoding = RTL_TEXTENCODING_IBM_850;
260                 }
261                 break;
262             case dBaseIVMemo:
263                 m_pFileStream->SetEndian(SvStreamEndian::LITTLE);
264                 break;
265             default:
266             {
267                 throwInvalidDbaseFormat();
268             }
269         }
270     }
271 }
272 
fillColumns()273 void ODbaseTable::fillColumns()
274 {
275     m_pFileStream->Seek(STREAM_SEEK_TO_BEGIN);
276     if (!checkSeek(*m_pFileStream, 32))
277     {
278         SAL_WARN("connectivity.drivers", "ODbaseTable::fillColumns: bad offset!");
279         return;
280     }
281 
282     if(!m_aColumns.is())
283         m_aColumns = new OSQLColumns();
284     else
285         m_aColumns->clear();
286 
287     m_aTypes.clear();
288     m_aPrecisions.clear();
289     m_aScales.clear();
290 
291     // Number of fields:
292     sal_Int32 nFieldCount = (m_aHeader.headerLength - 1) / 32 - 1;
293     if (nFieldCount <= 0)
294     {
295         SAL_WARN("connectivity.drivers", "No columns in table!");
296         return;
297     }
298 
299     auto nRemainingsize = m_pFileStream->remainingSize();
300     auto nMaxPossibleRecords = nRemainingsize / 32;
301     if (o3tl::make_unsigned(nFieldCount) > nMaxPossibleRecords)
302     {
303         SAL_WARN("connectivity.drivers", "Parsing error: " << nMaxPossibleRecords <<
304                  " max possible entries, but " << nFieldCount << " claimed, truncating");
305         nFieldCount = nMaxPossibleRecords;
306     }
307 
308     m_aColumns->reserve(nFieldCount);
309     m_aTypes.reserve(nFieldCount);
310     m_aPrecisions.reserve(nFieldCount);
311     m_aScales.reserve(nFieldCount);
312 
313     OUString aTypeName;
314     const bool bCase = getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers();
315     const bool bFoxPro = m_aHeader.type == VisualFoxPro || m_aHeader.type == VisualFoxProAuto || m_aHeader.type == FoxProMemo;
316 
317     sal_Int32 i = 0;
318     for (; i < nFieldCount; i++)
319     {
320         DBFColumn aDBFColumn;
321         m_pFileStream->ReadBytes(aDBFColumn.db_fnm, 11);
322         m_pFileStream->ReadUChar(aDBFColumn.db_typ);
323         m_pFileStream->ReadUInt32(aDBFColumn.db_adr);
324         m_pFileStream->ReadUChar(aDBFColumn.db_flng);
325         m_pFileStream->ReadUChar(aDBFColumn.db_dez);
326         m_pFileStream->ReadBytes(aDBFColumn.db_free2, 14);
327         if (!m_pFileStream->good())
328         {
329             SAL_WARN("connectivity.drivers", "ODbaseTable::fillColumns: short read!");
330             break;
331         }
332         if ( FIELD_DESCRIPTOR_TERMINATOR == aDBFColumn.db_fnm[0] ) // 0x0D stored as the Field Descriptor terminator.
333             break;
334 
335         aDBFColumn.db_fnm[sizeof(aDBFColumn.db_fnm)-1] = 0; //ensure null termination for broken input
336         const OUString aColumnName(reinterpret_cast<char *>(aDBFColumn.db_fnm), strlen(reinterpret_cast<char *>(aDBFColumn.db_fnm)), m_eEncoding);
337 
338         bool bIsRowVersion = bFoxPro && ( aDBFColumn.db_free2[0] & 0x01 ) == 0x01;
339 
340         m_aRealFieldLengths.push_back(aDBFColumn.db_flng);
341         sal_Int32 nPrecision = aDBFColumn.db_flng;
342         sal_Int32 eType;
343         bool bIsCurrency = false;
344 
345         char cType[2];
346         cType[0] = aDBFColumn.db_typ;
347         cType[1] = 0;
348         aTypeName = OUString(cType, 1, RTL_TEXTENCODING_ASCII_US);
349         SAL_INFO( "connectivity.drivers","column type: " << aDBFColumn.db_typ);
350 
351         switch (aDBFColumn.db_typ)
352         {
353         case 'C':
354             eType = DataType::VARCHAR;
355             aTypeName = "VARCHAR";
356             break;
357         case 'F':
358         case 'N':
359             aTypeName = "DECIMAL";
360             if ( aDBFColumn.db_typ == 'N' )
361                 aTypeName = "NUMERIC";
362             eType = DataType::DECIMAL;
363 
364             // for numeric fields two characters more are written, then the precision of the column description predescribes,
365             // to keep room for the possible sign and the comma. This has to be considered...
366             nPrecision = SvDbaseConverter::ConvertPrecisionToOdbc(nPrecision,aDBFColumn.db_dez);
367             // This is not true for older versions...
368             break;
369         case 'L':
370             eType = DataType::BIT;
371             aTypeName = "BOOLEAN";
372             break;
373         case 'Y':
374             bIsCurrency = true;
375             eType = DataType::DOUBLE;
376             aTypeName = "DOUBLE";
377             break;
378         case 'D':
379             eType = DataType::DATE;
380             aTypeName = "DATE";
381             break;
382         case 'T':
383             eType = DataType::TIMESTAMP;
384             aTypeName = "TIMESTAMP";
385             break;
386         case 'I':
387             eType = DataType::INTEGER;
388             aTypeName = "INTEGER";
389             break;
390         case 'M':
391             if ( bFoxPro && ( aDBFColumn.db_free2[0] & 0x04 ) == 0x04 )
392             {
393                 eType = DataType::LONGVARBINARY;
394                 aTypeName = "LONGVARBINARY";
395             }
396             else
397             {
398                 aTypeName = "LONGVARCHAR";
399                 eType = DataType::LONGVARCHAR;
400             }
401             nPrecision = 2147483647;
402             break;
403         case 'P':
404             aTypeName = "LONGVARBINARY";
405             eType = DataType::LONGVARBINARY;
406             nPrecision = 2147483647;
407             break;
408         case '0':
409         case 'B':
410             if ( m_aHeader.type == VisualFoxPro || m_aHeader.type == VisualFoxProAuto )
411             {
412                 aTypeName = "DOUBLE";
413                 eType = DataType::DOUBLE;
414             }
415             else
416             {
417                 aTypeName = "LONGVARBINARY";
418                 eType = DataType::LONGVARBINARY;
419                 nPrecision = 2147483647;
420             }
421             break;
422         default:
423             eType = DataType::OTHER;
424         }
425 
426         m_aTypes.push_back(eType);
427         m_aPrecisions.push_back(nPrecision);
428         m_aScales.push_back(aDBFColumn.db_dez);
429 
430         Reference< XPropertySet> xCol = new sdbcx::OColumn(aColumnName,
431                                                     aTypeName,
432                                                     OUString(),
433                                                     OUString(),
434                                                     ColumnValue::NULLABLE,
435                                                     nPrecision,
436                                                     aDBFColumn.db_dez,
437                                                     eType,
438                                                     false,
439                                                     bIsRowVersion,
440                                                     bIsCurrency,
441                                                     bCase,
442                                                     m_CatalogName, getSchema(), getName());
443         m_aColumns->push_back(xCol);
444     } // for (; i < nFieldCount; i++)
445     OSL_ENSURE(i,"No columns in table!");
446 }
447 
ODbaseTable(sdbcx::OCollection * _pTables,ODbaseConnection * _pConnection)448 ODbaseTable::ODbaseTable(sdbcx::OCollection* _pTables, ODbaseConnection* _pConnection)
449     : ODbaseTable_BASE(_pTables,_pConnection)
450 {
451     // initialize the header
452     m_aHeader.type = dBaseIII;
453     m_eEncoding = getConnection()->getTextEncoding();
454 }
455 
ODbaseTable(sdbcx::OCollection * _pTables,ODbaseConnection * _pConnection,const OUString & Name,const OUString & Type,const OUString & Description,const OUString & SchemaName,const OUString & CatalogName)456 ODbaseTable::ODbaseTable(sdbcx::OCollection* _pTables, ODbaseConnection* _pConnection,
457                          const OUString& Name,
458                          const OUString& Type,
459                          const OUString& Description ,
460                          const OUString& SchemaName,
461                          const OUString& CatalogName )
462     : ODbaseTable_BASE(_pTables,_pConnection,Name,
463                        Type,
464                        Description,
465                        SchemaName,
466                        CatalogName)
467 {
468     m_eEncoding = getConnection()->getTextEncoding();
469 }
470 
471 
construct()472 void ODbaseTable::construct()
473 {
474     // initialize the header
475     m_aHeader.type = dBaseIII;
476     m_aHeader.nbRecords = 0;
477     m_aHeader.headerLength = 0;
478     m_aHeader.recordLength = 0;
479     m_aMemoHeader.db_size = 0;
480 
481     OUString sFileName(getEntry(m_pConnection, m_Name));
482 
483     INetURLObject aURL;
484     aURL.SetURL(sFileName);
485 
486     OSL_ENSURE( m_pConnection->matchesExtension( aURL.getExtension() ),
487         "ODbaseTable::ODbaseTable: invalid extension!");
488         // getEntry is expected to ensure the correct file name
489 
490     m_pFileStream = createStream_simpleError( sFileName, StreamMode::READWRITE | StreamMode::NOCREATE | StreamMode::SHARE_DENYWRITE);
491     m_bWriteable = ( m_pFileStream != nullptr );
492 
493     if ( !m_pFileStream )
494     {
495         m_bWriteable = false;
496         m_pFileStream = createStream_simpleError( sFileName, StreamMode::READ | StreamMode::NOCREATE | StreamMode::SHARE_DENYNONE);
497     }
498 
499     if (!m_pFileStream)
500         return;
501 
502     readHeader();
503 
504     std::size_t nFileSize = lcl_getFileSize(*m_pFileStream);
505 
506     if (m_aHeader.headerLength > nFileSize)
507     {
508         SAL_WARN("connectivity.drivers", "Parsing error: " << nFileSize <<
509                  " max possible size, but " << m_aHeader.headerLength << " claimed, abandoning");
510         return;
511     }
512 
513     if (m_aHeader.recordLength)
514     {
515         std::size_t nMaxPossibleRecords = (nFileSize - m_aHeader.headerLength) / m_aHeader.recordLength;
516         // #i83401# seems to be empty or someone wrote nonsense into the dbase
517         // file try and recover if m_aHeader.db_slng is sane
518         if (m_aHeader.nbRecords == 0)
519         {
520             SAL_WARN("connectivity.drivers", "Parsing warning: 0 records claimed, recovering");
521             m_aHeader.nbRecords = nMaxPossibleRecords;
522         }
523         else if (m_aHeader.nbRecords > nMaxPossibleRecords)
524         {
525             SAL_WARN("connectivity.drivers", "Parsing error: " << nMaxPossibleRecords <<
526                      " max possible records, but " << m_aHeader.nbRecords << " claimed, truncating");
527             m_aHeader.nbRecords = std::max(nMaxPossibleRecords, static_cast<size_t>(1));
528         }
529     }
530 
531     if (HasMemoFields())
532     {
533     // Create Memo-Filename (.DBT):
534     // nyi: Ugly for Unix and Mac!
535 
536         if ( m_aHeader.type == FoxProMemo || m_aHeader.type == VisualFoxPro || m_aHeader.type == VisualFoxProAuto) // foxpro uses another extension
537             aURL.SetExtension(u"fpt");
538         else
539             aURL.SetExtension(u"dbt");
540 
541         // If the memo file isn't found, the data will be displayed anyhow.
542         // However, updates can't be done
543         // but the operation is executed
544         m_pMemoStream = createStream_simpleError( aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::READWRITE | StreamMode::NOCREATE | StreamMode::SHARE_DENYWRITE);
545         if ( !m_pMemoStream )
546         {
547             m_pMemoStream = createStream_simpleError( aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::READ | StreamMode::NOCREATE | StreamMode::SHARE_DENYNONE);
548         }
549         if (m_pMemoStream)
550             ReadMemoHeader();
551     }
552 
553     fillColumns();
554     m_pFileStream->Seek(STREAM_SEEK_TO_BEGIN);
555 
556 
557     // Buffersize dependent on the file size
558     m_pFileStream->SetBufferSize(nFileSize > 1000000 ? 32768 :
559                               nFileSize > 100000 ? 16384 :
560                               nFileSize > 10000 ? 4096 : 1024);
561 
562     if (m_pMemoStream)
563     {
564         // set the buffer exactly to the length of a record
565         nFileSize = m_pMemoStream->TellEnd();
566         m_pMemoStream->Seek(STREAM_SEEK_TO_BEGIN);
567 
568         // Buffersize dependent on the file size
569         m_pMemoStream->SetBufferSize(nFileSize > 1000000 ? 32768 :
570                                       nFileSize > 100000 ? 16384 :
571                                       nFileSize > 10000 ? 4096 :
572                                       m_aMemoHeader.db_size);
573     }
574 
575     AllocBuffer();
576 }
577 
ReadMemoHeader()578 void ODbaseTable::ReadMemoHeader()
579 {
580     m_pMemoStream->SetEndian(SvStreamEndian::LITTLE);
581     m_pMemoStream->RefreshBuffer();         // make sure that the header information is actually read again
582     m_pMemoStream->Seek(0);
583 
584     (*m_pMemoStream).ReadUInt32( m_aMemoHeader.db_next );
585     switch (m_aHeader.type)
586     {
587         case dBaseIIIMemo:  // dBase III: fixed block size
588         case dBaseIVMemo:
589             // sometimes dBase3 is attached to dBase4 memo
590             m_pMemoStream->Seek(20);
591             (*m_pMemoStream).ReadUInt16( m_aMemoHeader.db_size );
592             if (m_aMemoHeader.db_size > 1 && m_aMemoHeader.db_size != 512)  // 1 is also for dBase 3
593                 m_aMemoHeader.db_typ  = MemodBaseIV;
594             else if (m_aMemoHeader.db_size == 512)
595             {
596                 // There are files using size specification, though they are dBase-files
597                 char sHeader[4];
598                 m_pMemoStream->Seek(m_aMemoHeader.db_size);
599                 m_pMemoStream->ReadBytes(sHeader, 4);
600 
601                 if ((m_pMemoStream->GetErrorCode() != ERRCODE_NONE) || static_cast<sal_uInt8>(sHeader[0]) != 0xFF || static_cast<sal_uInt8>(sHeader[1]) != 0xFF || static_cast<sal_uInt8>(sHeader[2]) != 0x08)
602                     m_aMemoHeader.db_typ  = MemodBaseIII;
603                 else
604                     m_aMemoHeader.db_typ  = MemodBaseIV;
605             }
606             else
607             {
608                 m_aMemoHeader.db_typ  = MemodBaseIII;
609                 m_aMemoHeader.db_size = 512;
610             }
611             break;
612         case VisualFoxPro:
613         case VisualFoxProAuto:
614         case FoxProMemo:
615             m_aMemoHeader.db_typ    = MemoFoxPro;
616             m_pMemoStream->Seek(6);
617             m_pMemoStream->SetEndian(SvStreamEndian::BIG);
618             (*m_pMemoStream).ReadUInt16( m_aMemoHeader.db_size );
619             break;
620         default:
621             SAL_WARN( "connectivity.drivers", "ODbaseTable::ReadMemoHeader: unsupported memo type!" );
622             break;
623     }
624 }
625 
getEntry(file::OConnection const * _pConnection,std::u16string_view _sName)626 OUString ODbaseTable::getEntry(file::OConnection const * _pConnection, std::u16string_view _sName )
627 {
628     OUString sURL;
629     try
630     {
631         Reference< XResultSet > xDir = _pConnection->getDir()->getStaticResultSet();
632         Reference< XRow> xRow(xDir,UNO_QUERY);
633         OUString sName;
634         OUString sExt;
635         INetURLObject aURL;
636         xDir->beforeFirst();
637         while(xDir->next())
638         {
639             sName = xRow->getString(1);
640             aURL.SetSmartProtocol(INetProtocol::File);
641             OUString sUrl = _pConnection->getURL() + "/" + sName;
642             aURL.SetSmartURL( sUrl );
643 
644             // cut the extension
645             sExt = aURL.getExtension();
646 
647             // name and extension have to coincide
648             if ( _pConnection->matchesExtension( sExt ) )
649             {
650                 sName = sName.replaceAt(sName.getLength() - (sExt.getLength() + 1), sExt.getLength() + 1, OUString());
651                 if ( sName == _sName )
652                 {
653                     Reference< XContentAccess > xContentAccess( xDir, UNO_QUERY );
654                     sURL = xContentAccess->queryContentIdentifierString();
655                     break;
656                 }
657             }
658         }
659         xDir->beforeFirst(); // move back to before first record
660     }
661     catch(const Exception&)
662     {
663         OSL_ASSERT(false);
664     }
665     return sURL;
666 }
667 
refreshColumns()668 void ODbaseTable::refreshColumns()
669 {
670     ::osl::MutexGuard aGuard( m_aMutex );
671 
672     ::std::vector< OUString> aVector;
673     aVector.reserve(m_aColumns->size());
674 
675     for (auto const& column : *m_aColumns)
676         aVector.push_back(Reference< XNamed>(column,UNO_QUERY_THROW)->getName());
677 
678     if(m_xColumns)
679         m_xColumns->reFill(aVector);
680     else
681         m_xColumns = new ODbaseColumns(this,m_aMutex,aVector);
682 }
683 
refreshIndexes()684 void ODbaseTable::refreshIndexes()
685 {
686     ::std::vector< OUString> aVector;
687     if(m_pFileStream && (!m_xIndexes || m_xIndexes->getCount() == 0))
688     {
689         INetURLObject aURL;
690         aURL.SetURL(getEntry(m_pConnection,m_Name));
691 
692         aURL.setExtension(u"inf");
693         Config aInfFile(aURL.getFSysPath(FSysStyle::Detect));
694         aInfFile.SetGroup(dBASE_III_GROUP);
695         sal_uInt16 nKeyCnt = aInfFile.GetKeyCount();
696         OString aKeyName;
697 
698         for (sal_uInt16 nKey = 0; nKey < nKeyCnt; nKey++)
699         {
700             // References the key an index-file?
701             aKeyName = aInfFile.GetKeyName( nKey );
702             //...if yes, add the index list of the table
703             if (aKeyName.startsWith("NDX"))
704             {
705                 OString aIndexName = aInfFile.ReadKey(aKeyName);
706                 aURL.setName(OStringToOUString(aIndexName, m_eEncoding));
707                 try
708                 {
709                     Content aCnt(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),Reference<XCommandEnvironment>(), comphelper::getProcessComponentContext());
710                     if (aCnt.isDocument())
711                     {
712                         aVector.push_back(aURL.getBase());
713                     }
714                 }
715                 catch(const Exception&) // an exception is thrown when no file exists
716                 {
717                 }
718             }
719         }
720     }
721     if(m_xIndexes)
722         m_xIndexes->reFill(aVector);
723     else
724         m_xIndexes = new ODbaseIndexes(this,m_aMutex,aVector);
725 }
726 
727 
disposing()728 void SAL_CALL ODbaseTable::disposing()
729 {
730     OFileTable::disposing();
731     ::osl::MutexGuard aGuard(m_aMutex);
732     m_aColumns = nullptr;
733 }
734 
getTypes()735 Sequence< Type > SAL_CALL ODbaseTable::getTypes(  )
736 {
737     Sequence< Type > aTypes = OTable_TYPEDEF::getTypes();
738     std::vector<Type> aOwnTypes;
739     aOwnTypes.reserve(aTypes.getLength());
740 
741     const Type* pBegin = aTypes.getConstArray();
742     const Type* pEnd = pBegin + aTypes.getLength();
743     for(;pBegin != pEnd;++pBegin)
744     {
745         if(*pBegin != cppu::UnoType<XKeysSupplier>::get() &&
746            *pBegin != cppu::UnoType<XDataDescriptorFactory>::get())
747         {
748             aOwnTypes.push_back(*pBegin);
749         }
750     }
751     aOwnTypes.push_back(cppu::UnoType<css::lang::XUnoTunnel>::get());
752     return Sequence< Type >(aOwnTypes.data(), aOwnTypes.size());
753 }
754 
755 
queryInterface(const Type & rType)756 Any SAL_CALL ODbaseTable::queryInterface( const Type & rType )
757 {
758     if( rType == cppu::UnoType<XKeysSupplier>::get()||
759         rType == cppu::UnoType<XDataDescriptorFactory>::get())
760         return Any();
761 
762     Any aRet = OTable_TYPEDEF::queryInterface(rType);
763     return aRet.hasValue() ? aRet : ::cppu::queryInterface(rType,static_cast< css::lang::XUnoTunnel*> (this));
764 }
765 
766 
getUnoTunnelId()767 Sequence< sal_Int8 > ODbaseTable::getUnoTunnelId()
768 {
769     static ::cppu::OImplementationId implId;
770 
771     return implId.getImplementationId();
772 }
773 
774 // css::lang::XUnoTunnel
775 
getSomething(const Sequence<sal_Int8> & rId)776 sal_Int64 ODbaseTable::getSomething( const Sequence< sal_Int8 > & rId )
777 {
778     return (isUnoTunnelId<ODbaseTable>(rId))
779                 ? reinterpret_cast< sal_Int64 >( this )
780                 : ODbaseTable_BASE::getSomething(rId);
781 }
782 
fetchRow(OValueRefRow & _rRow,const OSQLColumns & _rCols,bool bRetrieveData)783 bool ODbaseTable::fetchRow(OValueRefRow& _rRow, const OSQLColumns & _rCols, bool bRetrieveData)
784 {
785     if (!m_pBuffer)
786         return false;
787 
788     // Read the data
789     bool bIsCurRecordDeleted = static_cast<char>(m_pBuffer[0]) == '*';
790 
791     // only read the bookmark
792 
793     // Mark record as deleted
794     _rRow->setDeleted(bIsCurRecordDeleted);
795     *(*_rRow)[0] = m_nFilePos;
796 
797     if (!bRetrieveData)
798         return true;
799 
800     std::size_t nByteOffset = 1;
801     // Fields:
802     OSQLColumns::const_iterator aIter = _rCols.begin();
803     OSQLColumns::const_iterator aEnd  = _rCols.end();
804     const std::size_t nCount = _rRow->size();
805     for (std::size_t i = 1; aIter != aEnd && nByteOffset <= m_nBufferSize && i < nCount;++aIter, i++)
806     {
807         // Lengths depending on data type:
808         sal_Int32 nLen = m_aPrecisions[i-1];
809         sal_Int32 nType = m_aTypes[i-1];
810 
811         switch(nType)
812         {
813             case DataType::INTEGER:
814             case DataType::DOUBLE:
815             case DataType::TIMESTAMP:
816             case DataType::DATE:
817             case DataType::BIT:
818             case DataType::LONGVARCHAR:
819             case DataType::LONGVARBINARY:
820                 nLen = m_aRealFieldLengths[i-1];
821                 break;
822             case DataType::DECIMAL:
823                 nLen = SvDbaseConverter::ConvertPrecisionToDbase(nLen,m_aScales[i-1]);
824                 break;  // the sign and the comma
825 
826             case DataType::BINARY:
827             case DataType::OTHER:
828                 nByteOffset += nLen;
829                 continue;
830         }
831 
832         // Is the variable bound?
833         if ( !(*_rRow)[i]->isBound() )
834         {
835             // No - next field.
836             nByteOffset += nLen;
837             OSL_ENSURE( nByteOffset <= m_nBufferSize ,"ByteOffset > m_nBufferSize!");
838             continue;
839         } // if ( !(_rRow->get())[i]->isBound() )
840         if ( ( nByteOffset + nLen) > m_nBufferSize )
841             break; // length doesn't match buffer size.
842 
843         char *pData = reinterpret_cast<char *>(m_pBuffer.get() + nByteOffset);
844 
845         if (nType == DataType::CHAR || nType == DataType::VARCHAR)
846         {
847             sal_Int32 nLastPos = -1;
848             for (sal_Int32 k = 0; k < nLen; ++k)
849             {
850                 if (pData[k] != ' ')
851                     // Record last non-empty position.
852                     nLastPos = k;
853             }
854             if (nLastPos < 0)
855             {
856                 // Empty string.  Skip it.
857                 (*_rRow)[i]->setNull();
858             }
859             else
860             {
861                 // Commit the string.  Use intern() to ref-count it.
862                 *(*_rRow)[i] = OUString::intern(pData, static_cast<sal_Int32>(nLastPos+1), m_eEncoding);
863             }
864         } // if (nType == DataType::CHAR || nType == DataType::VARCHAR)
865         else if ( DataType::TIMESTAMP == nType )
866         {
867             sal_Int32 nDate = 0,nTime = 0;
868             if (o3tl::make_unsigned(nLen) < 8)
869             {
870                 SAL_WARN("connectivity.drivers", "short TIMESTAMP");
871                 return false;
872             }
873             memcpy(&nDate, pData, 4);
874             memcpy(&nTime, pData + 4, 4);
875             if ( !nDate && !nTime )
876             {
877                 (*_rRow)[i]->setNull();
878             }
879             else
880             {
881                 css::util::DateTime aDateTime;
882                 lcl_CalDate(nDate,nTime,aDateTime);
883                 *(*_rRow)[i] = aDateTime;
884             }
885         }
886         else if ( DataType::INTEGER == nType )
887         {
888             sal_Int32 nValue = 0;
889             if (o3tl::make_unsigned(nLen) > sizeof(nValue))
890                 return false;
891             memcpy(&nValue, pData, nLen);
892             *(*_rRow)[i] = nValue;
893         }
894         else if ( DataType::DOUBLE == nType )
895         {
896             double d = 0.0;
897             if (getBOOL((*aIter)->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISCURRENCY)))) // Currency is treated separately
898             {
899                 sal_Int64 nValue = 0;
900                 if (o3tl::make_unsigned(nLen) > sizeof(nValue))
901                     return false;
902                 memcpy(&nValue, pData, nLen);
903 
904                 if ( m_aScales[i-1] )
905                     d = (nValue / pow(10.0,static_cast<int>(m_aScales[i-1])));
906                 else
907                     d = static_cast<double>(nValue);
908             }
909             else
910             {
911                 if (o3tl::make_unsigned(nLen) > sizeof(d))
912                     return false;
913                 memcpy(&d, pData, nLen);
914             }
915 
916             *(*_rRow)[i] = d;
917         }
918         else
919         {
920             sal_Int32 nPos1 = -1, nPos2 = -1;
921             // If the string contains Nul-characters, then convert them to blanks!
922             for (sal_Int32 k = 0; k < nLen; k++)
923             {
924                 if (pData[k] == '\0')
925                     pData[k] = ' ';
926 
927                 if (pData[k] != ' ')
928                 {
929                     if (nPos1 < 0)
930                         // first non-empty char position.
931                         nPos1 = k;
932 
933                     // last non-empty char position.
934                     nPos2 = k;
935                 }
936             }
937 
938             if (nPos1 < 0)
939             {
940                 // Empty string.  Skip it.
941                 nByteOffset += nLen;
942                 (*_rRow)[i]->setNull();   // no values -> done
943                 continue;
944             }
945 
946             OUString aStr = OUString::intern(pData+nPos1, nPos2-nPos1+1, m_eEncoding);
947 
948             switch (nType)
949             {
950                 case DataType::DATE:
951                 {
952                     if (aStr.getLength() != nLen)
953                     {
954                         (*_rRow)[i]->setNull();
955                         break;
956                     }
957                     const sal_uInt16  nYear   = static_cast<sal_uInt16>(aStr.copy( 0, 4 ).toInt32());
958                     const sal_uInt16  nMonth  = static_cast<sal_uInt16>(aStr.copy( 4, 2 ).toInt32());
959                     const sal_uInt16  nDay    = static_cast<sal_uInt16>(aStr.copy( 6, 2 ).toInt32());
960 
961                     const css::util::Date aDate(nDay,nMonth,nYear);
962                     *(*_rRow)[i] = aDate;
963                 }
964                 break;
965                 case DataType::DECIMAL:
966                     *(*_rRow)[i] = ORowSetValue(aStr);
967                 break;
968                 case DataType::BIT:
969                 {
970                     bool b;
971                     switch (*pData)
972                     {
973                         case 'T':
974                         case 'Y':
975                         case 'J':   b = true; break;
976                         default:    b = false; break;
977                     }
978                     *(*_rRow)[i] = b;
979                 }
980                 break;
981                 case DataType::LONGVARBINARY:
982                 case DataType::BINARY:
983                 case DataType::LONGVARCHAR:
984                 {
985                     const tools::Long nBlockNo = aStr.toInt32();   // read blocknumber
986                     if (nBlockNo > 0 && m_pMemoStream) // Read data from memo-file, only if
987                     {
988                         if ( !ReadMemo(nBlockNo, (*_rRow)[i]->get()) )
989                             break;
990                     }
991                     else
992                         (*_rRow)[i]->setNull();
993                 }   break;
994                 default:
995                     SAL_WARN( "connectivity.drivers","Wrong type");
996             }
997             (*_rRow)[i]->setTypeKind(nType);
998         }
999 
1000         nByteOffset += nLen;
1001         OSL_ENSURE( nByteOffset <= m_nBufferSize ,"ByteOffset > m_nBufferSize!");
1002     }
1003     return true;
1004 }
1005 
1006 
FileClose()1007 void ODbaseTable::FileClose()
1008 {
1009     ::osl::MutexGuard aGuard(m_aMutex);
1010     // if not everything has been written yet
1011     if (m_pMemoStream && m_pMemoStream->IsWritable())
1012         m_pMemoStream->Flush();
1013 
1014     m_pMemoStream.reset();
1015 
1016     ODbaseTable_BASE::FileClose();
1017 }
1018 
CreateImpl()1019 bool ODbaseTable::CreateImpl()
1020 {
1021     OSL_ENSURE(!m_pFileStream, "SequenceError");
1022 
1023     if ( m_pConnection->isCheckEnabled() && ::dbtools::convertName2SQLName(m_Name,OUString()) != m_Name )
1024     {
1025         const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution(
1026                 STR_SQL_NAME_ERROR,
1027                 "$name$", m_Name
1028              ) );
1029         ::dbtools::throwGenericSQLException( sError, *this );
1030     }
1031 
1032     INetURLObject aURL;
1033     aURL.SetSmartProtocol(INetProtocol::File);
1034     OUString aName = getEntry(m_pConnection, m_Name);
1035     if(aName.isEmpty())
1036     {
1037         OUString aIdent = m_pConnection->getContent()->getIdentifier()->getContentIdentifier();
1038         if ( aIdent.lastIndexOf('/') != (aIdent.getLength()-1) )
1039             aIdent += "/";
1040         aIdent += m_Name;
1041         aName = aIdent;
1042     }
1043     aURL.SetURL(aName);
1044 
1045     if ( !m_pConnection->matchesExtension( aURL.getExtension() ) )
1046         aURL.setExtension(m_pConnection->getExtension());
1047 
1048     try
1049     {
1050         Content aContent(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),Reference<XCommandEnvironment>(), comphelper::getProcessComponentContext());
1051         if (aContent.isDocument())
1052         {
1053             // Only if the file exists with length > 0 raise an error
1054             std::unique_ptr<SvStream> pFileStream(createStream_simpleError( aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::READ));
1055 
1056             if (pFileStream && pFileStream->TellEnd())
1057                 return false;
1058         }
1059     }
1060     catch(const Exception&) // an exception is thrown when no file exists
1061     {
1062     }
1063 
1064     bool bMemoFile = false;
1065 
1066     bool bOk = CreateFile(aURL, bMemoFile);
1067 
1068     FileClose();
1069 
1070     if (!bOk)
1071     {
1072         try
1073         {
1074             Content aContent(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),Reference<XCommandEnvironment>(), comphelper::getProcessComponentContext());
1075             aContent.executeCommand( "delete", css::uno::Any( true ) );
1076         }
1077         catch(const Exception&) // an exception is thrown when no file exists
1078         {
1079         }
1080         return false;
1081     }
1082 
1083     if (bMemoFile)
1084     {
1085         OUString aExt = aURL.getExtension();
1086         aURL.setExtension(u"dbt");                      // extension for memo file
1087 
1088         bool bMemoAlreadyExists = false;
1089         try
1090         {
1091             Content aMemo1Content(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),Reference<XCommandEnvironment>(), comphelper::getProcessComponentContext());
1092             bMemoAlreadyExists = aMemo1Content.isDocument();
1093         }
1094         catch(const Exception&) // an exception is thrown when no file exists
1095         {
1096         }
1097         if (bMemoAlreadyExists)
1098         {
1099             aURL.setExtension(aExt);      // kill dbf file
1100             try
1101             {
1102                 Content aMemoContent(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),Reference<XCommandEnvironment>(), comphelper::getProcessComponentContext());
1103                 aMemoContent.executeCommand( "delete", css::uno::Any( true ) );
1104             }
1105             catch(const Exception&)
1106             {
1107                 css::uno::Any anyEx = cppu::getCaughtException();
1108                 const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution(
1109                         STR_COULD_NOT_DELETE_FILE,
1110                         "$name$", aName
1111                      ) );
1112                 ::dbtools::throwGenericSQLException( sError, *this, anyEx );
1113             }
1114         }
1115         if (!CreateMemoFile(aURL))
1116         {
1117             aURL.setExtension(aExt);      // kill dbf file
1118             try
1119             {
1120                 Content aMemoContent(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),Reference<XCommandEnvironment>(), comphelper::getProcessComponentContext());
1121                 aMemoContent.executeCommand( "delete", css::uno::Any( true ) );
1122             }
1123             catch(const ContentCreationException&)
1124             {
1125                 css::uno::Any anyEx = cppu::getCaughtException();
1126                 const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution(
1127                         STR_COULD_NOT_DELETE_FILE,
1128                         "$name$", aName
1129                      ) );
1130                 ::dbtools::throwGenericSQLException( sError, *this, anyEx );
1131             }
1132             return false;
1133         }
1134         m_aHeader.type = dBaseIIIMemo;
1135     }
1136     else
1137         m_aHeader.type = dBaseIII;
1138 
1139     return true;
1140 }
1141 
throwInvalidColumnType(const char * pErrorId,const OUString & _sColumnName)1142 void ODbaseTable::throwInvalidColumnType(const char* pErrorId, const OUString& _sColumnName)
1143 {
1144     try
1145     {
1146         // we have to drop the file because it is corrupted now
1147         DropImpl();
1148     }
1149     catch(const Exception&)
1150     {
1151     }
1152 
1153     const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution(
1154             pErrorId,
1155             "$columnname$", _sColumnName
1156          ) );
1157     ::dbtools::throwGenericSQLException( sError, *this );
1158 }
1159 
1160 // creates in principle dBase IV file format
CreateFile(const INetURLObject & aFile,bool & bCreateMemo)1161 bool ODbaseTable::CreateFile(const INetURLObject& aFile, bool& bCreateMemo)
1162 {
1163     bCreateMemo = false;
1164     Date aDate( Date::SYSTEM );                     // current date
1165 
1166     m_pFileStream = createStream_simpleError( aFile.GetMainURL(INetURLObject::DecodeMechanism::NONE),StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE | StreamMode::TRUNC );
1167 
1168     if (!m_pFileStream)
1169         return false;
1170 
1171     sal_uInt8 nDbaseType = dBaseIII;
1172     Reference<XIndexAccess> xColumns(getColumns(),UNO_QUERY);
1173     Reference<XPropertySet> xCol;
1174     const OUString sPropType = OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE);
1175 
1176     try
1177     {
1178         const sal_Int32 nCount = xColumns->getCount();
1179         for(sal_Int32 i=0;i<nCount;++i)
1180         {
1181             xColumns->getByIndex(i) >>= xCol;
1182             OSL_ENSURE(xCol.is(),"This should be a column!");
1183 
1184             switch (getINT32(xCol->getPropertyValue(sPropType)))
1185             {
1186                 case DataType::DOUBLE:
1187                 case DataType::INTEGER:
1188                 case DataType::TIMESTAMP:
1189                 case DataType::LONGVARBINARY:
1190                     nDbaseType = VisualFoxPro;
1191                     i = nCount; // no more columns need to be checked
1192                     break;
1193             } // switch (getINT32(xCol->getPropertyValue(sPropType)))
1194         }
1195     }
1196     catch ( const Exception& )
1197     {
1198         try
1199         {
1200             // we have to drop the file because it is corrupted now
1201             DropImpl();
1202         }
1203         catch(const Exception&) { }
1204         throw;
1205     }
1206 
1207     char aBuffer[21] = {}; // write buffer
1208 
1209     m_pFileStream->Seek(0);
1210     (*m_pFileStream).WriteUChar( nDbaseType );                            // dBase format
1211     (*m_pFileStream).WriteUChar( aDate.GetYearUnsigned() % 100 );         // current date
1212 
1213 
1214     (*m_pFileStream).WriteUChar( aDate.GetMonth() );
1215     (*m_pFileStream).WriteUChar( aDate.GetDay() );
1216     (*m_pFileStream).WriteUInt32( 0 );                                    // number of data records
1217     (*m_pFileStream).WriteUInt16( (m_xColumns->getCount()+1) * 32 + 1 );  // header information,
1218                                                                           // pColumns contains always an additional column
1219     (*m_pFileStream).WriteUInt16( 0 );                                     // record length will be determined later
1220     m_pFileStream->WriteBytes(aBuffer, 20);
1221 
1222     sal_uInt16 nRecLength = 1;                                              // Length 1 for deleted flag
1223     sal_Int32  nMaxFieldLength = m_pConnection->getMetaData()->getMaxColumnNameLength();
1224     OUString aName;
1225     const OUString sPropName = OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME);
1226     const OUString sPropPrec = OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION);
1227     const OUString sPropScale = OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE);
1228 
1229     try
1230     {
1231         const sal_Int32 nCount = xColumns->getCount();
1232         for(sal_Int32 i=0;i<nCount;++i)
1233         {
1234             xColumns->getByIndex(i) >>= xCol;
1235             OSL_ENSURE(xCol.is(),"This should be a column!");
1236 
1237             char cTyp( 'C' );
1238 
1239             xCol->getPropertyValue(sPropName) >>= aName;
1240 
1241             OString aCol;
1242             if ( DBTypeConversion::convertUnicodeString( aName, aCol, m_eEncoding ) > nMaxFieldLength)
1243             {
1244                 throwInvalidColumnType( STR_INVALID_COLUMN_NAME_LENGTH, aName );
1245             }
1246 
1247             m_pFileStream->WriteOString( aCol );
1248             m_pFileStream->WriteBytes(aBuffer, 11 - aCol.getLength());
1249 
1250             sal_Int32 nPrecision = 0;
1251             xCol->getPropertyValue(sPropPrec) >>= nPrecision;
1252             sal_Int32 nScale = 0;
1253             xCol->getPropertyValue(sPropScale) >>= nScale;
1254 
1255             bool bBinary = false;
1256 
1257             switch (getINT32(xCol->getPropertyValue(sPropType)))
1258             {
1259                 case DataType::CHAR:
1260                 case DataType::VARCHAR:
1261                     cTyp = 'C';
1262                     break;
1263                 case DataType::DOUBLE:
1264                     if (getBOOL(xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISCURRENCY)))) // Currency will be treated separately
1265                         cTyp = 'Y';
1266                     else
1267                         cTyp = 'B';
1268                     break;
1269                 case DataType::INTEGER:
1270                     cTyp = 'I';
1271                     break;
1272                 case DataType::TINYINT:
1273                 case DataType::SMALLINT:
1274                 case DataType::BIGINT:
1275                 case DataType::DECIMAL:
1276                 case DataType::NUMERIC:
1277                 case DataType::REAL:
1278                     cTyp = 'N';                             // only dBase 3 format
1279                     break;
1280                 case DataType::TIMESTAMP:
1281                     cTyp = 'T';
1282                     break;
1283                 case DataType::DATE:
1284                     cTyp = 'D';
1285                     break;
1286                 case DataType::BIT:
1287                     cTyp = 'L';
1288                     break;
1289                 case DataType::LONGVARBINARY:
1290                     bBinary = true;
1291                     [[fallthrough]];
1292                 case DataType::LONGVARCHAR:
1293                     cTyp = 'M';
1294                     break;
1295                 default:
1296                     {
1297                         throwInvalidColumnType(STR_INVALID_COLUMN_TYPE, aName);
1298                     }
1299             }
1300 
1301             (*m_pFileStream).WriteChar( cTyp );
1302             if ( nDbaseType == VisualFoxPro )
1303                 (*m_pFileStream).WriteUInt32( nRecLength-1 );
1304             else
1305                 m_pFileStream->WriteBytes(aBuffer, 4);
1306 
1307             switch(cTyp)
1308             {
1309                 case 'C':
1310                     OSL_ENSURE(nPrecision < 255, "ODbaseTable::Create: Column too long!");
1311                     if (nPrecision > 254)
1312                     {
1313                         throwInvalidColumnType(STR_INVALID_COLUMN_PRECISION, aName);
1314                     }
1315                     (*m_pFileStream).WriteUChar( std::min(static_cast<unsigned>(nPrecision), 255U) );      // field length
1316                     nRecLength = nRecLength + static_cast<sal_uInt16>(std::min(static_cast<sal_uInt16>(nPrecision), sal_uInt16(255UL)));
1317                     (*m_pFileStream).WriteUChar( 0 );                                                                // decimals
1318                     break;
1319                 case 'F':
1320                 case 'N':
1321                     OSL_ENSURE(nPrecision >=  nScale,
1322                             "ODbaseTable::Create: Field length must be larger than decimal places!");
1323                     if (nPrecision <  nScale)
1324                     {
1325                         throwInvalidColumnType(STR_INVALID_PRECISION_SCALE, aName);
1326                     }
1327                     if (getBOOL(xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISCURRENCY)))) // Currency will be treated separately
1328                     {
1329                         (*m_pFileStream).WriteUChar( 10 );          // standard length
1330                         (*m_pFileStream).WriteUChar( 4 );
1331                         nRecLength += 10;
1332                     }
1333                     else
1334                     {
1335                         sal_Int32 nPrec = SvDbaseConverter::ConvertPrecisionToDbase(nPrecision,nScale);
1336 
1337                         (*m_pFileStream).WriteUChar( nPrec );
1338                         (*m_pFileStream).WriteUChar( nScale );
1339                         nRecLength += static_cast<sal_uInt16>(nPrec);
1340                     }
1341                     break;
1342                 case 'L':
1343                     (*m_pFileStream).WriteUChar( 1 );
1344                     (*m_pFileStream).WriteUChar( 0 );
1345                     ++nRecLength;
1346                     break;
1347                 case 'I':
1348                     (*m_pFileStream).WriteUChar( 4 );
1349                     (*m_pFileStream).WriteUChar( 0 );
1350                     nRecLength += 4;
1351                     break;
1352                 case 'Y':
1353                 case 'B':
1354                 case 'T':
1355                 case 'D':
1356                     (*m_pFileStream).WriteUChar( 8 );
1357                     (*m_pFileStream).WriteUChar( 0 );
1358                     nRecLength += 8;
1359                     break;
1360                 case 'M':
1361                     bCreateMemo = true;
1362                     (*m_pFileStream).WriteUChar( 10 );
1363                     (*m_pFileStream).WriteUChar( 0 );
1364                     nRecLength += 10;
1365                     if ( bBinary )
1366                         aBuffer[0] = 0x06;
1367                     break;
1368                 default:
1369                     throwInvalidColumnType(STR_INVALID_COLUMN_TYPE, aName);
1370             }
1371             m_pFileStream->WriteBytes(aBuffer, 14);
1372             aBuffer[0] = 0x00;
1373         }
1374 
1375         (*m_pFileStream).WriteUChar( FIELD_DESCRIPTOR_TERMINATOR );              // end of header
1376         (*m_pFileStream).WriteChar( char(DBF_EOL) );
1377         m_pFileStream->Seek(10);
1378         (*m_pFileStream).WriteUInt16( nRecLength );                                     // set record length afterwards
1379 
1380         if (bCreateMemo)
1381         {
1382             m_pFileStream->Seek(0);
1383             if (nDbaseType == VisualFoxPro)
1384                 (*m_pFileStream).WriteUChar( FoxProMemo );
1385             else
1386                 (*m_pFileStream).WriteUChar( dBaseIIIMemo );
1387         } // if (bCreateMemo)
1388     }
1389     catch ( const Exception& )
1390     {
1391         try
1392         {
1393             // we have to drop the file because it is corrupted now
1394             DropImpl();
1395         }
1396         catch(const Exception&) { }
1397         throw;
1398     }
1399     return true;
1400 }
1401 
1402 
1403 // creates in principle dBase III file format
CreateMemoFile(const INetURLObject & aFile)1404 bool ODbaseTable::CreateMemoFile(const INetURLObject& aFile)
1405 {
1406     // filehandling macro for table creation
1407     m_pMemoStream = createStream_simpleError( aFile.GetMainURL(INetURLObject::DecodeMechanism::NONE),StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE);
1408 
1409     if (!m_pMemoStream)
1410         return false;
1411 
1412     m_pMemoStream->SetStreamSize(512);
1413 
1414     m_pMemoStream->Seek(0);
1415     (*m_pMemoStream).WriteUInt32( 1 );                  // pointer to the first free block
1416 
1417     m_pMemoStream->Flush();
1418     m_pMemoStream.reset();
1419     return true;
1420 }
1421 
Drop_Static(const OUString & _sUrl,bool _bHasMemoFields,OCollection * _pIndexes)1422 bool ODbaseTable::Drop_Static(const OUString& _sUrl, bool _bHasMemoFields, OCollection* _pIndexes )
1423 {
1424     INetURLObject aURL;
1425     aURL.SetURL(_sUrl);
1426 
1427     bool bDropped = ::utl::UCBContentHelper::Kill(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE));
1428 
1429     if(bDropped)
1430     {
1431         if (_bHasMemoFields)
1432         {  // delete the memo fields
1433             aURL.setExtension(u"dbt");
1434             bDropped = ::utl::UCBContentHelper::Kill(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE));
1435         }
1436 
1437         if(bDropped)
1438         {
1439             if(_pIndexes)
1440             {
1441                 try
1442                 {
1443                     sal_Int32 i = _pIndexes->getCount();
1444                     while (i)
1445                     {
1446                         _pIndexes->dropByIndex(--i);
1447                     }
1448                 }
1449                 catch(const SQLException&)
1450                 {
1451                 }
1452             }
1453             aURL.setExtension(u"inf");
1454 
1455             // as the inf file does not necessarily exist, we aren't allowed to use UCBContentHelper::Kill
1456             try
1457             {
1458                 ::ucbhelper::Content aDeleteContent( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), Reference< XCommandEnvironment >(), comphelper::getProcessComponentContext() );
1459                 aDeleteContent.executeCommand( "delete", makeAny( true ) );
1460             }
1461             catch(const Exception&)
1462             {
1463                 // silently ignore this...
1464             }
1465         }
1466     }
1467     return bDropped;
1468 }
1469 
DropImpl()1470 bool ODbaseTable::DropImpl()
1471 {
1472     FileClose();
1473 
1474     if(!m_xIndexes)
1475         refreshIndexes(); // look for indexes which must be deleted as well
1476 
1477     bool bDropped = Drop_Static(getEntry(m_pConnection,m_Name),HasMemoFields(),m_xIndexes.get());
1478     if(!bDropped)
1479     {// we couldn't drop the table so we have to reopen it
1480         construct();
1481         if(m_xColumns)
1482             m_xColumns->refresh();
1483     }
1484     return bDropped;
1485 }
1486 
1487 
InsertRow(OValueRefVector & rRow,const Reference<XIndexAccess> & _xCols)1488 bool ODbaseTable::InsertRow(OValueRefVector& rRow, const Reference<XIndexAccess>& _xCols)
1489 {
1490     // fill buffer with blanks
1491     if (!AllocBuffer())
1492         return false;
1493 
1494     memset(m_pBuffer.get(), 0, m_aHeader.recordLength);
1495     m_pBuffer[0] = ' ';
1496 
1497     // Copy new row completely:
1498     // ... and add at the end as new Record:
1499     std::size_t nTempPos = m_nFilePos;
1500 
1501     m_nFilePos = static_cast<std::size_t>(m_aHeader.nbRecords) + 1;
1502     bool bInsertRow = UpdateBuffer( rRow, nullptr, _xCols, true );
1503     if ( bInsertRow )
1504     {
1505         std::size_t nFileSize = 0, nMemoFileSize = 0;
1506 
1507         nFileSize = lcl_getFileSize(*m_pFileStream);
1508 
1509         if (HasMemoFields() && m_pMemoStream)
1510         {
1511             m_pMemoStream->Seek(STREAM_SEEK_TO_END);
1512             nMemoFileSize = m_pMemoStream->Tell();
1513         }
1514 
1515         if (!WriteBuffer())
1516         {
1517             m_pFileStream->SetStreamSize(nFileSize);                // restore old size
1518 
1519             if (HasMemoFields() && m_pMemoStream)
1520                 m_pMemoStream->SetStreamSize(nMemoFileSize);    // restore old size
1521             m_nFilePos = nTempPos;              // restore file position
1522         }
1523         else
1524         {
1525             (*m_pFileStream).WriteChar( char(DBF_EOL) ); // write EOL
1526             // raise number of datasets in the header:
1527             m_pFileStream->Seek( 4 );
1528             (*m_pFileStream).WriteUInt32( m_aHeader.nbRecords + 1 );
1529 
1530             m_pFileStream->Flush();
1531 
1532             // raise number if successfully
1533             m_aHeader.nbRecords++;
1534             *rRow[0] = m_nFilePos;                                // set bookmark
1535             m_nFilePos = nTempPos;
1536         }
1537     }
1538     else
1539         m_nFilePos = nTempPos;
1540 
1541     return bInsertRow;
1542 }
1543 
1544 
UpdateRow(OValueRefVector & rRow,OValueRefRow & pOrgRow,const Reference<XIndexAccess> & _xCols)1545 bool ODbaseTable::UpdateRow(OValueRefVector& rRow, OValueRefRow& pOrgRow, const Reference<XIndexAccess>& _xCols)
1546 {
1547     // fill buffer with blanks
1548     if (!AllocBuffer())
1549         return false;
1550 
1551     // position on desired record:
1552     std::size_t nPos = m_aHeader.headerLength + static_cast<tools::Long>(m_nFilePos-1) * m_aHeader.recordLength;
1553     m_pFileStream->Seek(nPos);
1554     m_pFileStream->ReadBytes(m_pBuffer.get(), m_aHeader.recordLength);
1555 
1556     std::size_t nMemoFileSize( 0 );
1557     if (HasMemoFields() && m_pMemoStream)
1558     {
1559         m_pMemoStream->Seek(STREAM_SEEK_TO_END);
1560         nMemoFileSize = m_pMemoStream->Tell();
1561     }
1562     if (!UpdateBuffer(rRow, pOrgRow, _xCols, false) || !WriteBuffer())
1563     {
1564         if (HasMemoFields() && m_pMemoStream)
1565             m_pMemoStream->SetStreamSize(nMemoFileSize);    // restore old size
1566     }
1567     else
1568     {
1569         m_pFileStream->Flush();
1570     }
1571     return true;
1572 }
1573 
1574 
DeleteRow(const OSQLColumns & _rCols)1575 bool ODbaseTable::DeleteRow(const OSQLColumns& _rCols)
1576 {
1577     // Set the Delete-Flag (be it set or not):
1578     // Position on desired record:
1579     std::size_t nFilePos = m_aHeader.headerLength + static_cast<tools::Long>(m_nFilePos-1) * m_aHeader.recordLength;
1580     m_pFileStream->Seek(nFilePos);
1581 
1582     OValueRefRow aRow = new OValueRefVector(_rCols.size());
1583 
1584     if (!fetchRow(aRow,_rCols,true))
1585         return false;
1586 
1587     Reference<XPropertySet> xCol;
1588     OUString aColName;
1589     ::comphelper::UStringMixEqual aCase(isCaseSensitive());
1590     for (sal_Int32 i = 0; i < m_xColumns->getCount(); i++)
1591     {
1592         Reference<XPropertySet> xIndex = isUniqueByColumnName(i);
1593         if (xIndex.is())
1594         {
1595             xCol.set(m_xColumns->getByIndex(i), css::uno::UNO_QUERY);
1596             OSL_ENSURE(xCol.is(),"ODbaseTable::DeleteRow column is null!");
1597             if(xCol.is())
1598             {
1599                 xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= aColName;
1600 
1601                 Reference<XUnoTunnel> xTunnel(xIndex,UNO_QUERY);
1602                 OSL_ENSURE(xTunnel.is(),"No TunnelImplementation!");
1603                 ODbaseIndex* pIndex = reinterpret_cast< ODbaseIndex* >( xTunnel->getSomething(ODbaseIndex::getUnoTunnelId()) );
1604                 OSL_ENSURE(pIndex,"ODbaseTable::DeleteRow: No Index returned!");
1605 
1606                 OSQLColumns::const_iterator aIter = std::find_if(_rCols.begin(), _rCols.end(),
1607                     [&aCase, &aColName](const OSQLColumns::value_type& rxCol) {
1608                         return aCase(getString(rxCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_REALNAME))), aColName); });
1609                 if (aIter == _rCols.end())
1610                     continue;
1611 
1612                 auto nPos = static_cast<sal_Int32>(std::distance(_rCols.begin(), aIter)) + 1;
1613                 pIndex->Delete(m_nFilePos,*(*aRow)[nPos]);
1614             }
1615         }
1616     }
1617 
1618     m_pFileStream->Seek(nFilePos);
1619     (*m_pFileStream).WriteUChar( '*' ); // mark the row in the table as deleted
1620     m_pFileStream->Flush();
1621     return true;
1622 }
1623 
isUniqueByColumnName(sal_Int32 _nColumnPos)1624 Reference<XPropertySet> ODbaseTable::isUniqueByColumnName(sal_Int32 _nColumnPos)
1625 {
1626     if(!m_xIndexes)
1627         refreshIndexes();
1628     if(m_xIndexes->hasElements())
1629     {
1630         Reference<XPropertySet> xCol;
1631         m_xColumns->getByIndex(_nColumnPos) >>= xCol;
1632         OSL_ENSURE(xCol.is(),"ODbaseTable::isUniqueByColumnName column is null!");
1633         OUString sColName;
1634         xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= sColName;
1635 
1636         Reference<XPropertySet> xIndex;
1637         for(sal_Int32 i=0;i<m_xIndexes->getCount();++i)
1638         {
1639             xIndex.set(m_xIndexes->getByIndex(i), css::uno::UNO_QUERY);
1640             if(xIndex.is() && getBOOL(xIndex->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISUNIQUE))))
1641             {
1642                 Reference<XNameAccess> xCols(Reference<XColumnsSupplier>(xIndex,UNO_QUERY_THROW)->getColumns());
1643                 if(xCols->hasByName(sColName))
1644                     return xIndex;
1645 
1646             }
1647         }
1648     }
1649     return Reference<XPropertySet>();
1650 }
1651 
toDouble(const OString & rString)1652 static double toDouble(const OString& rString)
1653 {
1654     return ::rtl::math::stringToDouble( rString, '.', ',' );
1655 }
1656 
1657 
UpdateBuffer(OValueRefVector & rRow,const OValueRefRow & pOrgRow,const Reference<XIndexAccess> & _xCols,const bool bForceAllFields)1658 bool ODbaseTable::UpdateBuffer(OValueRefVector& rRow, const OValueRefRow& pOrgRow, const Reference<XIndexAccess>& _xCols, const bool bForceAllFields)
1659 {
1660     OSL_ENSURE(m_pBuffer,"Buffer is NULL!");
1661     if ( !m_pBuffer )
1662         return false;
1663     sal_Int32 nByteOffset  = 1;
1664 
1665     // Update fields:
1666     Reference<XPropertySet> xCol;
1667     Reference<XPropertySet> xIndex;
1668     OUString aColName;
1669     const sal_Int32 nColumnCount = m_xColumns->getCount();
1670     std::vector< Reference<XPropertySet> > aIndexedCols(nColumnCount);
1671 
1672     ::comphelper::UStringMixEqual aCase(isCaseSensitive());
1673 
1674     Reference<XIndexAccess> xColumns = m_xColumns;
1675     // first search a key that exist already in the table
1676     for (sal_Int32 i = 0; i < nColumnCount; ++i)
1677     {
1678         sal_Int32 nPos = i;
1679         if(_xCols != xColumns)
1680         {
1681             m_xColumns->getByIndex(i) >>= xCol;
1682             OSL_ENSURE(xCol.is(),"ODbaseTable::UpdateBuffer column is null!");
1683             xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= aColName;
1684 
1685             for(nPos = 0;nPos<_xCols->getCount();++nPos)
1686             {
1687                 Reference<XPropertySet> xFindCol(
1688                     _xCols->getByIndex(nPos), css::uno::UNO_QUERY);
1689                 OSL_ENSURE(xFindCol.is(),"ODbaseTable::UpdateBuffer column is null!");
1690                 if(aCase(getString(xFindCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME))),aColName))
1691                     break;
1692             }
1693             if (nPos >= _xCols->getCount())
1694                 continue;
1695         }
1696 
1697         ++nPos;
1698         xIndex = isUniqueByColumnName(i);
1699         aIndexedCols[i] = xIndex;
1700         if (xIndex.is())
1701         {
1702             // first check if the value is different to the old one and when if it conform to the index
1703             if(pOrgRow.is() && (rRow[nPos]->getValue().isNull() || rRow[nPos] == (*pOrgRow)[nPos]))
1704                 continue;
1705             else
1706             {
1707                 Reference<XUnoTunnel> xTunnel(xIndex,UNO_QUERY);
1708                 OSL_ENSURE(xTunnel.is(),"No TunnelImplementation!");
1709                 ODbaseIndex* pIndex = reinterpret_cast< ODbaseIndex* >( xTunnel->getSomething(ODbaseIndex::getUnoTunnelId()) );
1710                 OSL_ENSURE(pIndex,"ODbaseTable::UpdateBuffer: No Index returned!");
1711 
1712                 if (pIndex->Find(0,*rRow[nPos]))
1713                 {
1714                     // There is no unique value
1715                     if ( aColName.isEmpty() )
1716                     {
1717                         m_xColumns->getByIndex(i) >>= xCol;
1718                         OSL_ENSURE(xCol.is(),"ODbaseTable::UpdateBuffer column is null!");
1719                         xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= aColName;
1720                         xCol.clear();
1721                     } // if ( !aColName.getLength() )
1722                     const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution(
1723                             STR_DUPLICATE_VALUE_IN_COLUMN
1724                             ,"$columnname$", aColName
1725                          ) );
1726                     ::dbtools::throwGenericSQLException( sError, *this );
1727                 }
1728             }
1729         }
1730     }
1731 
1732     // when we are here there is no double key in the table
1733 
1734     for (sal_Int32 i = 0; i < nColumnCount && nByteOffset <= m_nBufferSize ; ++i)
1735     {
1736         // Lengths for each data type:
1737         assert(i >= 0);
1738         OSL_ENSURE(o3tl::make_unsigned(i) < m_aPrecisions.size(),"Illegal index!");
1739         sal_Int32 nLen = 0;
1740         sal_Int32 nType = 0;
1741         sal_Int32 nScale = 0;
1742         if ( o3tl::make_unsigned(i) < m_aPrecisions.size() )
1743         {
1744             nLen    = m_aPrecisions[i];
1745             nType   = m_aTypes[i];
1746             nScale  = m_aScales[i];
1747         }
1748         else
1749         {
1750             m_xColumns->getByIndex(i) >>= xCol;
1751             if ( xCol.is() )
1752             {
1753                 xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION)) >>= nLen;
1754                 xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE))      >>= nType;
1755                 xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE))     >>= nScale;
1756             }
1757         }
1758 
1759         bool bSetZero = false;
1760         switch (nType)
1761         {
1762             case DataType::INTEGER:
1763             case DataType::DOUBLE:
1764             case DataType::TIMESTAMP:
1765                 bSetZero = true;
1766                 [[fallthrough]];
1767             case DataType::LONGVARBINARY:
1768             case DataType::DATE:
1769             case DataType::BIT:
1770             case DataType::LONGVARCHAR:
1771                 nLen = m_aRealFieldLengths[i];
1772                 break;
1773             case DataType::DECIMAL:
1774                 nLen = SvDbaseConverter::ConvertPrecisionToDbase(nLen,nScale);
1775                 break;  // The sign and the comma
1776             default:
1777                 break;
1778 
1779         } // switch (nType)
1780 
1781         sal_Int32 nPos = i;
1782         if(_xCols != xColumns)
1783         {
1784             m_xColumns->getByIndex(i) >>= xCol;
1785             OSL_ENSURE(xCol.is(),"ODbaseTable::UpdateBuffer column is null!");
1786             xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= aColName;
1787             for(nPos = 0;nPos<_xCols->getCount();++nPos)
1788             {
1789                 Reference<XPropertySet> xFindCol(
1790                     _xCols->getByIndex(nPos), css::uno::UNO_QUERY);
1791                 if(aCase(getString(xFindCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME))),aColName))
1792                     break;
1793             }
1794             if (nPos >= _xCols->getCount())
1795             {
1796                 nByteOffset += nLen;
1797                 continue;
1798             }
1799         }
1800 
1801 
1802         ++nPos; // the row values start at 1
1803         const ORowSetValue &thisColVal = rRow[nPos]->get();
1804         const bool thisColIsBound = thisColVal.isBound();
1805         const bool thisColIsNull = !thisColIsBound || thisColVal.isNull();
1806         // don't overwrite non-bound columns
1807         if ( ! (bForceAllFields || thisColIsBound) )
1808         {
1809             // No - don't overwrite this field, it has not changed.
1810             nByteOffset += nLen;
1811             continue;
1812         }
1813         if (aIndexedCols[i].is())
1814         {
1815             Reference<XUnoTunnel> xTunnel(aIndexedCols[i],UNO_QUERY);
1816             OSL_ENSURE(xTunnel.is(),"No TunnelImplementation!");
1817             ODbaseIndex* pIndex = reinterpret_cast< ODbaseIndex* >( xTunnel->getSomething(ODbaseIndex::getUnoTunnelId()) );
1818             OSL_ENSURE(pIndex,"ODbaseTable::UpdateBuffer: No Index returned!");
1819             // Update !!
1820             if (pOrgRow.is() && !thisColIsNull)
1821                 pIndex->Update(m_nFilePos, *(*pOrgRow)[nPos], thisColVal);
1822             else
1823                 pIndex->Insert(m_nFilePos, thisColVal);
1824         }
1825 
1826         char* pData = reinterpret_cast<char *>(m_pBuffer.get() + nByteOffset);
1827         if (thisColIsNull)
1828         {
1829             if ( bSetZero )
1830                 memset(pData,0,nLen);   // Clear to NULL char ('\0')
1831             else
1832                 memset(pData,' ',nLen); // Clear to space/blank ('\0x20')
1833             nByteOffset += nLen;
1834             OSL_ENSURE( nByteOffset <= m_nBufferSize ,"ByteOffset > m_nBufferSize!");
1835             continue;
1836         }
1837 
1838         try
1839         {
1840             switch (nType)
1841             {
1842                 case DataType::TIMESTAMP:
1843                     {
1844                         sal_Int32 nJulianDate = 0, nJulianTime = 0;
1845                         lcl_CalcJulDate(nJulianDate,nJulianTime, thisColVal);
1846                         // Exactly 8 bytes to copy:
1847                         memcpy(pData,&nJulianDate,4);
1848                         memcpy(pData+4,&nJulianTime,4);
1849                     }
1850                     break;
1851                 case DataType::DATE:
1852                 {
1853                     css::util::Date aDate;
1854                     if(thisColVal.getTypeKind() == DataType::DOUBLE)
1855                         aDate = ::dbtools::DBTypeConversion::toDate(thisColVal.getDouble());
1856                     else
1857                         aDate = thisColVal;
1858                     char s[sizeof("-327686553565535")];
1859                         // reserve enough space for hypothetical max length
1860                     snprintf(s,
1861                         sizeof(s),
1862                         "%04" SAL_PRIdINT32 "%02" SAL_PRIuUINT32 "%02" SAL_PRIuUINT32,
1863                         static_cast<sal_Int32>(aDate.Year),
1864                         static_cast<sal_uInt32>(aDate.Month),
1865                         static_cast<sal_uInt32>(aDate.Day));
1866 
1867                     // Exactly 8 bytes to copy (even if s could hypothetically be longer):
1868                     memcpy(pData,s,8);
1869                 } break;
1870                 case DataType::INTEGER:
1871                     {
1872                         sal_Int32 nValue = thisColVal;
1873                         if (o3tl::make_unsigned(nLen) > sizeof(nValue))
1874                             return false;
1875                         memcpy(pData,&nValue,nLen);
1876                     }
1877                     break;
1878                 case DataType::DOUBLE:
1879                     {
1880                         const double d = thisColVal;
1881                         m_xColumns->getByIndex(i) >>= xCol;
1882 
1883                         if (getBOOL(xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISCURRENCY)))) // Currency is treated separately
1884                         {
1885                             sal_Int64 nValue = 0;
1886                             if ( m_aScales[i] )
1887                                 nValue = static_cast<sal_Int64>(d * pow(10.0,static_cast<int>(m_aScales[i])));
1888                             else
1889                                 nValue = static_cast<sal_Int64>(d);
1890                             if (o3tl::make_unsigned(nLen) > sizeof(nValue))
1891                                 return false;
1892                             memcpy(pData,&nValue,nLen);
1893                         } // if (getBOOL(xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISCURRENCY)))) // Currency is treated separately
1894                         else
1895                         {
1896                             if (o3tl::make_unsigned(nLen) > sizeof(d))
1897                                 return false;
1898                             memcpy(pData,&d,nLen);
1899                         }
1900                     }
1901                     break;
1902                 case DataType::DECIMAL:
1903                 {
1904                     memset(pData,' ',nLen); // Clear to NULL
1905 
1906                     const double n = thisColVal;
1907 
1908                     // one, because const_cast GetFormatPrecision on SvNumberFormat is not constant,
1909                     // even though it really could and should be
1910                     const OString aDefaultValue( ::rtl::math::doubleToString( n, rtl_math_StringFormat_F, nScale, '.', nullptr, 0));
1911                     const sal_Int32 nValueLen = aDefaultValue.getLength();
1912                     if ( nValueLen <= nLen )
1913                     {
1914                         // Write value right-justified, padded with blanks to the left.
1915                         memcpy(pData+nLen-nValueLen,aDefaultValue.getStr(),nValueLen);
1916                         // write the resulting double back
1917                         *rRow[nPos] = toDouble(aDefaultValue);
1918                     }
1919                     else
1920                     {
1921                         m_xColumns->getByIndex(i) >>= xCol;
1922                         OSL_ENSURE(xCol.is(),"ODbaseTable::UpdateBuffer column is null!");
1923                         xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= aColName;
1924                         std::vector< std::pair<const char* , OUString > > aStringToSubstitutes
1925                         {
1926                             { "$columnname$", aColName },
1927                             { "$precision$", OUString::number(nLen) },
1928                             { "$scale$", OUString::number(nScale) },
1929                             { "$value$", OStringToOUString(aDefaultValue,RTL_TEXTENCODING_UTF8) }
1930                         };
1931 
1932                         const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution(
1933                                 STR_INVALID_COLUMN_DECIMAL_VALUE
1934                                 ,aStringToSubstitutes
1935                              ) );
1936                         ::dbtools::throwGenericSQLException( sError, *this );
1937                     }
1938                 } break;
1939                 case DataType::BIT:
1940                     *pData = thisColVal.getBool() ? 'T' : 'F';
1941                     break;
1942                 case DataType::LONGVARBINARY:
1943                 case DataType::LONGVARCHAR:
1944                 {
1945                     char cNext = pData[nLen]; // Mark's scratch and replaced by 0
1946                     pData[nLen] = '\0';       // This is because the buffer is always a sign of greater ...
1947 
1948                     std::size_t nBlockNo = strtol(pData,nullptr,10); // Block number read
1949 
1950                     // Next initial character restore again:
1951                     pData[nLen] = cNext;
1952                     if (!m_pMemoStream)
1953                         break;
1954                     WriteMemo(thisColVal, nBlockNo);
1955 
1956                     OString aBlock(OString::number(nBlockNo));
1957                     //align aBlock at the right of a nLen sequence, fill to the left with '0'
1958                     OStringBuffer aStr;
1959                     comphelper::string::padToLength(aStr, nLen - aBlock.getLength(), '0');
1960                     aStr.append(aBlock);
1961 
1962                     // Copy characters:
1963                     memcpy(pData, aStr.getStr(), nLen);
1964                 }   break;
1965                 default:
1966                 {
1967                     memset(pData,' ',nLen); // Clear to NULL
1968 
1969                     OUString sStringToWrite( thisColVal.getString() );
1970 
1971                     // convert the string, using the connection's encoding
1972                     OString sEncoded;
1973 
1974                     DBTypeConversion::convertUnicodeStringToLength( sStringToWrite, sEncoded, nLen, m_eEncoding );
1975                     memcpy( pData, sEncoded.getStr(), sEncoded.getLength() );
1976 
1977                 }
1978                 break;
1979             }
1980         }
1981         catch( const SQLException&  )
1982         {
1983             throw;
1984         }
1985         catch ( const Exception& )
1986         {
1987             m_xColumns->getByIndex(i) >>= xCol;
1988             OSL_ENSURE( xCol.is(), "ODbaseTable::UpdateBuffer column is null!" );
1989             if ( xCol.is() )
1990                 xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= aColName;
1991 
1992             const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution(
1993                     STR_INVALID_COLUMN_VALUE,
1994                     "$columnname$", aColName
1995                  ) );
1996             ::dbtools::throwGenericSQLException( sError, *this );
1997         }
1998         // And more ...
1999         nByteOffset += nLen;
2000         OSL_ENSURE( nByteOffset <= m_nBufferSize ,"ByteOffset > m_nBufferSize!");
2001     }
2002     return true;
2003 }
2004 
2005 
WriteMemo(const ORowSetValue & aVariable,std::size_t & rBlockNr)2006 void ODbaseTable::WriteMemo(const ORowSetValue& aVariable, std::size_t& rBlockNr)
2007 {
2008     // if the BlockNo 0 is given, the block will be appended at the end
2009     std::size_t nSize = 0;
2010     OString aStr;
2011     css::uno::Sequence<sal_Int8> aValue;
2012     sal_uInt8 nHeader[4];
2013     const bool bBinary = aVariable.getTypeKind() == DataType::LONGVARBINARY && m_aMemoHeader.db_typ == MemoFoxPro;
2014     if ( bBinary )
2015     {
2016         aValue = aVariable.getSequence();
2017         nSize = aValue.getLength();
2018     }
2019     else
2020     {
2021         nSize = DBTypeConversion::convertUnicodeString( aVariable.getString(), aStr, m_eEncoding );
2022     }
2023 
2024     // append or overwrite
2025     bool bAppend = rBlockNr == 0;
2026 
2027     if (!bAppend)
2028     {
2029         switch (m_aMemoHeader.db_typ)
2030         {
2031             case MemodBaseIII: // dBase III-Memofield, ends with 2 * Ctrl-Z
2032                 bAppend = nSize > (512 - 2);
2033                 break;
2034             case MemoFoxPro:
2035             case MemodBaseIV: // dBase IV-Memofield with length
2036             {
2037                 char sHeader[4];
2038                 m_pMemoStream->Seek(rBlockNr * m_aMemoHeader.db_size);
2039                 m_pMemoStream->SeekRel(4);
2040                 m_pMemoStream->ReadBytes(sHeader, 4);
2041 
2042                 std::size_t nOldSize;
2043                 if (m_aMemoHeader.db_typ == MemoFoxPro)
2044                     nOldSize = ((static_cast<unsigned char>(sHeader[0]) * 256 +
2045                                  static_cast<unsigned char>(sHeader[1])) * 256 +
2046                                  static_cast<unsigned char>(sHeader[2])) * 256 +
2047                                  static_cast<unsigned char>(sHeader[3]);
2048                 else
2049                     nOldSize = ((static_cast<unsigned char>(sHeader[3]) * 256 +
2050                                  static_cast<unsigned char>(sHeader[2])) * 256 +
2051                                  static_cast<unsigned char>(sHeader[1])) * 256 +
2052                                  static_cast<unsigned char>(sHeader[0])  - 8;
2053 
2054                 // fits the new length in the used blocks
2055                 std::size_t nUsedBlocks = ((nSize + 8) / m_aMemoHeader.db_size) + (((nSize + 8) % m_aMemoHeader.db_size > 0) ? 1 : 0),
2056                       nOldUsedBlocks = ((nOldSize + 8) / m_aMemoHeader.db_size) + (((nOldSize + 8) % m_aMemoHeader.db_size > 0) ? 1 : 0);
2057                 bAppend = nUsedBlocks > nOldUsedBlocks;
2058             }
2059         }
2060     }
2061 
2062     if (bAppend)
2063     {
2064         sal_uInt64 const nStreamSize = m_pMemoStream->TellEnd();
2065         // fill last block
2066         rBlockNr = (nStreamSize / m_aMemoHeader.db_size) + ((nStreamSize % m_aMemoHeader.db_size) > 0 ? 1 : 0);
2067 
2068         m_pMemoStream->SetStreamSize(rBlockNr * m_aMemoHeader.db_size);
2069         m_pMemoStream->Seek(STREAM_SEEK_TO_END);
2070     }
2071     else
2072     {
2073         m_pMemoStream->Seek(rBlockNr * m_aMemoHeader.db_size);
2074     }
2075 
2076     switch (m_aMemoHeader.db_typ)
2077     {
2078         case MemodBaseIII: // dBase III-Memofield, ends with Ctrl-Z
2079         {
2080             const char cEOF = char(DBF_EOL);
2081             nSize++;
2082             m_pMemoStream->WriteBytes(aStr.getStr(), aStr.getLength());
2083             m_pMemoStream->WriteChar( cEOF ).WriteChar( cEOF );
2084         } break;
2085         case MemoFoxPro:
2086         case MemodBaseIV: // dBase IV-Memofield with length
2087         {
2088             if ( MemodBaseIV == m_aMemoHeader.db_typ )
2089                 (*m_pMemoStream).WriteUChar( 0xFF )
2090                                 .WriteUChar( 0xFF )
2091                                 .WriteUChar( 0x08 );
2092             else
2093                 (*m_pMemoStream).WriteUChar( 0x00 )
2094                                 .WriteUChar( 0x00 )
2095                                 .WriteUChar( 0x00 );
2096 
2097             sal_uInt32 nWriteSize = nSize;
2098             if (m_aMemoHeader.db_typ == MemoFoxPro)
2099             {
2100                 if ( bBinary )
2101                     (*m_pMemoStream).WriteUChar( 0x00 ); // Picture
2102                 else
2103                     (*m_pMemoStream).WriteUChar( 0x01 ); // Memo
2104                 for (int i = 4; i > 0; nWriteSize >>= 8)
2105                     nHeader[--i] = static_cast<sal_uInt8>(nWriteSize % 256);
2106             }
2107             else
2108             {
2109                 (*m_pMemoStream).WriteUChar( 0x00 );
2110                 nWriteSize += 8;
2111                 for (int i = 0; i < 4; nWriteSize >>= 8)
2112                     nHeader[i++] = static_cast<sal_uInt8>(nWriteSize % 256);
2113             }
2114 
2115             m_pMemoStream->WriteBytes(nHeader, 4);
2116             if ( bBinary )
2117                 m_pMemoStream->WriteBytes(aValue.getConstArray(), aValue.getLength());
2118             else
2119                 m_pMemoStream->WriteBytes(aStr.getStr(), aStr.getLength());
2120             m_pMemoStream->Flush();
2121         }
2122     }
2123 
2124 
2125     // Write the new block number
2126     if (bAppend)
2127     {
2128         sal_uInt64 const nStreamSize = m_pMemoStream->TellEnd();
2129         m_aMemoHeader.db_next = (nStreamSize / m_aMemoHeader.db_size) + ((nStreamSize % m_aMemoHeader.db_size) > 0 ? 1 : 0);
2130 
2131         // Write the new block number
2132         m_pMemoStream->Seek(0);
2133         (*m_pMemoStream).WriteUInt32( m_aMemoHeader.db_next );
2134         m_pMemoStream->Flush();
2135     }
2136 }
2137 
2138 
2139 // XAlterTable
alterColumnByName(const OUString & colName,const Reference<XPropertySet> & descriptor)2140 void SAL_CALL ODbaseTable::alterColumnByName( const OUString& colName, const Reference< XPropertySet >& descriptor )
2141 {
2142     ::osl::MutexGuard aGuard(m_aMutex);
2143     checkDisposed(OTableDescriptor_BASE::rBHelper.bDisposed);
2144 
2145 
2146     Reference<XDataDescriptorFactory> xOldColumn;
2147     m_xColumns->getByName(colName) >>= xOldColumn;
2148 
2149     try
2150     {
2151         alterColumn(m_xColumns->findColumn(colName)-1,descriptor,xOldColumn);
2152     }
2153     catch (const css::lang::IndexOutOfBoundsException&)
2154     {
2155         throw NoSuchElementException(colName, *this);
2156     }
2157 }
2158 
alterColumnByIndex(sal_Int32 index,const Reference<XPropertySet> & descriptor)2159 void SAL_CALL ODbaseTable::alterColumnByIndex( sal_Int32 index, const Reference< XPropertySet >& descriptor )
2160 {
2161     ::osl::MutexGuard aGuard(m_aMutex);
2162     checkDisposed(OTableDescriptor_BASE::rBHelper.bDisposed);
2163 
2164     if(index < 0 || index >= m_xColumns->getCount())
2165         throw IndexOutOfBoundsException(OUString::number(index),*this);
2166 
2167     Reference<XDataDescriptorFactory> xOldColumn;
2168     m_xColumns->getByIndex(index) >>= xOldColumn;
2169     alterColumn(index,descriptor,xOldColumn);
2170 }
2171 
alterColumn(sal_Int32 index,const Reference<XPropertySet> & descriptor,const Reference<XDataDescriptorFactory> & xOldColumn)2172 void ODbaseTable::alterColumn(sal_Int32 index,
2173                               const Reference< XPropertySet >& descriptor ,
2174                               const Reference< XDataDescriptorFactory >& xOldColumn )
2175 {
2176     if(index < 0 || index >= m_xColumns->getCount())
2177         throw IndexOutOfBoundsException(OUString::number(index),*this);
2178 
2179     try
2180     {
2181         OSL_ENSURE(descriptor.is(),"ODbaseTable::alterColumn: descriptor can not be null!");
2182         // creates a copy of the original column and copy all properties from descriptor in xCopyColumn
2183         Reference<XPropertySet> xCopyColumn;
2184         if(xOldColumn.is())
2185             xCopyColumn = xOldColumn->createDataDescriptor();
2186         else
2187             xCopyColumn = new OColumn(getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers());
2188 
2189         ::comphelper::copyProperties(descriptor,xCopyColumn);
2190 
2191         // creates a temp file
2192 
2193         OUString sTempName = createTempFile();
2194 
2195         rtl::Reference<ODbaseTable> pNewTable = new ODbaseTable(m_pTables,static_cast<ODbaseConnection*>(m_pConnection));
2196         Reference<XPropertySet> xHoldTable = pNewTable;
2197         pNewTable->setPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME),makeAny(sTempName));
2198         Reference<XAppend> xAppend(pNewTable->getColumns(),UNO_QUERY);
2199         OSL_ENSURE(xAppend.is(),"ODbaseTable::alterColumn: No XAppend interface!");
2200 
2201         // copy the structure
2202         sal_Int32 i=0;
2203         for(;i < index;++i)
2204         {
2205             Reference<XPropertySet> xProp;
2206             m_xColumns->getByIndex(i) >>= xProp;
2207             Reference<XDataDescriptorFactory> xColumn(xProp,UNO_QUERY);
2208             Reference<XPropertySet> xCpy;
2209             if(xColumn.is())
2210                 xCpy = xColumn->createDataDescriptor();
2211             else
2212                 xCpy = new OColumn(getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers());
2213             ::comphelper::copyProperties(xProp,xCpy);
2214             xAppend->appendByDescriptor(xCpy);
2215         }
2216         ++i; // now insert our new column
2217         xAppend->appendByDescriptor(xCopyColumn);
2218 
2219         for(;i < m_xColumns->getCount();++i)
2220         {
2221             Reference<XPropertySet> xProp;
2222             m_xColumns->getByIndex(i) >>= xProp;
2223             Reference<XDataDescriptorFactory> xColumn(xProp,UNO_QUERY);
2224             Reference<XPropertySet> xCpy;
2225             if(xColumn.is())
2226                 xCpy = xColumn->createDataDescriptor();
2227             else
2228                 xCpy = new OColumn(getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers());
2229             ::comphelper::copyProperties(xProp,xCpy);
2230             xAppend->appendByDescriptor(xCpy);
2231         }
2232 
2233         // construct the new table
2234         if(!pNewTable->CreateImpl())
2235         {
2236             const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution(
2237                     STR_COLUMN_NOT_ALTERABLE,
2238                     "$columnname$", ::comphelper::getString(descriptor->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)))
2239                  ) );
2240             ::dbtools::throwGenericSQLException( sError, *this );
2241         }
2242 
2243         pNewTable->construct();
2244 
2245         // copy the data
2246         copyData(pNewTable.get(),0);
2247 
2248         // now drop the old one
2249         if( DropImpl() ) // we don't want to delete the memo columns too
2250         {
2251             try
2252             {
2253                 // rename the new one to the old one
2254                 pNewTable->renameImpl(m_Name);
2255             }
2256             catch(const css::container::ElementExistException&)
2257             {
2258                 const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution(
2259                         STR_COULD_NOT_DELETE_FILE,
2260                         "$filename$", m_Name
2261                      ) );
2262                 ::dbtools::throwGenericSQLException( sError, *this );
2263             }
2264             // release the temp file
2265             pNewTable = nullptr;
2266             ::comphelper::disposeComponent(xHoldTable);
2267         }
2268         else
2269         {
2270             pNewTable = nullptr;
2271         }
2272         FileClose();
2273         construct();
2274         if(m_xColumns)
2275             m_xColumns->refresh();
2276 
2277     }
2278     catch(const SQLException&)
2279     {
2280         throw;
2281     }
2282     catch(const Exception&)
2283     {
2284         TOOLS_WARN_EXCEPTION( "connectivity.drivers","");
2285         throw;
2286     }
2287 }
2288 
getMetaData() const2289 Reference< XDatabaseMetaData> ODbaseTable::getMetaData() const
2290 {
2291     return getConnection()->getMetaData();
2292 }
2293 
rename(const OUString & newName)2294 void SAL_CALL ODbaseTable::rename( const OUString& newName )
2295 {
2296     ::osl::MutexGuard aGuard(m_aMutex);
2297     checkDisposed(OTableDescriptor_BASE::rBHelper.bDisposed);
2298     if(m_pTables && m_pTables->hasByName(newName))
2299         throw ElementExistException(newName,*this);
2300 
2301 
2302     renameImpl(newName);
2303 
2304     ODbaseTable_BASE::rename(newName);
2305 
2306     construct();
2307     if(m_xColumns)
2308         m_xColumns->refresh();
2309 }
2310 namespace
2311 {
renameFile(file::OConnection const * _pConnection,std::u16string_view oldName,const OUString & newName,std::u16string_view _sExtension)2312     void renameFile(file::OConnection const * _pConnection,std::u16string_view oldName,
2313                     const OUString& newName, std::u16string_view _sExtension)
2314     {
2315         OUString aName = ODbaseTable::getEntry(_pConnection,oldName);
2316         if(aName.isEmpty())
2317         {
2318             OUString aIdent = _pConnection->getContent()->getIdentifier()->getContentIdentifier();
2319             if ( aIdent.lastIndexOf('/') != (aIdent.getLength()-1) )
2320                 aIdent += "/";
2321             aIdent += oldName;
2322             aName = aIdent;
2323         }
2324         INetURLObject aURL;
2325         aURL.SetURL(aName);
2326 
2327         aURL.setExtension( _sExtension );
2328         OUString sNewName(newName + "." + _sExtension);
2329 
2330         try
2331         {
2332             Content aContent(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),Reference<XCommandEnvironment>(), comphelper::getProcessComponentContext());
2333 
2334             Sequence< PropertyValue > aProps( 1 );
2335             aProps[0].Name      = "Title";
2336             aProps[0].Handle    = -1; // n/a
2337             aProps[0].Value     <<= sNewName;
2338             Sequence< Any > aValues;
2339             aContent.executeCommand( "setPropertyValues",makeAny(aProps) ) >>= aValues;
2340             if(aValues.hasElements() && aValues[0].hasValue())
2341                 throw Exception("setPropertyValues returned non-zero", nullptr);
2342         }
2343         catch(const Exception&)
2344         {
2345             throw ElementExistException(newName);
2346         }
2347     }
2348 }
2349 
renameImpl(const OUString & newName)2350 void ODbaseTable::renameImpl( const OUString& newName )
2351 {
2352     ::osl::MutexGuard aGuard(m_aMutex);
2353 
2354     FileClose();
2355 
2356 
2357     renameFile(m_pConnection,m_Name,newName,m_pConnection->getExtension());
2358     if ( HasMemoFields() )
2359     {  // delete the memo fields
2360         renameFile(m_pConnection,m_Name,newName,u"dbt");
2361     }
2362 }
2363 
addColumn(const Reference<XPropertySet> & _xNewColumn)2364 void ODbaseTable::addColumn(const Reference< XPropertySet >& _xNewColumn)
2365 {
2366     OUString sTempName = createTempFile();
2367 
2368     rtl::Reference xNewTable(new ODbaseTable(m_pTables,static_cast<ODbaseConnection*>(m_pConnection)));
2369     xNewTable->setPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME),makeAny(sTempName));
2370     {
2371         Reference<XAppend> xAppend(xNewTable->getColumns(),UNO_QUERY);
2372         bool bCase = getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers();
2373         // copy the structure
2374         for(sal_Int32 i=0;i < m_xColumns->getCount();++i)
2375         {
2376             Reference<XPropertySet> xProp;
2377             m_xColumns->getByIndex(i) >>= xProp;
2378             Reference<XDataDescriptorFactory> xColumn(xProp,UNO_QUERY);
2379             Reference<XPropertySet> xCpy;
2380             if(xColumn.is())
2381                 xCpy = xColumn->createDataDescriptor();
2382             else
2383             {
2384                 xCpy = new OColumn(bCase);
2385                 ::comphelper::copyProperties(xProp,xCpy);
2386             }
2387 
2388             xAppend->appendByDescriptor(xCpy);
2389         }
2390         Reference<XPropertySet> xCpy = new OColumn(bCase);
2391         ::comphelper::copyProperties(_xNewColumn,xCpy);
2392         xAppend->appendByDescriptor(xCpy);
2393     }
2394 
2395     // construct the new table
2396     if(!xNewTable->CreateImpl())
2397     {
2398         const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution(
2399                 STR_COLUMN_NOT_ADDABLE,
2400                 "$columnname$", ::comphelper::getString(_xNewColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)))
2401              ) );
2402         ::dbtools::throwGenericSQLException( sError, *this );
2403     }
2404 
2405     xNewTable->construct();
2406     // copy the data
2407     copyData(xNewTable.get(),xNewTable->m_xColumns->getCount());
2408     // drop the old table
2409     if(DropImpl())
2410     {
2411         xNewTable->renameImpl(m_Name);
2412         // release the temp file
2413     }
2414     xNewTable.clear();
2415 
2416     FileClose();
2417     construct();
2418     if(m_xColumns)
2419         m_xColumns->refresh();
2420 }
2421 
dropColumn(sal_Int32 _nPos)2422 void ODbaseTable::dropColumn(sal_Int32 _nPos)
2423 {
2424     OUString sTempName = createTempFile();
2425 
2426     rtl::Reference xNewTable(new ODbaseTable(m_pTables,static_cast<ODbaseConnection*>(m_pConnection)));
2427     xNewTable->setPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME),makeAny(sTempName));
2428     {
2429         Reference<XAppend> xAppend(xNewTable->getColumns(),UNO_QUERY);
2430         bool bCase = getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers();
2431         // copy the structure
2432         for(sal_Int32 i=0;i < m_xColumns->getCount();++i)
2433         {
2434             if(_nPos != i)
2435             {
2436                 Reference<XPropertySet> xProp;
2437                 m_xColumns->getByIndex(i) >>= xProp;
2438                 Reference<XDataDescriptorFactory> xColumn(xProp,UNO_QUERY);
2439                 Reference<XPropertySet> xCpy;
2440                 if(xColumn.is())
2441                     xCpy = xColumn->createDataDescriptor();
2442                 else
2443                 {
2444                     xCpy = new OColumn(bCase);
2445                     ::comphelper::copyProperties(xProp,xCpy);
2446                 }
2447                 xAppend->appendByDescriptor(xCpy);
2448             }
2449         }
2450     }
2451 
2452     // construct the new table
2453     if(!xNewTable->CreateImpl())
2454     {
2455         const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution(
2456                 STR_COLUMN_NOT_DROP,
2457                 "$position$", OUString::number(_nPos)
2458              ) );
2459         ::dbtools::throwGenericSQLException( sError, *this );
2460     }
2461     xNewTable->construct();
2462     // copy the data
2463     copyData(xNewTable.get(),_nPos);
2464     // drop the old table
2465     if(DropImpl())
2466         xNewTable->renameImpl(m_Name);
2467         // release the temp file
2468 
2469     xNewTable.clear();
2470 
2471     FileClose();
2472     construct();
2473 }
2474 
createTempFile()2475 OUString ODbaseTable::createTempFile()
2476 {
2477     OUString aIdent = m_pConnection->getContent()->getIdentifier()->getContentIdentifier();
2478     if ( aIdent.lastIndexOf('/') != (aIdent.getLength()-1) )
2479         aIdent += "/";
2480 
2481     OUString sTempName(aIdent);
2482     OUString sExt("." + m_pConnection->getExtension());
2483     OUString sName(m_Name);
2484     TempFile aTempFile(sName, true, &sExt, &sTempName);
2485     if(!aTempFile.IsValid())
2486         getConnection()->throwGenericSQLException(STR_COULD_NOT_ALTER_TABLE, *this);
2487 
2488     INetURLObject aURL;
2489     aURL.SetSmartProtocol(INetProtocol::File);
2490     aURL.SetURL(aTempFile.GetURL());
2491 
2492     OUString sNewName(aURL.getName().copy(0, aURL.getName().getLength() - sExt.getLength()));
2493 
2494     return sNewName;
2495 }
2496 
copyData(ODbaseTable * _pNewTable,sal_Int32 _nPos)2497 void ODbaseTable::copyData(ODbaseTable* _pNewTable,sal_Int32 _nPos)
2498 {
2499     sal_Int32 nPos = _nPos + 1; // +1 because we always have the bookmark column as well
2500     OValueRefRow aRow = new OValueRefVector(m_xColumns->getCount());
2501     OValueRefRow aInsertRow;
2502     if(_nPos)
2503     {
2504         aInsertRow = new OValueRefVector(_pNewTable->m_xColumns->getCount());
2505         std::for_each(aInsertRow->begin(),aInsertRow->end(),TSetRefBound(true));
2506     }
2507     else
2508         aInsertRow = aRow;
2509 
2510     // we only have to bind the values which we need to copy into the new table
2511     std::for_each(aRow->begin(),aRow->end(),TSetRefBound(true));
2512     if(_nPos && (_nPos < static_cast<sal_Int32>(aRow->size())))
2513         (*aRow)[nPos]->setBound(false);
2514 
2515 
2516     sal_Int32 nCurPos;
2517     OValueRefVector::const_iterator aIter;
2518     for(sal_uInt32 nRowPos = 0; nRowPos < m_aHeader.nbRecords;++nRowPos)
2519     {
2520         bool bOk = seekRow( IResultSetHelper::BOOKMARK, nRowPos+1, nCurPos );
2521         if ( bOk )
2522         {
2523             bOk = fetchRow( aRow, *m_aColumns, true);
2524             if ( bOk && !aRow->isDeleted() ) // copy only not deleted rows
2525             {
2526                 // special handling when pos == 0 then we don't have to distinguish between the two rows
2527                 if(_nPos)
2528                 {
2529                     aIter = aRow->begin()+1;
2530                     sal_Int32 nCount = 1;
2531                     for(OValueRefVector::iterator aInsertIter = aInsertRow->begin()+1; aIter != aRow->end() && aInsertIter != aInsertRow->end();++aIter,++nCount)
2532                     {
2533                         if(nPos != nCount)
2534                         {
2535                             (*aInsertIter)->setValue( (*aIter)->getValue() );
2536                             ++aInsertIter;
2537                         }
2538                     }
2539                 }
2540                 bOk = _pNewTable->InsertRow(*aInsertRow,_pNewTable->m_xColumns);
2541                 SAL_WARN_IF(!bOk, "connectivity.drivers", "Row could not be inserted!");
2542             }
2543             else
2544             {
2545                 SAL_WARN_IF(!bOk, "connectivity.drivers", "Row could not be fetched!");
2546             }
2547         }
2548         else
2549         {
2550             OSL_ASSERT(false);
2551         }
2552     } // for(sal_uInt32 nRowPos = 0; nRowPos < m_aHeader.db_anz;++nRowPos)
2553 }
2554 
throwInvalidDbaseFormat()2555 void ODbaseTable::throwInvalidDbaseFormat()
2556 {
2557     FileClose();
2558     // no dbase file
2559 
2560     const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution(
2561                 STR_INVALID_DBASE_FILE,
2562                 "$filename$", getEntry(m_pConnection,m_Name)
2563              ) );
2564     ::dbtools::throwGenericSQLException( sError, *this );
2565 }
2566 
refreshHeader()2567 void ODbaseTable::refreshHeader()
2568 {
2569     if ( m_aHeader.nbRecords == 0 )
2570         readHeader();
2571 }
2572 
seekRow(IResultSetHelper::Movement eCursorPosition,sal_Int32 nOffset,sal_Int32 & nCurPos)2573 bool ODbaseTable::seekRow(IResultSetHelper::Movement eCursorPosition, sal_Int32 nOffset, sal_Int32& nCurPos)
2574 {
2575     // prepare positioning:
2576     OSL_ENSURE(m_pFileStream,"ODbaseTable::seekRow: FileStream is NULL!");
2577 
2578     sal_uInt32  nNumberOfRecords = m_aHeader.nbRecords;
2579     sal_uInt32 nTempPos = m_nFilePos;
2580     m_nFilePos = nCurPos;
2581 
2582     switch(eCursorPosition)
2583     {
2584         case IResultSetHelper::NEXT:
2585             ++m_nFilePos;
2586             break;
2587         case IResultSetHelper::PRIOR:
2588             if (m_nFilePos > 0)
2589                 --m_nFilePos;
2590             break;
2591         case IResultSetHelper::FIRST:
2592             m_nFilePos = 1;
2593             break;
2594         case IResultSetHelper::LAST:
2595             m_nFilePos = nNumberOfRecords;
2596             break;
2597         case IResultSetHelper::RELATIVE1:
2598             m_nFilePos = (m_nFilePos + nOffset < 0) ? 0
2599                             : static_cast<sal_uInt32>(m_nFilePos + nOffset);
2600             break;
2601         case IResultSetHelper::ABSOLUTE1:
2602         case IResultSetHelper::BOOKMARK:
2603             m_nFilePos = static_cast<sal_uInt32>(nOffset);
2604             break;
2605     }
2606 
2607     if (m_nFilePos > static_cast<sal_Int32>(nNumberOfRecords))
2608         m_nFilePos = static_cast<sal_Int32>(nNumberOfRecords) + 1;
2609 
2610     if (m_nFilePos == 0 || m_nFilePos == static_cast<sal_Int32>(nNumberOfRecords) + 1)
2611         goto Error;
2612     else
2613     {
2614         std::size_t nEntryLen = m_aHeader.recordLength;
2615 
2616         OSL_ENSURE(m_nFilePos >= 1,"SdbDBFCursor::FileFetchRow: invalid record position");
2617         std::size_t nPos = m_aHeader.headerLength + static_cast<std::size_t>(m_nFilePos-1) * nEntryLen;
2618 
2619         m_pFileStream->Seek(nPos);
2620         if (m_pFileStream->GetError() != ERRCODE_NONE)
2621             goto Error;
2622 
2623         std::size_t nRead = m_pFileStream->ReadBytes(m_pBuffer.get(), nEntryLen);
2624         if (nRead != nEntryLen)
2625         {
2626             SAL_WARN("connectivity.drivers", "ODbaseTable::seekRow: short read!");
2627             goto Error;
2628         }
2629         if (m_pFileStream->GetError() != ERRCODE_NONE)
2630             goto Error;
2631     }
2632     goto End;
2633 
2634 Error:
2635     switch(eCursorPosition)
2636     {
2637         case IResultSetHelper::PRIOR:
2638         case IResultSetHelper::FIRST:
2639             m_nFilePos = 0;
2640             break;
2641         case IResultSetHelper::LAST:
2642         case IResultSetHelper::NEXT:
2643         case IResultSetHelper::ABSOLUTE1:
2644         case IResultSetHelper::RELATIVE1:
2645             if (nOffset > 0)
2646                 m_nFilePos = nNumberOfRecords + 1;
2647             else if (nOffset < 0)
2648                 m_nFilePos = 0;
2649             break;
2650         case IResultSetHelper::BOOKMARK:
2651             m_nFilePos = nTempPos;   // last position
2652     }
2653     return false;
2654 
2655 End:
2656     nCurPos = m_nFilePos;
2657     return true;
2658 }
2659 
ReadMemo(std::size_t nBlockNo,ORowSetValue & aVariable)2660 bool ODbaseTable::ReadMemo(std::size_t nBlockNo, ORowSetValue& aVariable)
2661 {
2662     m_pMemoStream->Seek(nBlockNo * m_aMemoHeader.db_size);
2663     switch (m_aMemoHeader.db_typ)
2664     {
2665         case MemodBaseIII: // dBase III-Memofield, ends with Ctrl-Z
2666         {
2667             const char cEOF = char(DBF_EOL);
2668             OStringBuffer aBStr;
2669             static char aBuf[514];
2670             aBuf[512] = 0;          // avoid random value
2671             bool bReady = false;
2672 
2673             do
2674             {
2675                 m_pMemoStream->ReadBytes(&aBuf, 512);
2676 
2677                 sal_uInt16 i = 0;
2678                 while (aBuf[i] != cEOF && ++i < 512)
2679                     ;
2680                 bReady = aBuf[i] == cEOF;
2681 
2682                 aBuf[i] = 0;
2683                 aBStr.append(aBuf);
2684 
2685             } while (!bReady && !m_pMemoStream->eof());
2686 
2687             aVariable = OStringToOUString(aBStr.makeStringAndClear(),
2688                 m_eEncoding);
2689 
2690         } break;
2691         case MemoFoxPro:
2692         case MemodBaseIV: // dBase IV-Memofield with length
2693         {
2694             bool bIsText = true;
2695             char sHeader[4];
2696             m_pMemoStream->ReadBytes(sHeader, 4);
2697             // Foxpro stores text and binary data
2698             if (m_aMemoHeader.db_typ == MemoFoxPro)
2699             {
2700                 bIsText = sHeader[3] != 0;
2701             }
2702             else if (static_cast<sal_uInt8>(sHeader[0]) != 0xFF || static_cast<sal_uInt8>(sHeader[1]) != 0xFF || static_cast<sal_uInt8>(sHeader[2]) != 0x08)
2703             {
2704                 return false;
2705             }
2706 
2707             sal_uInt32 nLength(0);
2708             (*m_pMemoStream).ReadUInt32( nLength );
2709 
2710             if (m_aMemoHeader.db_typ == MemodBaseIV)
2711                 nLength -= 8;
2712 
2713             if ( nLength )
2714             {
2715                 if ( bIsText )
2716                 {
2717                     OStringBuffer aBuffer(read_uInt8s_ToOString(*m_pMemoStream, nLength));
2718                     //pad it out with ' ' to expected length on short read
2719                     sal_Int32 nRequested = sal::static_int_cast<sal_Int32>(nLength);
2720                     comphelper::string::padToLength(aBuffer, nRequested, ' ');
2721                     aVariable = OStringToOUString(aBuffer.makeStringAndClear(), m_eEncoding);
2722                 } // if ( bIsText )
2723                 else
2724                 {
2725                     css::uno::Sequence< sal_Int8 > aData(nLength);
2726                     m_pMemoStream->ReadBytes(aData.getArray(), nLength);
2727                     aVariable = aData;
2728                 }
2729             } // if ( nLength )
2730         }
2731     }
2732     return true;
2733 }
2734 
AllocBuffer()2735 bool ODbaseTable::AllocBuffer()
2736 {
2737     sal_uInt16 nSize = m_aHeader.recordLength;
2738     SAL_WARN_IF(nSize == 0, "connectivity.drivers", "Size too small");
2739 
2740     if (m_nBufferSize != nSize)
2741     {
2742         m_pBuffer.reset();
2743     }
2744 
2745     // if there is no buffer available: allocate:
2746     if (!m_pBuffer && nSize > 0)
2747     {
2748         m_nBufferSize = nSize;
2749         m_pBuffer.reset(new sal_uInt8[m_nBufferSize+1]);
2750     }
2751 
2752     return m_pBuffer != nullptr;
2753 }
2754 
WriteBuffer()2755 bool ODbaseTable::WriteBuffer()
2756 {
2757     OSL_ENSURE(m_nFilePos >= 1,"SdbDBFCursor::FileFetchRow: invalid record position");
2758 
2759     // position on desired record:
2760     std::size_t nPos = m_aHeader.headerLength + static_cast<tools::Long>(m_nFilePos-1) * m_aHeader.recordLength;
2761     m_pFileStream->Seek(nPos);
2762     return m_pFileStream->WriteBytes(m_pBuffer.get(), m_aHeader.recordLength) > 0;
2763 }
2764 
getCurrentLastPos() const2765 sal_Int32 ODbaseTable::getCurrentLastPos() const
2766 {
2767     return m_aHeader.nbRecords;
2768 }
2769 
2770 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2771