Coverage Report

Created: 2026-08-11 08:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/mitab/mitab_tabview.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * Name:     mitab_tabfile.cpp
4
 * Project:  MapInfo TAB Read/Write library
5
 * Language: C++
6
 * Purpose:  Implementation of the TABView class, used to handle .TAB
7
 *           datasets composed of a number of .TAB files linked through
8
 *           indexed fields.
9
 * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
10
 *
11
 **********************************************************************
12
 * Copyright (c) 1999-2002, Daniel Morissette
13
 * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
14
 *
15
 * SPDX-License-Identifier: MIT
16
 **********************************************************************/
17
18
#include "cpl_port.h"
19
#include "mitab.h"
20
21
#include <cassert>
22
#include <cctype>
23
#include <cstddef>
24
#include <cstdio>
25
#include <cstring>
26
27
#include "cpl_conv.h"
28
#include "cpl_error.h"
29
#include "cpl_string.h"
30
#include "cpl_vsi.h"
31
#include "mitab_priv.h"
32
#include "mitab_utils.h"
33
#include "ogr_core.h"
34
#include "ogr_feature.h"
35
#include "ogr_geometry.h"
36
#include "ogr_spatialref.h"
37
38
/*=====================================================================
39
 *                      class TABView
40
 *====================================================================*/
41
42
/**********************************************************************
43
 *                   TABView::TABView()
44
 *
45
 * Constructor.
46
 **********************************************************************/
47
TABView::TABView(GDALDataset *poDS)
48
1.19k
    : IMapInfoFile(poDS), m_pszFname(nullptr), m_eAccessMode(TABRead),
49
1.19k
      m_papszTABFile(nullptr), m_pszVersion(nullptr), m_papszTABFnames(nullptr),
50
1.19k
      m_papoTABFiles(nullptr), m_numTABFiles(0), m_nMainTableIndex(-1),
51
1.19k
      m_papszFieldNames(nullptr), m_papszWhereClause(nullptr),
52
1.19k
      m_poRelation(nullptr), m_bRelFieldsCreated(FALSE)
53
1.19k
{
54
1.19k
}
55
56
/**********************************************************************
57
 *                   TABView::~TABView()
58
 *
59
 * Destructor.
60
 **********************************************************************/
61
TABView::~TABView()
62
1.19k
{
63
1.19k
    TABView::Close();
64
1.19k
}
65
66
GIntBig TABView::GetFeatureCount(int bForce)
67
0
{
68
69
0
    if (m_nMainTableIndex != -1)
70
0
        return m_papoTABFiles[m_nMainTableIndex]->GetFeatureCount(bForce);
71
72
0
    return 0;
73
0
}
74
75
void TABView::ResetReading()
76
0
{
77
0
    if (m_nMainTableIndex != -1)
78
0
        m_papoTABFiles[m_nMainTableIndex]->ResetReading();
79
0
}
80
81
/**********************************************************************
82
 *                   TABView::Open()
83
 *
84
 * Open a .TAB dataset and the associated files, and initialize the
85
 * structures to be ready to read features from it.
86
 *
87
 * This class is used to open .TAB files that define a view on
88
 * two other .TAB files.  Regular .TAB datasets should be opened using
89
 * the TABFile class instead.
90
 *
91
 * Set bTestOpenNoError=TRUE to silently return -1 with no error message
92
 * if the file cannot be opened.  This is intended to be used in the
93
 * context of a TestOpen() function.  The default value is FALSE which
94
 * means that an error is reported if the file cannot be opened.
95
 *
96
 * Returns 0 on success, -1 on error.
97
 **********************************************************************/
98
int TABView::Open(const char *pszFname, TABAccess eAccess,
99
                  GBool bTestOpenNoError /*= FALSE*/,
100
                  const char *pszCharset /* = NULL */)
101
1.19k
{
102
1.19k
    char nStatus = 0;
103
104
1.19k
    if (m_numTABFiles > 0)
105
0
    {
106
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
107
0
                 "Open() failed: object already contains an open file");
108
0
        return -1;
109
0
    }
110
111
    /*-----------------------------------------------------------------
112
     * Validate access mode and call the right open method
113
     *----------------------------------------------------------------*/
114
1.19k
    if (eAccess == TABRead)
115
1.19k
    {
116
1.19k
        m_eAccessMode = TABRead;
117
1.19k
        nStatus = static_cast<char>(OpenForRead(pszFname, bTestOpenNoError));
118
1.19k
    }
119
0
    else if (eAccess == TABWrite)
120
0
    {
121
0
        m_eAccessMode = TABWrite;
122
0
        if (pszCharset != nullptr)
123
0
            SetCharset(pszCharset);
124
0
        nStatus = static_cast<char>(OpenForWrite(pszFname));
125
0
    }
126
0
    else
127
0
    {
128
0
        CPLError(CE_Failure, CPLE_NotSupported,
129
0
                 "Open() failed: access mode \"%d\" not supported", eAccess);
130
0
        return -1;
131
0
    }
132
133
1.19k
    return nStatus;
134
1.19k
}
135
136
/**********************************************************************
137
 *                   TABView::OpenForRead()
138
 *
139
 * Open for reading
140
 *
141
 * Returns 0 on success, -1 on error.
142
 **********************************************************************/
143
int TABView::OpenForRead(const char *pszFname,
144
                         GBool bTestOpenNoError /*= FALSE*/)
145
1.19k
{
146
1.19k
    char *pszPath = nullptr;
147
1.19k
    int nFnameLen = 0;
148
149
1.19k
    m_eAccessMode = TABRead;
150
151
    /*-----------------------------------------------------------------
152
     * Read main .TAB (text) file
153
     *----------------------------------------------------------------*/
154
1.19k
    m_pszFname = CPLStrdup(pszFname);
155
156
1.19k
#ifndef _WIN32
157
    /*-----------------------------------------------------------------
158
     * On Unix, make sure extension uses the right cases
159
     * We do it even for write access because if a file with the same
160
     * extension already exists we want to overwrite it.
161
     *----------------------------------------------------------------*/
162
1.19k
    TABAdjustFilenameExtension(m_pszFname);
163
1.19k
#endif
164
165
    /*-----------------------------------------------------------------
166
     * Open .TAB file... since it is a small text file, we will just load
167
     * it as a stringlist in memory.
168
     *----------------------------------------------------------------*/
169
1.19k
    m_papszTABFile = TAB_CSLLoad(m_pszFname);
170
1.19k
    if (m_papszTABFile == nullptr)
171
0
    {
172
0
        if (!bTestOpenNoError)
173
0
        {
174
0
            CPLError(CE_Failure, CPLE_FileIO, "Failed opening %s.", m_pszFname);
175
0
        }
176
177
0
        CPLFree(m_pszFname);
178
0
        m_pszFname = nullptr;
179
0
        return -1;
180
0
    }
181
182
    /*-------------------------------------------------------------
183
     * Look for a line with the "create view" keyword.
184
     * If there is no "create view", then we may have a valid .TAB file,
185
     * but we do not support it in this class.
186
     *------------------------------------------------------------*/
187
1.19k
    GBool bCreateViewFound = FALSE;
188
139k
    for (int i = 0; !bCreateViewFound && m_papszTABFile[i]; i++)
189
138k
    {
190
138k
        const char *pszStr = m_papszTABFile[i];
191
162k
        while (*pszStr != '\0' && isspace(static_cast<unsigned char>(*pszStr)))
192
24.2k
            pszStr++;
193
138k
        if (STARTS_WITH_CI(pszStr, "create view"))
194
1.19k
            bCreateViewFound = TRUE;
195
138k
    }
196
197
1.19k
    if (!bCreateViewFound)
198
0
    {
199
0
        if (!bTestOpenNoError)
200
0
            CPLError(CE_Failure, CPLE_NotSupported,
201
0
                     "%s contains no table view definition.  "
202
0
                     "This type of .TAB file cannot be read by this library.",
203
0
                     m_pszFname);
204
0
        else
205
0
            CPLErrorReset();
206
207
0
        CPLFree(m_pszFname);
208
0
        m_pszFname = nullptr;
209
210
0
        return -1;
211
0
    }
212
213
    /*-----------------------------------------------------------------
214
     * OK, this appears to be a valid TAB view dataset...
215
     * Extract the path component from the main .TAB filename
216
     * to build the filename of the sub-tables
217
     *----------------------------------------------------------------*/
218
1.19k
    pszPath = CPLStrdup(m_pszFname);
219
1.19k
    nFnameLen = static_cast<int>(strlen(pszPath));
220
18.1k
    for (; nFnameLen > 0; nFnameLen--)
221
18.1k
    {
222
18.1k
        if (pszPath[nFnameLen - 1] == '/' || pszPath[nFnameLen - 1] == '\\')
223
1.19k
        {
224
1.19k
            break;
225
1.19k
        }
226
16.9k
        pszPath[nFnameLen - 1] = '\0';
227
16.9k
    }
228
229
    /*-----------------------------------------------------------------
230
     * Extract the useful info from the TAB header
231
     *----------------------------------------------------------------*/
232
1.19k
    if (ParseTABFile(pszPath, bTestOpenNoError) != 0)
233
551
    {
234
        // Failed parsing... an error has already been produced if necessary
235
551
        CPLFree(pszPath);
236
551
        Close();
237
551
        return -1;
238
551
    }
239
642
    CPLFree(pszPath);
240
642
    pszPath = nullptr;
241
242
    /*-----------------------------------------------------------------
243
     * __TODO__ For now, we support only 2 files linked through a single
244
     *          field... so we'll do some validation first to make sure
245
     *          that what we found in the header respects these limitations.
246
     *----------------------------------------------------------------*/
247
642
    if (m_numTABFiles != 2)
248
40
    {
249
40
        if (!bTestOpenNoError)
250
0
            CPLError(CE_Failure, CPLE_NotSupported,
251
0
                     "Open Failed: Dataset %s defines a view on %d tables. "
252
0
                     "This is not currently supported.",
253
0
                     m_pszFname, m_numTABFiles);
254
40
        Close();
255
40
        return -1;
256
40
    }
257
258
    /*-----------------------------------------------------------------
259
     * Open all the tab files listed in the view
260
     *----------------------------------------------------------------*/
261
602
    m_papoTABFiles =
262
602
        static_cast<TABFile **>(CPLCalloc(m_numTABFiles, sizeof(TABFile *)));
263
264
1.58k
    for (int iFile = 0; iFile < m_numTABFiles; iFile++)
265
1.11k
    {
266
1.11k
#ifndef _WIN32
267
1.11k
        TABAdjustFilenameExtension(m_papszTABFnames[iFile]);
268
1.11k
#endif
269
270
1.11k
        m_papoTABFiles[iFile] = new TABFile(m_poDS);
271
272
1.11k
        if (m_papoTABFiles[iFile]->Open(m_papszTABFnames[iFile], m_eAccessMode,
273
1.11k
                                        bTestOpenNoError) != 0)
274
128
        {
275
            // Open Failed... an error has already been reported, just return.
276
128
            if (bTestOpenNoError)
277
128
                CPLErrorReset();
278
128
            Close();
279
128
            return -1;
280
128
        }
281
1.11k
    }
282
283
    /*-----------------------------------------------------------------
284
     * Create TABRelation... this will build FeatureDefn, etc.
285
     * __TODO__ For now this assumes only 2 tables in the view...
286
     *----------------------------------------------------------------*/
287
474
    m_poRelation = new TABRelation;
288
289
474
    CPLAssert(m_nMainTableIndex == 0);
290
474
    CPLAssert(CSLCount(m_papszWhereClause) == 5);
291
474
    char *pszTableName = TABGetBasename(m_pszFname);
292
474
    if (m_poRelation->Init(pszTableName, m_papoTABFiles[0], m_papoTABFiles[1],
293
474
                           m_papszWhereClause[4], m_papszWhereClause[2],
294
474
                           m_papszFieldNames) != 0)
295
83
    {
296
        // An error should already have been reported
297
83
        CPLFree(pszTableName);
298
83
        Close();
299
83
        return -1;
300
83
    }
301
391
    CPLFree(pszTableName);
302
303
391
    return 0;
304
474
}
305
306
/**********************************************************************
307
 *                   TABView::OpenForWrite()
308
 *
309
 * Create a new TABView dataset
310
 *
311
 * Returns 0 on success, -1 on error.
312
 **********************************************************************/
