LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/mitab - mitab_tabview.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 253 690 36.7 %
Date: 2024-05-05 20:23:34 Functions: 17 45 37.8 %

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

Generated by: LCOV version 1.14