Coverage Report

Created: 2025-12-31 10:39

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