313
int TABView::OpenForWrite(const char *pszFname)
314
0
{
315
0
    int nFnameLen = 0;
316
317
0
    m_eAccessMode = TABWrite;
318
319
    /*-----------------------------------------------------------------
320
     * Read main .TAB (text) file
321
     *----------------------------------------------------------------*/
322
0
    m_pszFname = CPLStrdup(pszFname);
323
324
0
#ifndef _WIN32
325
    /*-----------------------------------------------------------------
326
     * On Unix, make sure extension uses the right cases
327
     * We do it even for write access because if a file with the same
328
     * extension already exists we want to overwrite it.
329
     *----------------------------------------------------------------*/
330
0
    TABAdjustFilenameExtension(m_pszFname);
331
0
#endif
332
333
    /*-----------------------------------------------------------------
334
     * Extract the path component from the main .TAB filename
335
     *----------------------------------------------------------------*/
336
0
    char *pszPath = CPLStrdup(m_pszFname);
337
0
    nFnameLen = static_cast<int>(strlen(pszPath));
338
0
    for (; nFnameLen > 0; nFnameLen--)
339
0
    {
340
0
        if (pszPath[nFnameLen - 1] == '/' || pszPath[nFnameLen - 1] == '\\')
341
0
        {
342
0
            break;
343
0
        }
344
0
        pszPath[nFnameLen - 1] = '\0';
345
0
    }
346
347
0
    char *pszBasename = TABGetBasename(m_pszFname);
348
349
    /*-----------------------------------------------------------------
350
     * Create the 2 TAB files for the view.
351
     *
352
     * __TODO__ For now, we support only 2 files linked through a single
353
     *          field... not sure if anything else than that can be useful
354
     *          anyways.
355
     *----------------------------------------------------------------*/
356
0
    m_numTABFiles = 2;
357
0
    m_papszTABFnames = nullptr;
358
0
    m_nMainTableIndex = 0;
359
0
    m_bRelFieldsCreated = FALSE;
360
361
0
    m_papoTABFiles =
362
0
        static_cast<TABFile **>(CPLCalloc(m_numTABFiles, sizeof(TABFile *)));
363
364
0
    for (int iFile = 0; iFile < m_numTABFiles; iFile++)
365
0
    {
366
0
        m_papszTABFnames = CSLAppendPrintf(m_papszTABFnames, "%s%s%d.tab",
367
0
                                           pszPath, pszBasename, iFile + 1);
368
0
#ifndef _WIN32
369
0
        assert(m_papszTABFnames);
370
0
        assert(m_papszTABFnames[iFile]);
371
0
        TABAdjustFilenameExtension(m_papszTABFnames[iFile]);
372
0
#endif
373
374
0
        m_papoTABFiles[iFile] = new TABFile(m_poDS);
375
376
0
        if (m_papoTABFiles[iFile]->Open(m_papszTABFnames[iFile], m_eAccessMode,
377
0
                                        FALSE, GetCharset()) != 0)
378
0
        {
379
            // Open Failed... an error has already been reported, just return.
380
0
            CPLFree(pszPath);
381
0
            CPLFree(pszBasename);
382
0
            Close();
383
0
            return -1;
384
0
        }
385
0
    }
386
387
    /*-----------------------------------------------------------------
388
     * Create TABRelation...
389
     *----------------------------------------------------------------*/
390
0
    m_poRelation = new TABRelation;
391
392
0
    if (m_poRelation->Init(pszBasename, m_papoTABFiles[0], m_papoTABFiles[1],
393
0
                           nullptr, nullptr, nullptr) != 0)
394
0
    {
395
        // An error should already have been reported
396
0
        CPLFree(pszPath);
397
0
        CPLFree(pszBasename);
398
0
        Close();
399
0
        return -1;
400
0
    }
401
402
0
    CPLFree(pszPath);
403
0
    CPLFree(pszBasename);
404
405
0
    return 0;
406
0
}
407
408
/**********************************************************************
409
 *                   TABView::ParseTABFile()
410
 *
411
 * Scan the lines of the TAB file, and store any useful information into
412
 * class members.  The main piece of information being the sub-table
413
 * names, and the list of fields to include in the view that we will
414
 * use to build the OGRFeatureDefn for this file.
415
 *
416
 * It is assumed that the TAB header file is already loaded in m_papszTABFile
417
 *
418
 * This private method should be used only during the Open() call.
419
 *
420
 * Returns 0 on success, -1 on error.
421
 **********************************************************************/
422
int TABView::ParseTABFile(const char *pszDatasetPath,
423
                          GBool bTestOpenNoError /*=FALSE*/)
424
1.19k
{
425
1.19k
    int iLine, numLines;
426
1.19k
    char **papszTok = nullptr;
427
1.19k
    GBool bInsideTableDef = FALSE;
428
429
1.19k
    if (m_eAccessMode != TABRead)
430
0
    {
431
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
432
0
                 "ParseTABFile() can be used only with Read access.");
433
0
        return -1;
434
0
    }
435
436
1.19k
    numLines = CSLCount(m_papszTABFile);
437
438
539k
    for (iLine = 0; iLine < numLines; iLine++)
439
538k
    {
440
        /*-------------------------------------------------------------
441
         * Tokenize the next .TAB line, and check first keyword
442
         *------------------------------------------------------------*/
443
538k
        CSLDestroy(papszTok);
444
538k
        papszTok = CSLTokenizeStringComplex(m_papszTABFile[iLine], " \t(),;",
445
538k
                                            TRUE, FALSE);
446
538k
        if (CSLCount(papszTok) < 2)
447
344k
            continue;  // All interesting lines have at least 2 tokens
448
449
193k
        if (EQUAL(papszTok[0], "!version"))
450
3.83k
        {
451
3.83k
            CPLFree(m_pszVersion);
452
3.83k
            m_pszVersion = CPLStrdup(papszTok[1]);
453
3.83k
        }
454
189k
        else if (EQUAL(papszTok[0], "!charset"))
455
1.89k
        {
456
1.89k
            CPLFree(m_pszCharset);
457
1.89k
            m_pszCharset = CPLStrdup(papszTok[1]);
458
1.89k
        }
459
187k
        else if (EQUAL(papszTok[0], "open") && EQUAL(papszTok[1], "table") &&
460
13.2k
                 CSLCount(papszTok) >= 3)
461
12.5k
        {
462
            // Source table name may be either "filename" or "filename.tab"
463
12.5k
            int nLen = static_cast<int>(strlen(papszTok[2]));
464
12.5k
            if (nLen > 4 && EQUAL(papszTok[2] + nLen - 4, ".tab"))
465
1.98k
                papszTok[2][nLen - 4] = '\0';
466
467
12.5k
            m_papszTABFnames = CSLAppendPrintf(m_papszTABFnames, "%s%s.tab",
468
12.5k
                                               pszDatasetPath, papszTok[2]);
469
12.5k
        }
470
175k
        else if (EQUAL(papszTok[0], "create") && EQUAL(papszTok[1], "view"))
471
7.95k
        {
472
7.95k
            bInsideTableDef = TRUE;
473
7.95k
        }
474
167k
        else if (bInsideTableDef && (EQUAL(papszTok[0], "Select")))
475
5.55k
        {
476
            /*---------------------------------------------------------
477
             * We found the list of table fields (comma-delimited list)
478
             *--------------------------------------------------------*/
479
17.9k
            for (int iTok = 1; papszTok[iTok] != nullptr; iTok++)
480
12.4k
                m_papszFieldNames =
481
12.4k
                    CSLAddString(m_papszFieldNames, papszTok[iTok]);
482
5.55k
        }
483
161k
        else if (bInsideTableDef && (EQUAL(papszTok[0], "where")))
484
782
        {
485
            /*---------------------------------------------------------
486
             * We found the where clause that relates the 2 tables
487
             * Something in the form:
488
             *   where table1.field1=table2.field2
489
             * The tokenized array will contain:
490
             *  {"where", "table1", "field1", "table2", "field2"}
491
             *--------------------------------------------------------*/
492
782
            CSLDestroy(m_papszWhereClause);
493
782
            m_papszWhereClause = CSLTokenizeStringComplex(
494
782
                m_papszTABFile[iLine], " \t(),;=.", TRUE, FALSE);
495
496
            /*---------------------------------------------------------
497
             * For now we are very limiting on the format of the WHERE
498
             * clause... we will be more permitting as we learn more about
499
             * what it can contain... (I don't want to implement a full SQL
500
             * parser here!!!).  If you encountered this error,
501
             * (and are reading this!) please report the test dataset
502
             * that produced the error and I'll see if we can support it.
503
             *--------------------------------------------------------*/
504
782
            if (CSLCount(m_papszWhereClause) != 5)
505
24
            {
506
24
                if (!bTestOpenNoError)
507
0
                    CPLError(CE_Failure, CPLE_NotSupported,
508
0
                             "WHERE clause in %s is not in a supported format: "
509
0
                             "\"%s\"",
510
0
                             m_pszFname, m_papszTABFile[iLine]);
511
24
                CSLDestroy(papszTok);
512
24
                return -1;
513
24
            }
514
782
        }
515
161k
        else
516
161k
        {
517
            // Simply Ignore unrecognized lines
518
161k
        }
519
193k
    }
