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

Generated by: LCOV version 1.14