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