520
521
1.16k
    CSLDestroy(papszTok);
522
523
    /*-----------------------------------------------------------------
524
     * The main table is the one from which we read the geometries, etc...
525
     * For now we assume it is always the first one in the list
526
     *----------------------------------------------------------------*/
527
1.16k
    m_nMainTableIndex = 0;
528
529
    /*-----------------------------------------------------------------
530
     * Make sure all required class members are set
531
     *----------------------------------------------------------------*/
532
1.16k
    m_numTABFiles = CSLCount(m_papszTABFnames);
533
534
1.16k
    if (m_pszCharset == nullptr)
535
1.02k
        m_pszCharset = CPLStrdup("Neutral");
536
1.16k
    if (m_pszVersion == nullptr)
537
666
        m_pszVersion = CPLStrdup("100");
538
539
1.16k
    if (CSLCount(m_papszFieldNames) == 0)
540
318
    {
541
318
        if (!bTestOpenNoError)
542
0
            CPLError(CE_Failure, CPLE_NotSupported,
543
0
                     "%s: header contains no table field definition.  "
544
0
                     "This type of .TAB file cannot be read by this library.",
545
0
                     m_pszFname);
546
318
        return -1;
547
318
    }
548
549
851
    if (CSLCount(m_papszWhereClause) == 0)
550
209
    {
551
209
        if (!bTestOpenNoError)
552
0
            CPLError(CE_Failure, CPLE_NotSupported,
553
0
                     "%s: WHERE clause not found or missing in header.  "
554
0
                     "This type of .TAB file cannot be read by this library.",
555
0
                     m_pszFname);
556
209
        return -1;
557
209
    }
558
642
    return 0;
559
851
}
560
561
/**********************************************************************
562
 *                   TABView::WriteTABFile()
563
 *
564
 * Generate the TAB header file.  This is usually done during the
565
 * Close() call.
566
 *
567
 * Returns 0 on success, -1 on error.
568
 **********************************************************************/
569
int TABView::WriteTABFile()
570
0
{
571
0
    CPLAssert(m_eAccessMode == TABWrite);
572
0
    CPLAssert(m_numTABFiles == 2);
573
0
    CPLAssert(GetLayerDefn());
574
575
0
    char *pszTable = TABGetBasename(m_pszFname);
576
0
    char *pszTable1 = TABGetBasename(m_papszTABFnames[0]);
577
0
    char *pszTable2 = TABGetBasename(m_papszTABFnames[1]);
578
579
0
    VSILFILE *fp = VSIFOpenL(m_pszFname, "wt");
580
0
    if (fp != nullptr)
581
0
    {
582
        // Version is always 100, no matter what the sub-table's version is
583
0
        VSIFPrintfL(fp, "!Table\n");
584
0
        VSIFPrintfL(fp, "!Version 100\n");
585
586
0
        VSIFPrintfL(fp, "Open Table \"%s\" Hide\n", pszTable1);
587
0
        VSIFPrintfL(fp, "Open Table \"%s\" Hide\n", pszTable2);
588
0
        VSIFPrintfL(fp, "\n");
589
0
        VSIFPrintfL(fp, "Create View %s As\n", pszTable);
590
0
        VSIFPrintfL(fp, "Select ");
591
592
0
        const OGRFeatureDefn *poDefn = GetLayerDefn();
593
0
        for (int iField = 0; iField < poDefn->GetFieldCount(); iField++)
594
0
        {
595
0
            const OGRFieldDefn *poFieldDefn = poDefn->GetFieldDefn(iField);
596
0
            if (iField == 0)
597
0
                VSIFPrintfL(fp, "%s", poFieldDefn->GetNameRef());
598
0
            else
599
0
                VSIFPrintfL(fp, ",%s", poFieldDefn->GetNameRef());
600
0
        }
601
0
        VSIFPrintfL(fp, "\n");
602
603
0
        VSIFPrintfL(fp, "From %s, %s\n", pszTable2, pszTable1);
604
0
        VSIFPrintfL(fp, "Where %s.%s=%s.%s\n", pszTable2,
605
0
                    m_poRelation->GetRelFieldName(), pszTable1,
606
0
                    m_poRelation->GetMainFieldName());
607
608
0
        VSIFCloseL(fp);
609
0
    }
610
0
    else
611
0
    {
612
0
        CPLFree(pszTable);
613
0
        CPLFree(pszTable1);
614
0
        CPLFree(pszTable2);
615
616
0
        CPLError(CE_Failure, CPLE_FileIO, "Failed to create file `%s'",
617
0
                 m_pszFname);
618
0
        return -1;
619
0
    }
620
621
0
    CPLFree(pszTable);
622
0
    CPLFree(pszTable1);
623
0
    CPLFree(pszTable2);
624
625
0
    return 0;
626
0
}
627
628
/**********************************************************************
629
 *                   TABView::Close()
630
 *
631
 * Close current file, and release all memory used.
632
 *
633
 * Returns 0 on success, -1 on error.
634
 **********************************************************************/
635
int TABView::Close()
636
1.99k
{
637
    // In write access, the main .TAB file has not been written yet.
638
1.99k
    if (m_eAccessMode == TABWrite && m_poRelation)
639
0
        WriteTABFile();
640
641
3.19k
    for (int i = 0; m_papoTABFiles && i < m_numTABFiles; i++)
642
1.20k
    {
643
1.20k
        if (m_papoTABFiles[i])
644
1.11k
            delete m_papoTABFiles[i];  // Automatically closes.
645
1.20k
    }
646
1.99k
    CPLFree(m_papoTABFiles);
647
1.99k
    m_papoTABFiles = nullptr;
648
1.99k
    m_numTABFiles = 0;
649
650
    /*-----------------------------------------------------------------
651
     * __TODO__ OK, MapInfo does not like to see a .map and .id file
652
     * attached to the second table, even if they're empty.
653
     * We'll use a little hack to delete them now, but eventually we
654
     * should avoid creating them at all.
655
     *----------------------------------------------------------------*/
656
1.99k
    if (m_eAccessMode == TABWrite && m_pszFname)
657
0
    {
658
0
        m_pszFname[strlen(m_pszFname) - 4] = '\0';
659
0
        char *pszFile = CPLStrdup(CPLSPrintf("%s2.map", m_pszFname));
660
0
        TABAdjustFilenameExtension(pszFile);
661
0
        VSIUnlink(pszFile);
662
663
0
        snprintf(pszFile, strlen(pszFile) + 1, "%s2.id", m_pszFname);
664
0
        TABAdjustFilenameExtension(pszFile);
665
0
        VSIUnlink(pszFile);
666
667
0
        CPLFree(pszFile);
668
0
    }
669
    // End of hack!
670
671
1.99k
    CPLFree(m_pszFname);
672
1.99k
    m_pszFname = nullptr;
673
674
1.99k
    CSLDestroy(m_papszTABFile);
675
1.99k
    m_papszTABFile = nullptr;
676
677
1.99k
    CPLFree(m_pszVersion);
678
1.99k
    m_pszVersion = nullptr;
679
1.99k
    CPLFree(m_pszCharset);
680
1.99k
    m_pszCharset = nullptr;
681
682
1.99k
    CSLDestroy(m_papszTABFnames);
683
1.99k
    m_papszTABFnames = nullptr;
684
685
1.99k
    CSLDestroy(m_papszFieldNames);
686
1.99k
    m_papszFieldNames = nullptr;
687
1.99k
    CSLDestroy(m_papszWhereClause);
688
1.99k
    m_papszWhereClause = nullptr;
689
690
1.99k
    m_nMainTableIndex = -1;
691
692
1.99k
    if (m_poRelation)
693
474
        delete m_poRelation;
694
1.99k
    m_poRelation = nullptr;
695
696
1.99k
    m_bRelFieldsCreated = FALSE;
697
698
1.99k
    return 0;
699
1.99k
}
700
701
/**********************************************************************
702
 *                   TABView::SetQuickSpatialIndexMode()
703
 *
704
 * Select "quick spatial index mode".
705
 *
706
 * The default behavior of MITAB is to generate an optimized spatial index,
707
 * but this results in slower write speed.
708
 *
709
 * Applications that want faster write speed and do not care
710
 * about the performance of spatial queries on the resulting file can
711
 * use SetQuickSpatialIndexMode() to require the creation of a non-optimal
712
 * spatial index (actually emulating the type of spatial index produced
713
 * by MITAB before version 1.6.0). In this mode writing files can be
714
 * about 5 times faster, but spatial queries can be up to 30 times slower.
715
 *
716
 * Returns 0 on success, -1 on error.
717
 **********************************************************************/
718
int TABView::SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode /*=TRUE*/)
719
0
{
720
0
    if (m_eAccessMode != TABWrite || m_numTABFiles == 0)
721
0
    {
722
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
723
0
                 "SetQuickSpatialIndexMode() failed: file not opened for write "
724
0
                 "access.");
725
0
        return -1;
726
0
    }
727
728
0
    for (int iFile = 0; iFile < m_numTABFiles; iFile++)
729
0
    {
730
0
        if (m_papoTABFiles[iFile]->SetQuickSpatialIndexMode(
731
0
                bQuickSpatialIndexMode) != 0)
732
0
        {
733
            // An error has already been reported, just return.
734
0
            return -1;
735
0
        }
736
0
    }
737
738
0
    return 0;
739
0
}
740
741
/**********************************************************************
742
 *                   TABView::GetNextFeatureId()
743
 *
744
 * Returns feature id that follows nPrevId, or -1 if it is the
745
 * last feature id.  Pass nPrevId=-1 to fetch the first valid feature id.
746
 **********************************************************************/
747
GIntBig TABView::GetNextFeatureId(GIntBig nPrevId)
748
10.5k
{
749
10.5k
    if (m_nMainTableIndex != -1)
750
10.5k
        return m_papoTABFiles[m_nMainTableIndex]->GetNextFeatureId(nPrevId);
751
752
0
    return -1;
753
10.5k
}
754
755
/**********************************************************************
756
 *                   TABView::GetFeatureRef()
757
 *
758
 * Fill and return a TABFeature object for the specified feature id.
759
 *
760
 * The returned pointer is a reference to an object owned and maintained
761
 * by this TABView object.  It should not be altered or freed by the
762
 * caller and its contents is guaranteed to be valid only until the next
763
 * call to GetFeatureRef() or Close().
764
 *
765
 * Returns NULL if the specified feature id does not exist of if an
766
 * error happened.  In any case, CPLError() will have been called to
767
 * report the reason of the failure.
768
 **********************************************************************/
