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

Generated by: LCOV version 1.14