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