769
TABFeature *TABView::GetFeatureRef(GIntBig nFeatureId)
770
10.4k
{
771
772
    /*-----------------------------------------------------------------
773
     * Make sure file is open.
774
     *----------------------------------------------------------------*/
775
10.4k
    if (m_poRelation == nullptr)
776
0
    {
777
0
        CPLError(CE_Failure, CPLE_IllegalArg,
778
0
                 "GetFeatureRef() failed: file is not opened!");
779
0
        return nullptr;
780
0
    }
781
782
10.4k
    if (!CPL_INT64_FITS_ON_INT32(nFeatureId))
783
0
        return nullptr;
784
785
10.4k
    if (m_poCurFeature)
786
0
    {
787
0
        delete m_poCurFeature;
788
0
        m_poCurFeature = nullptr;
789
0
    }
790
791
10.4k
    m_poCurFeature = m_poRelation->GetFeature(static_cast<int>(nFeatureId));
792
10.4k
    m_nCurFeatureId = nFeatureId;
793
10.4k
    if (m_poCurFeature)
794
10.4k
    {
795
10.4k
        m_poCurFeature->SetFID(m_nCurFeatureId);
796
10.4k
    }
797
10.4k
    return m_poCurFeature;
798
10.4k
}
799
800
/**********************************************************************
801
 *                   TABView::CreateFeature()
802
 *
803
 * Write a new feature to this dataset. The passed in feature is updated
804
 * with the new feature id.
805
 *
806
 * Returns OGRERR_NONE on success, or an appropriate OGRERR_ code if an
807
 * error happened in which case, CPLError() will have been called to
808
 * report the reason of the failure.
809
 **********************************************************************/
810
OGRErr TABView::CreateFeature(TABFeature *poFeature)
811
0
{
812
0
    if (m_eAccessMode != TABWrite)
813
0
    {
814
0
        CPLError(CE_Failure, CPLE_NotSupported,
815
0
                 "CreateFeature() can be used only with Write access.");
816
0
        return OGRERR_UNSUPPORTED_OPERATION;
817
0
    }
818
819
0
    if (m_poRelation == nullptr)
820
0
    {
821
0
        CPLError(CE_Failure, CPLE_IllegalArg,
822
0
                 "CreateFeature() failed: file is not opened!");
823
0
        return OGRERR_FAILURE;
824
0
    }
825
826
    /*-----------------------------------------------------------------
827
     * If we're about to write the first feature, then we must finish
828
     * the initialization of the view first by creating the MI_refnum fields
829
     *----------------------------------------------------------------*/
830
0
    if (!m_bRelFieldsCreated)
831
0
    {
832
0
        if (m_poRelation->CreateRelFields() != 0)
833
0
            return OGRERR_FAILURE;
834
0
        m_bRelFieldsCreated = TRUE;
835
0
    }
836
837
0
    int nFeatureId = m_poRelation->WriteFeature(poFeature);
838
0
    if (nFeatureId < 0)
839
0
        return OGRERR_FAILURE;
840
841
0
    poFeature->SetFID(nFeatureId);
842
843
0
    return OGRERR_NONE;
844
0
}
845
846
/**********************************************************************
847
 *                   TABView::GetLayerDefn() const
848
 *
849
 * Returns a reference to the OGRFeatureDefn that will be used to create
850
 * features in this dataset.
851
 *
852
 * Returns a reference to an object that is maintained by this TABView
853
 * object (and thus should not be modified or freed by the caller) or
854
 * NULL if the OGRFeatureDefn has not been initialized yet (i.e. no file
855
 * opened yet)
856
 **********************************************************************/
857
const OGRFeatureDefn *TABView::GetLayerDefn() const
858
715
{
859
715
    if (m_poRelation)
860
715
        return m_poRelation->GetFeatureDefn();
861
862
0
    return nullptr;
863
715
}
864
865
/**********************************************************************
866
 *                   TABView::SetFeatureDefn()
867
 *
868
 * Set the FeatureDefn for this dataset.
869
 *
870
 * For now, fields passed through SetFeatureDefn will not be mapped
871
 * properly, so this function can be used only with an empty feature defn.
872
 **********************************************************************/
873
int TABView::SetFeatureDefn(
874
    OGRFeatureDefn *poFeatureDefn,
875
    CPL_UNUSED TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */)
876
0
{
877
0
    if (m_poRelation)
878
0
        return m_poRelation->SetFeatureDefn(poFeatureDefn);
879
880
0
    return -1;
881
0
}
882
883
/**********************************************************************
884
 *                   TABView::GetNativeFieldType()
885
 *
886
 * Returns the native MapInfo field type for the specified field.
887
 *
888
 * Returns TABFUnknown if file is not opened, or if specified field index is
889
 * invalid.
890
 *
891
 * Note that field ids are positive and start at 0.
892
 **********************************************************************/
893
TABFieldType TABView::GetNativeFieldType(int nFieldId)
894
0
{
895
0
    if (m_poRelation)
896
0
        return m_poRelation->GetNativeFieldType(nFieldId);
897
898
0
    return TABFUnknown;
899
0
}
900
901
/**********************************************************************
902
 *                   TABView::AddFieldNative()
903
 *
904
 * Create a new field using a native mapinfo data type... this is an
905
 * alternative to defining fields through the OGR interface.
906
 * This function should be called after creating a new dataset, but before
907
 * writing the first feature.
908
 *
909
 * This function will build/update the OGRFeatureDefn that will have to be
910
 * used when writing features to this dataset.
911
 *
912
 * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn().
913
 *
914
 * Returns 0 on success, -1 on error.
915
 **********************************************************************/
916
int TABView::AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
917
                            int nWidth /*=0*/, int nPrecision /*=0*/,
918
                            GBool bIndexed /*=FALSE*/, GBool bUnique /*=FALSE*/,
919
                            int bApproxOK)
920
0
{
921
0
    if (m_poRelation)
922
0
        return m_poRelation->AddFieldNative(pszName, eMapInfoType, nWidth,
923
0
                                            nPrecision, bIndexed, bUnique,
924
0
                                            bApproxOK);
925
926
0
    return -1;
927
0
}
928
929
/**********************************************************************
930
 *                   TABView::SetFieldIndexed()
931
 *
932
 * Request that a field be indexed.  This will create the .IND file if
933
 * necessary, etc.
934
 *
935
 * Note that field ids are positive and start at 0.
936
 *
937
 * Returns 0 on success, -1 on error.
938
 **********************************************************************/
939
int TABView::SetFieldIndexed(int nFieldId)
940
0
{
941
0
    if (m_poRelation)
942
0
        return m_poRelation->SetFieldIndexed(nFieldId);
943
944
0
    return -1;
945
0
}
946
947
int TABView::SetCharset(const char *pszCharset)
948
0
{
949
0
    if (0 != IMapInfoFile::SetCharset(pszCharset))
950
0
    {
951
0
        return -1;
952
0
    }
953
954
0
    for (int i = 0; i != m_numTABFiles; ++i)
955
0
    {
956
0
        m_papoTABFiles[i]->SetCharset(pszCharset);
957
0
    }
958
959
0
    return 0;
960
0
}
961
962
/**********************************************************************
963
 *                   TABView::IsFieldIndexed()
964
 *
965
 * Returns TRUE if field is indexed, or FALSE otherwise.
966
 **********************************************************************/
967
GBool TABView::IsFieldIndexed(int nFieldId)
968
0
{
969
0
    if (m_poRelation)
970
0
        return m_poRelation->IsFieldIndexed(nFieldId);
971
972
0
    return FALSE;
973
0
}
974
975
/**********************************************************************
976
 *                   TABView::IsFieldUnique()
977
 *
978
 * Returns TRUE if field is in the Unique table, or FALSE otherwise.
979
 **********************************************************************/
980
GBool TABView::IsFieldUnique(int nFieldId)
981
0
{
982
0
    if (m_poRelation)
983
0
        return m_poRelation->IsFieldUnique(nFieldId);
984
985
0
    return FALSE;
986
0
}
987
988
/**********************************************************************
989
 *                   TABView::GetBounds()
990
 *
991
 * Fetch projection coordinates bounds of a dataset.
992
 *
993
 * The bForce flag has no effect on TAB files since the bounds are
994
 * always in the header.
995
 *
996
 * Returns 0 on success, -1 on error.
997
 **********************************************************************/
998
int TABView::GetBounds(double &dXMin, double &dYMin, double &dXMax,
999
                       double &dYMax, GBool bForce /*= TRUE*/)
1000
0
{
1001
0
    if (m_nMainTableIndex == -1)
1002
0
    {
1003
0
        CPLError(
1004
0
            CE_Failure, CPLE_AppDefined,
1005
0
            "GetBounds() can be called only after dataset has been opened.");
1006
0
        return -1;
1007
0
    }
1008
1009
0
    return m_papoTABFiles[m_nMainTableIndex]->GetBounds(dXMin, dYMin, dXMax,
1010
0
                                                        dYMax, bForce);
1011
0
}
1012
1013
/**********************************************************************
1014
 *                   TABView::IGetExtent()
1015
 *
1016
 * Fetch extent of the data currently stored in the dataset.
1017
 *
1018
 * The bForce flag has no effect on TAB files since that value is
1019
 * always in the header.
1020
 *
1021
 * Returns OGRERR_NONE/OGRRERR_FAILURE.
1022
 **********************************************************************/
1023
OGRErr TABView::IGetExtent(int iGeomField, OGREnvelope *psExtent, bool bForce)
1024
0
{
1025
0
    if (m_nMainTableIndex == -1)
1026
0
    {
1027
0
        CPLError(
1028
0
            CE_Failure, CPLE_AppDefined,
1029
0
            "GetExtent() can be called only after dataset has been opened.");
1030
0
        return OGRERR_FAILURE;
1031
0
    }
1032
1033
0
    return m_papoTABFiles[m_nMainTableIndex]->GetExtent(iGeomField, psExtent,
1034
0
                                                        bForce);
1035
0
}
1036
1037
/**********************************************************************
1038
 *                   TABView::GetFeatureCountByType()
1039
 *
1040
 * Return number of features of each type.
1041
 *
1042
 * Note that the sum of the 4 returned values may be different from
1043
 * the total number of features since features with NONE geometry
1044
 * are not taken into account here.
1045
 *
1046
 * Note: the bForce flag has nmo effect on .TAB files since the info
1047
 * is always in the header.
1048
 *
1049
 * Returns 0 on success, or silently returns -1 (with no error) if this
1050
 * information is not available.
1051
 **********************************************************************/
1052
int TABView::GetFeatureCountByType(int &numPoints, int &numLines,
1053
                                   int &numRegions, int &numTexts,
1054
                                   GBool bForce /*= TRUE*/)
1055
0
{
1056
0
    if (m_nMainTableIndex == -1)
1057
0
        return -1;
1058
1059
0
    return m_papoTABFiles[m_nMainTableIndex]->GetFeatureCountByType(
1060
0
        numPoints, numLines, numRegions, numTexts, bForce);
1061
0
}
1062
1063
/**********************************************************************
1064
 *                   TABView::GetSpatialRef()
1065
 *
1066
 * Returns a reference to an OGRSpatialReference for this dataset.
1067
 * If the projection parameters have not been parsed yet, then we will
1068
 * parse them before returning.
1069
 *
1070
 * The returned object is owned and maintained by this TABFile and
1071
 * should not be modified or freed by the caller.
1072
 *
1073
 * Returns NULL if the SpatialRef cannot be accessed.
1074
 **********************************************************************/
1075
const OGRSpatialReference *TABView::GetSpatialRef() const
1076
163
{
1077
163
    if (m_nMainTableIndex == -1)
1078
0
    {
1079
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
1080
0
                 "GetSpatialRef() failed: file has not been opened yet.");
1081
0
        return nullptr;
1082
0
    }
1083
1084
163
    return m_papoTABFiles[m_nMainTableIndex]->GetSpatialRef();
1085
163
}
1086
1087
/**********************************************************************
1088
 *                   TABView::SetSpatialRef()
1089
 **********************************************************************/
1090
int TABView::SetSpatialRef(OGRSpatialReference *poSpatialRef)
1091
0
{
1092
0
    if (m_nMainTableIndex == -1)
1093
0
    {
1094
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
1095
0
                 "SetSpatialRef() failed: file has not been opened yet.");
1096
0
        return -1;
1097
0
    }
1098
1099
0
    return m_papoTABFiles[m_nMainTableIndex]->SetSpatialRef(poSpatialRef);
1100
0
}
1101
1102
/**********************************************************************
1103
 *                   TABView::SetBounds()
1104
 **********************************************************************/
1105
int TABView::SetBounds(double dXMin, double dYMin, double dXMax, double dYMax)
1106
0
{
1107
0
    if (m_nMainTableIndex == -1)
1108
0
    {
1109
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
1110
0
                 "SetBounds() failed: file has not been opened yet.");
1111
0
        return -1;
1112
0
    }
1113
1114
0
    return m_papoTABFiles[m_nMainTableIndex]->SetBounds(dXMin, dYMin, dXMax,
1115
0
                                                        dYMax);
1116
0
}
1117
1118
/************************************************************************/
1119
/*                           TestCapability()                           */
1120
/************************************************************************/
1121
1122
int TABView::TestCapability(const char *pszCap) const
1123
1124
0
{
1125
0
    if (EQUAL(pszCap, OLCRandomRead))
1126
0
        return TRUE;
1127
1128
0
    else if (EQUAL(pszCap, OLCSequentialWrite))
1129
0
        return TRUE;
1130
1131
0
    else if (EQUAL(pszCap, OLCRandomWrite))
1132
0
        return FALSE;
1133
1134
0
    else if (EQUAL(pszCap, OLCFastFeatureCount))
1135
0
        return m_poFilterGeom == nullptr;
1136
1137
0
    else if (EQUAL(pszCap, OLCFastSpatialFilter))
1138
0
        return FALSE;
1139
1140
0
    else if (EQUAL(pszCap, OLCFastGetExtent))
1141
0
        return TRUE;
1142
1143
0
    else if (EQUAL(pszCap, OLCStringsAsUTF8))
1144
0
        return TestUtf8Capability();
1145
1146
0
    else
1147
0
        return FALSE;
1148
0
}
1149
1150
/**********************************************************************
1151
 *                   TABView::Dump()
1152
 *
1153
 * Dump block contents... available only in DEBUG mode.
1154
 **********************************************************************/
1155
#ifdef DEBUG
1156
1157
void TABView::Dump(FILE *fpOut /*=NULL*/)
1158
{
1159
    if (fpOut == nullptr)
1160
        fpOut = stdout;
1161
1162
    fprintf(fpOut, "----- TABView::Dump() -----\n");
1163
1164
    if (m_numTABFiles > 0)
1165
    {
1166
        fprintf(fpOut, "File is not opened.\n");
1167
    }
1168
    else
1169
    {
1170
        fprintf(fpOut, "File is opened: %s\n", m_pszFname);
1171
        fprintf(fpOut, "View contains %d tables\n", m_numTABFiles);
1172
    }
1173
1174
    fflush(fpOut);
1175
}
1176
1177
#endif  // DEBUG
1178
1179
/*=====================================================================
1180
 *                      class TABRelation
1181
 *====================================================================*/
1182
1183
/**********************************************************************
1184
 *                   TABRelation::TABRelation()
1185
 *
1186
 * Constructor.
1187
 **********************************************************************/
1188
TABRelation::TABRelation()
1189
474
    : m_poMainTable(nullptr), m_pszMainFieldName(nullptr), m_nMainFieldNo(-1),
1190
474
      m_poRelTable(nullptr), m_pszRelFieldName(nullptr), m_nRelFieldNo(-1),
1191
474
      m_poRelINDFileRef(nullptr), m_nRelFieldIndexNo(-1), m_nUniqueRecordNo(0),
1192
474
      m_panMainTableFieldMap(nullptr), m_panRelTableFieldMap(nullptr),
1193
474
      m_poDefn(nullptr)
1194
474
{
1195
474
}
1196
1197
/**********************************************************************
1198
 *                   TABRelation::~TABRelation()
1199
 *
1200
 * Destructor.
1201
 **********************************************************************/
1202
TABRelation::~TABRelation()
1203
474
{
1204
474
    ResetAllMembers();
1205
474
}
1206
1207
/**********************************************************************
1208
 *                   TABRelation::ResetAllMembers()
1209
 *
1210
 * Reset all class members.
1211
 **********************************************************************/
1212
void TABRelation::ResetAllMembers()
1213
948
{
1214
948
    m_poMainTable = nullptr;
1215
948
    CPLFree(m_pszMainFieldName);
1216
948
    m_pszMainFieldName = nullptr;
1217
948
    m_nMainFieldNo = -1;
1218
1219
948
    m_poRelTable = nullptr;
1220
948
    CPLFree(m_pszRelFieldName);
1221
948
    m_pszRelFieldName = nullptr;
1222
948
    m_nRelFieldNo = -1;
1223
948
    m_nRelFieldIndexNo = -1;
1224
1225
948
    m_nUniqueRecordNo = 0;
1226
1227
    // No need to close m_poRelINDFileRef since we only got a ref. to it
1228
948
    m_poRelINDFileRef = nullptr;
1229
1230
948
    CPLFree(m_panMainTableFieldMap);
1231
948
    m_panMainTableFieldMap = nullptr;
1232
948
    CPLFree(m_panRelTableFieldMap);
1233
948
    m_panRelTableFieldMap = nullptr;
1234
1235
    /*-----------------------------------------------------------------
1236
     * Note: we have to check the reference count before deleting m_poDefn
1237
     *----------------------------------------------------------------*/
1238
948
    if (m_poDefn)
1239
391
        m_poDefn->Release();
1240
948
    m_poDefn = nullptr;
1241
948
}
1242
1243
/**********************************************************************
1244
 *                   TABRelation::Init()
1245
 *
1246
 * Set the details of the relation: the main and related tables, the fields
1247
 * through which they will be connected, and the list of fields to select.
1248
 * After this call, we are ready to read data records.
1249
 *
1250
 * For write access, Init() is called with pszMain/RelFieldName and
1251
 * **papszSelectedFields passed as NULL.  They will have to be set through
1252
 * other methods before a first feature can be written.
1253
 *
1254
 * A new OGRFeatureDefn is also built for the combined tables.
1255
 *
1256
 * Returns 0 on success, or -1 or error.
1257
 **********************************************************************/
1258
int TABRelation::Init(const char *pszViewName, TABFile *poMainTable,
1259
                      TABFile *poRelTable, const char *pszMainFieldName,
1260
                      const char *pszRelFieldName, char **papszSelectedFields)
1261
474
{
1262
474
    if (poMainTable == nullptr || poRelTable == nullptr)
1263
0
        return -1;
1264
1265
    // We'll need the feature Defn later...
1266
474
    const OGRFeatureDefn *poMainDefn = poMainTable->GetLayerDefn();
1267
474
    const OGRFeatureDefn *poRelDefn = poRelTable->GetLayerDefn();
1268
1269
    /*-----------------------------------------------------------------
1270
     * Keep info for later use about source tables, etc.
1271
     *----------------------------------------------------------------*/
1272
474
    ResetAllMembers();
1273
1274
474
    m_poMainTable = poMainTable;
1275
474
    if (pszMainFieldName)
1276
474
    {
1277
474
        m_pszMainFieldName = CPLStrdup(pszMainFieldName);
1278
474
        m_nMainFieldNo = poMainDefn->GetFieldIndex(pszMainFieldName);
1279
474
    }
1280
1281
474
    m_poRelTable = poRelTable;
1282
474
    if (pszRelFieldName)
1283
474
    {
1284
474
        m_pszRelFieldName = CPLStrdup(pszRelFieldName);
1285
474
        m_nRelFieldNo = poRelDefn->GetFieldIndex(pszRelFieldName);
1286
474
        m_nRelFieldIndexNo = poRelTable->GetFieldIndexNumber(m_nRelFieldNo);
1287
474
        m_poRelINDFileRef = poRelTable->GetINDFileRef();
1288
1289
474
        if (m_nRelFieldIndexNo >= 0 && m_poRelINDFileRef == nullptr)
1290
83
        {
1291
83
            CPLError(CE_Failure, CPLE_FileIO,
1292
83
                     "Field %s is indexed but the .IND file is missing.",
1293
83
                     pszRelFieldName);
1294
83
            return -1;
1295
83
        }
1296
474
    }
1297
1298
    /*-----------------------------------------------------------------
1299
     * Init field maps.  For each field in each table, a -1 means that
1300
     * the field is not selected, and a value >=0 is the index of the
1301
     * field in the view's FeatureDefn
1302
     *----------------------------------------------------------------*/
1303
391
    const int numFields1 = poMainDefn ? poMainDefn->GetFieldCount() : 0;
1304
391
    const int numFields2 = poRelDefn ? poRelDefn->GetFieldCount() : 0;
1305
1306
391
    m_panMainTableFieldMap =
1307
391
        static_cast<int *>(CPLMalloc((numFields1 + 1) * sizeof(int)));
1308
1.17k
    for (int i = 0; i < numFields1; i++)
1309
780
        m_panMainTableFieldMap[i] = -1;
1310
391
    m_panRelTableFieldMap =
1311
391
        static_cast<int *>(CPLMalloc((numFields2 + 1) * sizeof(int)));
1312
1.03k
    for (int i = 0; i < numFields2; i++)
1313
646
        m_panRelTableFieldMap[i] = -1;
1314
1315
    /*-----------------------------------------------------------------
1316
     * If selectedFields = "*" then select all fields from both tables
1317
     *----------------------------------------------------------------*/
1318
391
    papszSelectedFields = CSLDuplicate(papszSelectedFields);
1319
391
    if (papszSelectedFields != nullptr && papszSelectedFields[0] != nullptr &&
1320
391
        papszSelectedFields[1] == nullptr && EQUAL(papszSelectedFields[0], "*"))
1321
0
    {
1322
0
        CSLDestroy(papszSelectedFields);
1323
0
        papszSelectedFields = nullptr;
1324
1325
0
        for (int i = 0; i < numFields1; i++)
1326
0
        {
1327
0
            const OGRFieldDefn *poFieldDefn = poMainDefn->GetFieldDefn(i);
1328
1329
0
            papszSelectedFields =
1330
0
                CSLAddString(papszSelectedFields, poFieldDefn->GetNameRef());
1331
0
        }
1332
1333
0
        for (int i = 0; i < numFields2; i++)
1334
0
        {
1335
0
            const OGRFieldDefn *poFieldDefn = poRelDefn->GetFieldDefn(i);
1336
1337
0
            if (CSLFindString(papszSelectedFields, poFieldDefn->GetNameRef()) !=
1338
0
                -1)
1339
0
                continue;  // Avoid duplicate field name in view
1340
1341
0
            papszSelectedFields =
1342
0
                CSLAddString(papszSelectedFields, poFieldDefn->GetNameRef());
1343
0
        }
1344
0
    }
1345
1346
    /*-----------------------------------------------------------------
1347
     * Create new FeatureDefn and copy selected fields definitions
1348
     * while updating the appropriate field maps.
1349
     *----------------------------------------------------------------*/
1350
391
    const OGRFieldDefn *poFieldDefn = nullptr;
1351
1352
391
    m_poDefn = new OGRFeatureDefn(pszViewName);
1353
    // Ref count defaults to 0... set it to 1
1354
391
    m_poDefn->Reference();
1355
1356
391
    for (int i = 0;
1357
1.48k
         papszSelectedFields != nullptr && papszSelectedFields[i] != nullptr;
1358
1.08k
         i++)
1359
1.08k
    {
1360
1.08k
        int nIndex;
1361
1.08k
        if (poMainDefn &&
1362
1.08k
            (nIndex = poMainDefn->GetFieldIndex(papszSelectedFields[i])) >= 0)
1363
457
        {
1364
            /* Field from the main table
1365
             */
1366
457
            poFieldDefn = poMainDefn->GetFieldDefn(nIndex);
1367
457
            m_poDefn->AddFieldDefn(poFieldDefn);
1368
457
            m_panMainTableFieldMap[nIndex] = m_poDefn->GetFieldCount() - 1;
1369
457
        }
1370
632
        else if (poRelDefn && (nIndex = poRelDefn->GetFieldIndex(
1371
632
                                   papszSelectedFields[i])) >= 0)
1372
88
        {
1373
            /* Field from the related table
1374
             */
1375
88
            poFieldDefn = poRelDefn->GetFieldDefn(nIndex);
1376
88
            m_poDefn->AddFieldDefn(poFieldDefn);
1377
88
            m_panRelTableFieldMap[nIndex] = m_poDefn->GetFieldCount() - 1;
1378
88
        }
1379
544
        else
1380
544
        {
1381
            // Hummm... field does not exist... likely an unsupported feature!
1382
            // At least send a warning and ignore the field.
1383
544
            CPLError(CE_Warning, CPLE_IllegalArg,
1384
544
                     "Selected Field %s not found in source tables %s and %s",
1385
544
                     papszSelectedFields[i],
1386
544
                     poMainDefn ? poMainDefn->GetName() : "(null)",
1387
544
                     poRelDefn ? poRelDefn->GetName() : "(null)");
1388
544
        }
1389
1.08k
    }
1390
391
    CSLDestroy(papszSelectedFields);
1391
391
    return 0;
1392
474
}
1393
1394
/**********************************************************************
1395
 *                   TABRelation::CreateRelFields()
1396
 *
1397
 * For write access, create the integer fields in each table that will
1398
 * link them, and setup everything to be ready to write the first feature.
1399
 *
1400
 * This function should be called just before writing the first feature.
1401
 *
1402
 * Returns 0 on success, or -1 or error.
1403
 **********************************************************************/
1404
int TABRelation::CreateRelFields()
1405
0
{
1406
    /*-----------------------------------------------------------------
1407
     * Create the field in each table.
1408
     * The default name is "MI_refnum" but if a field with the same name
1409
     * already exists then we'll try to generate a unique name.
1410
     *----------------------------------------------------------------*/
1411
0
    m_pszMainFieldName = CPLStrdup("MI_Refnum      ");
1412
0
    const size_t nLen = strlen(m_pszMainFieldName) + 1;
1413
0
    strcpy(m_pszMainFieldName, "MI_Refnum");
1414
0
    int i = 1;
1415
0
    while (m_poDefn->GetFieldIndex(m_pszMainFieldName) >= 0)
1416
0
    {
1417
0
        snprintf(m_pszMainFieldName, nLen, "MI_Refnum_%d", i++);
1418
0
    }
1419
0
    m_pszRelFieldName = CPLStrdup(m_pszMainFieldName);
1420
1421
0
    m_nMainFieldNo = m_nRelFieldNo = -1;
1422
0
    if (m_poMainTable->AddFieldNative(m_pszMainFieldName, TABFInteger, 0, 0) ==
1423
0
        0)
1424
0
        m_nMainFieldNo = m_poMainTable->GetLayerDefn()->GetFieldCount() - 1;
1425
1426
0
    if (m_poRelTable->AddFieldNative(m_pszRelFieldName, TABFInteger, 0, 0) == 0)
1427
0
        m_nRelFieldNo = m_poRelTable->GetLayerDefn()->GetFieldCount() - 1;
1428
1429
0
    if (m_nMainFieldNo == -1 || m_nRelFieldNo == -1)
1430
0
        return -1;
1431
1432
0
    if (m_poMainTable->SetFieldIndexed(m_nMainFieldNo) == -1)
1433
0
        return -1;
1434
1435
0
    if ((m_nRelFieldIndexNo = m_poRelTable->SetFieldIndexed(m_nRelFieldNo)) ==
1436
0
        -1)
1437
0
        return -1;
1438
1439
0
    m_poRelINDFileRef = m_poRelTable->GetINDFileRef();
1440
1441
    /*-----------------------------------------------------------------
1442
     * Update field maps
1443
     *----------------------------------------------------------------*/
1444
0
    const OGRFeatureDefn *poMainDefn = m_poMainTable->GetLayerDefn();
1445
0
    const OGRFeatureDefn *poRelDefn = m_poRelTable->GetLayerDefn();
1446
1447
0
    m_panMainTableFieldMap = static_cast<int *>(CPLRealloc(
1448
0
        m_panMainTableFieldMap, poMainDefn->GetFieldCount() * sizeof(int)));
1449
0
    m_panMainTableFieldMap[poMainDefn->GetFieldCount() - 1] = -1;
1450
1451
0
    m_panRelTableFieldMap = static_cast<int *>(CPLRealloc(
1452
0
        m_panRelTableFieldMap, poRelDefn->GetFieldCount() * sizeof(int)));
1453
0
    m_panRelTableFieldMap[poRelDefn->GetFieldCount() - 1] = -1;
1454
1455
    /*-----------------------------------------------------------------
1456
     * Make sure the first unique field (in poRelTable) is indexed since
1457
     * it is the one against which we will try to match records.
1458
     *----------------------------------------------------------------*/
1459
0
    if (m_poRelTable->SetFieldIndexed(0) == -1)
1460
0
        return -1;
1461
1462
0
    return 0;
1463
0
}
1464
1465
/**********************************************************************
1466
 *                   TABRelation::GetFeature()
1467
 *
1468
 * Fill and return a TABFeature object for the specified feature id.
1469
 *
1470
 * The returned pointer is a new TABFeature that will have to be freed
1471
 * by the caller.
1472
 *
1473
 * Returns NULL if the specified feature id does not exist of if an
1474
 * error happened.  In any case, CPLError() will have been called to
1475
 * report the reason of the failure.
1476
 *
1477
 * __TODO__ The current implementation fetches the features from each table
1478
 * and creates a 3rd feature to merge them.  There would be room for
1479
 * optimization, at least by avoiding the duplication of the geometry
1480
 * which can be big sometimes... but this would imply changes at the
1481
 * lower-level in the lib. and we won't go there yet.
1482
 **********************************************************************/
1483
TABFeature *TABRelation::GetFeature(int nFeatureId)
1484
10.4k
{
1485
    /*-----------------------------------------------------------------
1486
     * Make sure init() has been called
1487
     *----------------------------------------------------------------*/
1488
10.4k
    if (m_poMainTable == nullptr || m_poRelTable == nullptr)
1489
0
    {
1490
0
        CPLError(CE_Failure, CPLE_IllegalArg,
1491
0
                 "GetFeatureRef() failed: object not initialized yet!");
1492
0
        return nullptr;
1493
0
    }
1494
1495
    /*-----------------------------------------------------------------
1496
     * Read main feature and create a new one of the right type
1497
     *----------------------------------------------------------------*/
1498
10.4k
    TABFeature *poMainFeature = m_poMainTable->GetFeatureRef(nFeatureId);
1499
10.4k
    if (poMainFeature == nullptr)
1500
0
    {
1501
        // Feature cannot be read from main table...
1502
        // an error has already been reported.
1503
0
        return nullptr;
1504
0
    }
1505
1506
10.4k
    auto poCurFeature =
1507
10.4k
        std::unique_ptr<TABFeature>(poMainFeature->CloneTABFeature(m_poDefn));
1508
1509
    /*-----------------------------------------------------------------
1510
     * Keep track of FID and copy the geometry
1511
     *----------------------------------------------------------------*/
1512
10.4k
    poCurFeature->SetFID(nFeatureId);
1513
1514
10.4k
    if (poCurFeature->GetFeatureClass() != TABFCNoGeomFeature)
1515
1
    {
1516
1
        OGRGeometry *poGeom = poMainFeature->GetGeometryRef();
1517
1
        poCurFeature->SetGeometry(poGeom);
1518
1
    }
1519
1520
    /*-----------------------------------------------------------------
1521
     * Fetch feature from related table
1522
     *
1523
     * __TODO__ Right now we support only many-to-1 relationships, but
1524
     *          it might be possible to have several related entries
1525
     *          for a single key, and in this case we should return
1526
     *          one new feature for each of them.
1527
     *----------------------------------------------------------------*/
1528
10.4k
    TABFeature *poRelFeature = nullptr;
1529
10.4k
    if (m_poRelINDFileRef)
1530
9.43k
    {
1531
9.43k
        GByte *pKey =
1532
9.43k
            BuildFieldKey(poMainFeature, m_nMainFieldNo,
1533
9.43k
                          m_poMainTable->GetNativeFieldType(m_nMainFieldNo),
1534
9.43k
                          m_nRelFieldIndexNo);
1535
9.43k
        if (!pKey)
1536
6
            return nullptr;
1537
1538
9.43k
        int nRelFeatureId =
1539
9.43k
            m_poRelINDFileRef->FindFirst(m_nRelFieldIndexNo, pKey);
1540
1541
9.43k
        if (nRelFeatureId > 0)
1542
839
            poRelFeature = m_poRelTable->GetFeatureRef(nRelFeatureId);
1543
9.43k
    }
1544
1545
    /*-----------------------------------------------------------------
1546
     * Copy fields from poMainFeature
1547
     *----------------------------------------------------------------*/
1548
31.2k
    for (int i = 0; i < poMainFeature->GetFieldCount(); i++)
1549
20.8k
    {
1550
20.8k
        if (m_panMainTableFieldMap[i] != -1)
1551
2.20k
        {
1552
2.20k
            poCurFeature->SetField(m_panMainTableFieldMap[i],
1553
2.20k
                                   poMainFeature->GetRawFieldRef(i));
1554
2.20k
        }
1555
20.8k
    }
1556
1557
    /*-----------------------------------------------------------------
1558
     * Copy fields from poRelFeature...
1559
     *
1560
     * NOTE: For now, if no corresponding feature is found in RelTable
1561
     *       then we will just leave the corresponding fields unset.
1562
     *----------------------------------------------------------------*/
1563
10.4k
    for (int i = 0; poRelFeature && i < poRelFeature->GetFieldCount(); i++)
1564
4
    {
1565
4
        if (m_panRelTableFieldMap[i] != -1)
1566
2
        {
1567
2
            poCurFeature->SetField(m_panRelTableFieldMap[i],
1568
2
                                   poRelFeature->GetRawFieldRef(i));
1569
2
        }
1570
4
    }
1571
1572
10.4k
    return poCurFeature.release();
1573
10.4k
}
1574
1575
/**********************************************************************
1576
 *                   TABRelation::BuildFieldKey()
1577
 *
1578
 * Return the index key for the specified field in poFeature.
1579
 * Simply maps the call to the proper method in the TABINDFile class.
1580
 *
1581
 * Returns a reference to a TABINDFile internal buffer that should not
1582
 * be freed by the caller.
1583
 **********************************************************************/
1584
GByte *TABRelation::BuildFieldKey(TABFeature *poFeature, int nFieldNo,
1585
                                  TABFieldType eType, int nIndexNo)
1586
9.43k
{
1587
9.43k
    GByte *pKey = nullptr;
1588
1589
9.43k
    switch (eType)
1590
9.43k
    {
1591
0
        case TABFChar:
1592
0
            pKey = m_poRelINDFileRef->BuildKey(
1593
0
                nIndexNo, poFeature->GetFieldAsString(nFieldNo));
1594
0
            break;
1595
1596
0
        case TABFDecimal:
1597
0
        case TABFFloat:
1598
0
            pKey = m_poRelINDFileRef->BuildKey(
1599
0
                nIndexNo, poFeature->GetFieldAsDouble(nFieldNo));
1600
0
            break;
1601
1602
        // __TODO__ DateTime fields are 8 bytes long, not supported yet by
1603
        // the indexing code (see bug #1844).
1604
0
        case TABFDateTime:
1605
0
            CPLError(
1606
0
                CE_Failure, CPLE_NotSupported,
1607
0
                "TABRelation on field of type DateTime not supported yet.");
1608
0
            break;
1609
1610
24
        case TABFInteger:
1611
24
        case TABFSmallInt:
1612
24
        case TABFDate:
1613
24
        case TABFTime:
1614
24
        case TABFLogical:
1615
9.43k
        default:
1616
9.43k
            pKey = m_poRelINDFileRef->BuildKey(
1617
9.43k
                nIndexNo, poFeature->GetFieldAsInteger(nFieldNo));
1618
9.43k
            break;
1619
9.43k
    }
1620
1621
9.43k
    return pKey;
1622
9.43k
}
1623
1624
/**********************************************************************
1625
 *                   TABRelation::GetNativeFieldType()
1626
 *
1627
 * Returns the native MapInfo field type for the specified field.
1628
 *
1629
 * Returns TABFUnknown if file is not opened, or if specified field index is
1630
 * invalid.
1631
 *
1632
 * Note that field ids are positive and start at 0.
1633
 **********************************************************************/
1634
TABFieldType TABRelation::GetNativeFieldType(int nFieldId)
1635
0
{
1636
0
    int i, numFields;
1637
1638
0
    if (m_poMainTable == nullptr || m_poRelTable == nullptr ||
1639
0
        m_panMainTableFieldMap == nullptr || m_panRelTableFieldMap == nullptr)
1640
0
        return TABFUnknown;
1641
1642
    /*-----------------------------------------------------------------
1643
     * Look for nFieldId in the field maps and call the corresponding
1644
     * TAB file's GetNativeFieldType()
1645
     *----------------------------------------------------------------*/
1646
0
    numFields = m_poMainTable->GetLayerDefn()->GetFieldCount();
1647
0
    for (i = 0; i < numFields; i++)
1648
0
    {
1649
0
        if (m_panMainTableFieldMap[i] == nFieldId)
1650
0
        {
1651
0
            return m_poMainTable->GetNativeFieldType(i);
1652
0
        }
1653
0
    }
1654
1655
0
    numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
1656
0
    for (i = 0; i < numFields; i++)
1657
0
    {
1658
0
        if (m_panRelTableFieldMap[i] == nFieldId)
1659
0
        {
1660
0
            return m_poRelTable->GetNativeFieldType(i);
1661
0
        }
1662
0
    }
1663
1664
0
    return TABFUnknown;
1665
0
}
1666
1667
/**********************************************************************
1668
 *                   TABRelation::AddFieldNative()
1669
 *
1670
 * Create a new field using a native mapinfo data type... this is an
1671
 * alternative to defining fields through the OGR interface.
1672
 * This function should be called after creating a new dataset, but before
1673
 * writing the first feature.
1674
 *
1675
 * This function will build/update the OGRFeatureDefn that will have to be
1676
 * used when writing features to this dataset.
1677
 *
1678
 * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn().
1679
 *
1680
 * Returns 0 on success, -1 on error.
1681
 **********************************************************************/
1682
int TABRelation::AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
1683
                                int nWidth /*=0*/, int nPrecision /*=0*/,
1684
                                GBool bIndexed /*=FALSE*/,
1685
                                GBool bUnique /*=FALSE*/, int bApproxOK)
1686
0
{
1687
0
    if (m_poMainTable == nullptr || m_poRelTable == nullptr ||
1688
0
        m_panMainTableFieldMap == nullptr || m_panRelTableFieldMap == nullptr)
1689
0
        return -1;
1690
1691
0
    if (!bUnique)
1692
0
    {
1693
        /*-------------------------------------------------------------
1694
         * Add field to poMainTable and to m_poDefn
1695
         *------------------------------------------------------------*/
1696
0
        if (m_poMainTable->AddFieldNative(pszName, eMapInfoType, nWidth,
1697
0
                                          nPrecision, bIndexed, bUnique,
1698
0
                                          bApproxOK) != 0)
1699
0
            return -1;
1700
1701
0
        const OGRFeatureDefn *poMainDefn = m_poMainTable->GetLayerDefn();
1702
1703
0
        m_panMainTableFieldMap = static_cast<int *>(CPLRealloc(
1704
0
            m_panMainTableFieldMap, poMainDefn->GetFieldCount() * sizeof(int)));
1705
1706
0
        m_poDefn->AddFieldDefn(
1707
0
            poMainDefn->GetFieldDefn(poMainDefn->GetFieldCount() - 1));
1708
1709
0
        m_panMainTableFieldMap[poMainDefn->GetFieldCount() - 1] =
1710
0
            m_poDefn->GetFieldCount() - 1;
1711
0
    }
1712
0
    else
1713
0
    {
1714
        /*-------------------------------------------------------------
1715
         * Add field to poRelTable and to m_poDefn
1716
         *------------------------------------------------------------*/
1717
0
        if (m_poRelTable->AddFieldNative(pszName, eMapInfoType, nWidth,
1718
0
                                         nPrecision, bIndexed, bUnique,
1719
0
                                         bApproxOK) != 0)
1720
0
            return -1;
1721
1722
0
        const OGRFeatureDefn *poRelDefn = m_poRelTable->GetLayerDefn();
1723
1724
0
        m_panRelTableFieldMap = static_cast<int *>(CPLRealloc(
1725
0
            m_panRelTableFieldMap, poRelDefn->GetFieldCount() * sizeof(int)));
1726
1727
0
        m_poDefn->AddFieldDefn(
1728
0
            poRelDefn->GetFieldDefn(poRelDefn->GetFieldCount() - 1));
1729
1730
0
        m_panRelTableFieldMap[poRelDefn->GetFieldCount() - 1] =
1731
0
            m_poDefn->GetFieldCount() - 1;
1732
1733
        // The first field in this table must be indexed.
1734
0
        if (poRelDefn->GetFieldCount() == 1)
1735
0
            m_poRelTable->SetFieldIndexed(0);
1736
0
    }
1737
1738
0
    return 0;
1739
0
}
1740
1741
/**********************************************************************
1742
 *                   TABRelation::IsFieldIndexed()
1743
 *
1744
 * Returns TRUE is specified field is indexed.
1745
 *
1746
 * Note that field ids are positive and start at 0.
1747
 **********************************************************************/
1748
GBool TABRelation::IsFieldIndexed(int nFieldId)
1749
0
{
1750
0
    int i, numFields;
1751
1752
0
    if (m_poMainTable == nullptr || m_poRelTable == nullptr ||
1753
0
        m_panMainTableFieldMap == nullptr || m_panRelTableFieldMap == nullptr)
1754
0
        return FALSE;
1755
1756
    /*-----------------------------------------------------------------
1757
     * Look for nFieldId in the field maps and call the corresponding
1758
     * TAB file's GetNativeFieldType()
1759
     *----------------------------------------------------------------*/
1760
0
    numFields = m_poMainTable->GetLayerDefn()->GetFieldCount();
1761
0
    for (i = 0; i < numFields; i++)
1762
0
    {
1763
0
        if (m_panMainTableFieldMap[i] == nFieldId)
1764
0
        {
1765
0
            return m_poMainTable->IsFieldIndexed(i);
1766
0
        }
1767
0
    }
1768
1769
0
    numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
1770
0
    for (i = 0; i < numFields; i++)
1771
0
    {
1772
0
        if (m_panRelTableFieldMap[i] == nFieldId)
1773
0
        {
1774
0
            return m_poRelTable->IsFieldIndexed(i);
1775
0
        }
1776
0
    }
1777
1778
0
    return FALSE;
1779
0
}
1780
1781
/**********************************************************************
1782
 *                   TABRelation::SetFieldIndexed()
1783
 *
1784
 * Request that the specified field be indexed.  This will create the .IND
1785
 * file, etc.
1786
 *
1787
 * Note that field ids are positive and start at 0.
1788
 *
1789
 * Returns 0 on success, -1 on error.
1790
 **********************************************************************/
1791
int TABRelation::SetFieldIndexed(int nFieldId)
1792
0
{
1793
0
    int i, numFields;
1794
1795
0
    if (m_poMainTable == nullptr || m_poRelTable == nullptr ||
1796
0
        m_panMainTableFieldMap == nullptr || m_panRelTableFieldMap == nullptr)
1797
0
        return -1;
1798
1799
    /*-----------------------------------------------------------------
1800
     * Look for nFieldId in the field maps and call the corresponding
1801
     * TAB file's GetNativeFieldType()
1802
     *----------------------------------------------------------------*/
1803
0
    numFields = m_poMainTable->GetLayerDefn()->GetFieldCount();
1804
0
    for (i = 0; i < numFields; i++)
1805
0
    {
1806
0
        if (m_panMainTableFieldMap[i] == nFieldId)
1807
0
        {
1808
0
            return m_poMainTable->SetFieldIndexed(i);
1809
0
        }
1810
0
    }
1811
1812
0
    numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
1813
0
    for (i = 0; i < numFields; i++)
1814
0
    {
1815
0
        if (m_panRelTableFieldMap[i] == nFieldId)
1816
0
        {
1817
0
            return m_poRelTable->SetFieldIndexed(i);
1818
0
        }
1819
0
    }
1820
1821
0
    return -1;
1822
0
}
1823
1824
/**********************************************************************
1825
 *                   TABRelation::IsFieldUnique()
1826
 *
1827
 * Returns TRUE is specified field is part of the unique table (poRelTable).
1828
 *
1829
 * Note that field ids are positive and start at 0.
1830
 **********************************************************************/
1831
GBool TABRelation::IsFieldUnique(int nFieldId)
1832
0
{
1833
0
    int i, numFields;
1834
1835
0
    if (m_poMainTable == nullptr || m_poRelTable == nullptr ||
1836
0
        m_panMainTableFieldMap == nullptr || m_panRelTableFieldMap == nullptr)
1837
0
        return FALSE;
1838
1839
    /*-----------------------------------------------------------------
1840
     * Look for nFieldId in the poRelTable field map
1841
     *----------------------------------------------------------------*/
1842
0
    numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
1843
0
    for (i = 0; i < numFields; i++)
1844
0
    {
1845
0
        if (m_panRelTableFieldMap[i] == nFieldId)
1846
0
        {
1847
0
            return TRUE;  // If it is here then it is unique!
1848
0
        }
1849
0
    }
1850
1851
0
    return FALSE;
1852
0
}
1853
1854
/**********************************************************************
1855
 *                   TABRelation::WriteFeature()
1856
 *
1857
 * Write a feature to this dataset.
1858
 *
1859
 * For now only sequential writes are supported (i.e. with nFeatureId=-1)
1860
 * but eventually we should be able to do random access by specifying
1861
 * a value through nFeatureId.
1862
 *
1863
 * Returns the new featureId (> 0) on success, or -1 if an
1864
 * error happened in which case, CPLError() will have been called to
1865
 * report the reason of the failure.
1866
 **********************************************************************/
1867
int TABRelation::WriteFeature(TABFeature *poFeature, int nFeatureId /*=-1*/)
1868
0
{
1869
0
    TABFeature *poMainFeature = nullptr;
1870
1871
0
    if (nFeatureId != -1)
1872
0
    {
1873
0
        CPLError(CE_Failure, CPLE_NotSupported,
1874
0
                 "WriteFeature(): random access not implemented yet.");
1875
0
        return -1;
1876
0
    }
1877
1878
0
    CPLAssert(m_poMainTable && m_poRelTable);
1879
1880
    // We'll need the feature Defn later...
1881
0
    const OGRFeatureDefn *poMainDefn = m_poMainTable->GetLayerDefn();
1882
0
    const OGRFeatureDefn *poRelDefn = m_poRelTable->GetLayerDefn();
1883
1884
    /*-----------------------------------------------------------------
1885
     * Create one feature for each table
1886
     * Copy the geometry only to the feature from the main table
1887
     *----------------------------------------------------------------*/
1888
0
    poMainFeature = poFeature->CloneTABFeature(poMainDefn);
1889
1890
0
    if (poFeature->GetFeatureClass() != TABFCNoGeomFeature)
1891
0
    {
1892
0
        OGRGeometry *poGeom = poFeature->GetGeometryRef();
1893
0
        poMainFeature->SetGeometry(poGeom);
1894
0
    }
1895
1896
    /*-----------------------------------------------------------------
1897
     * Copy fields to poMainFeature
1898
     *----------------------------------------------------------------*/
1899
0
    for (int i = 0; i < poMainDefn->GetFieldCount(); i++)
1900
0
    {
1901
0
        if (m_panMainTableFieldMap[i] != -1)
1902
0
        {
1903
0
            poMainFeature->SetField(
1904
0
                i, poFeature->GetRawFieldRef(m_panMainTableFieldMap[i]));
1905
0
        }
1906
0
    }
1907
1908
    /*-----------------------------------------------------------------
1909
     * Look for a record id for the unique fields, and write a new
1910
     * record if necessary
1911
     *----------------------------------------------------------------*/
1912
0
    int nRecordNo = 0;
1913
0
    int nUniqueIndexNo = -1;
1914
0
    if (m_panMainTableFieldMap[0] != -1)
1915
0
        nUniqueIndexNo = m_poRelTable->GetFieldIndexNumber(0);
1916
1917
0
    if (nUniqueIndexNo > 0)
1918
0
    {
1919
0
        GByte *pKey = BuildFieldKey(
1920
0
            poFeature, 0, m_poRelTable->GetNativeFieldType(0), nUniqueIndexNo);
1921
1922
0
        if (!pKey || (nRecordNo = m_poRelINDFileRef->FindFirst(nUniqueIndexNo,
1923
0
                                                               pKey)) == -1)
1924
0
            return -1;
1925
1926
0
        if (nRecordNo == 0)
1927
0
        {
1928
            /*---------------------------------------------------------
1929
             * No record in poRelTable yet for this unique value...
1930
             * add one now...
1931
             *--------------------------------------------------------*/
1932
0
            TABFeature *poRelFeature = new TABFeature(poRelDefn);
1933
1934
0
            for (int i = 0; i < poRelDefn->GetFieldCount(); i++)
1935
0
            {
1936
0
                if (m_panRelTableFieldMap[i] != -1)
1937
0
                {
1938
0
                    poRelFeature->SetField(
1939
0
                        i, poFeature->GetRawFieldRef(m_panRelTableFieldMap[i]));
1940
0
                }
1941
0
            }
1942
1943
0
            nRecordNo = ++m_nUniqueRecordNo;
1944
1945
0
            poRelFeature->SetField(m_nRelFieldNo, nRecordNo);
1946
1947
0
            if (m_poRelTable->CreateFeature(poRelFeature) == OGRERR_NONE)
1948
0
                return -1;
1949
1950
0
            delete poRelFeature;
1951
0
        }
1952
0
    }
1953
1954
    /*-----------------------------------------------------------------
1955
     * Write poMainFeature to the main table
1956
     *----------------------------------------------------------------*/
1957
0
    poMainFeature->SetField(m_nMainFieldNo, nRecordNo);
1958
1959
0
    if (m_poMainTable->CreateFeature(poMainFeature) != OGRERR_NONE)
1960
0
        nFeatureId = static_cast<int>(poMainFeature->GetFID());
1961
0
    else
1962
0
        nFeatureId = -1;
1963
1964
0
    delete poMainFeature;
1965
1966
0
    return nFeatureId;
1967
0
}
1968
1969
/**********************************************************************
1970
 *                   TABFile::SetFeatureDefn()
1971
 *
1972
 * NOT FULLY IMPLEMENTED YET...
1973
 *
1974
 * Returns 0 on success, -1 on error.
1975
 **********************************************************************/
1976
int TABRelation::SetFeatureDefn(
1977
    OGRFeatureDefn *poFeatureDefn,
1978
    CPL_UNUSED TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */)
1979
0
{
1980
0
    if (m_poDefn && m_poDefn->GetFieldCount() > 0)
1981
0
    {
1982
0
        CPLAssert(m_poDefn == nullptr);
1983
0
        return -1;
1984
0
    }
1985
1986
    /*-----------------------------------------------------------------
1987
     * Keep a reference to the OGRFeatureDefn... we'll have to take the
1988
     * reference count into account when we are done with it.
1989
     *----------------------------------------------------------------*/
1990
0
    if (m_poDefn)
1991
0
        m_poDefn->Release();
1992
0
    m_poDefn = poFeatureDefn;
1993
0
    m_poDefn->Reference();
1994
1995
0
    return 0;
1996
0
}