LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gml - hugefileresolver.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 767 953 80.5 %
Date: 2024-05-06 22:33:47 Functions: 23 23 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GML Reader
       4             :  * Purpose:  Implementation of GMLReader::HugeFileResolver() method.
       5             :  * Author:   Alessandro Furieri, a.furitier@lqt.it
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2011, Alessandro Furieri
       9             :  * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice shall be included
      19             :  * in all copies or substantial portions of the Software.
      20             :  *
      21             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      22             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
      24             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27             :  * DEALINGS IN THE SOFTWARE.
      28             :  *
      29             :  ******************************************************************************
      30             :  * Contributor: Alessandro Furieri, a.furieri@lqt.it
      31             :  * This module implements GML_SKIP_RESOLVE_ELEMS HUGE
      32             :  * Developed for Faunalia ( http://www.faunalia.it) with funding from
      33             :  * Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED AMBIENTALE
      34             :  *
      35             :  ****************************************************************************/
      36             : 
      37             : #include "cpl_port.h"
      38             : #include "gmlreader.h"
      39             : #include "gmlreaderp.h"
      40             : 
      41             : #include <algorithm>
      42             : #include <limits>
      43             : 
      44             : #include "cpl_conv.h"
      45             : #include "cpl_error.h"
      46             : #include "cpl_http.h"
      47             : #include "cpl_string.h"
      48             : #include "gmlutils.h"
      49             : #include "ogr_p.h"
      50             : 
      51             : #ifdef HAVE_SQLITE
      52             : #include <sqlite3.h>
      53             : 
      54             : #undef SQLITE_STATIC
      55             : #define SQLITE_STATIC ((sqlite3_destructor_type) nullptr)
      56             : 
      57             : #endif
      58             : 
      59             : /****************************************************/
      60             : /*      SQLite is absolutely required in order to   */
      61             : /*      support the HUGE xlink:href resolver        */
      62             : /****************************************************/
      63             : 
      64             : #ifdef HAVE_SQLITE
      65             : 
      66             : // Internal helper struct supporting GML tags <Edge>.
      67             : struct huge_tag
      68             : {
      69             :     CPLString *gmlTagValue;
      70             :     CPLString *gmlId;
      71             :     CPLString *gmlNodeFrom;
      72             :     CPLString *gmlNodeTo;
      73             :     bool bIsNodeFromHref;
      74             :     bool bIsNodeToHref;
      75             :     bool bHasCoords;
      76             :     bool bHasZ;
      77             :     double xNodeFrom;
      78             :     double yNodeFrom;
      79             :     double zNodeFrom;
      80             :     double xNodeTo;
      81             :     double yNodeTo;
      82             :     double zNodeTo;
      83             :     struct huge_tag *pNext;
      84             : };
      85             : 
      86             : // Internal helper struct supporting GML tags xlink:href.
      87             : struct huge_href
      88             : {
      89             :     CPLString *gmlId;
      90             :     CPLString *gmlText;
      91             :     const CPLXMLNode *psParent;
      92             :     const CPLXMLNode *psNode;
      93             :     // bool                bIsDirectedEdge;
      94             :     char cOrientation;
      95             :     struct huge_href *pNext;
      96             : };
      97             : 
      98             : // Internal struct supporting GML rewriting.
      99             : struct huge_child
     100             : {
     101             :     CPLXMLNode *psChild;
     102             :     struct huge_href *pItem;
     103             :     struct huge_child *pNext;
     104             : };
     105             : 
     106             : // Internal struct supporting GML rewriting.
     107             : struct huge_parent
     108             : {
     109             :     CPLXMLNode *psParent;
     110             :     struct huge_child *pFirst;
     111             :     // cppcheck-suppress unusedStructMember
     112             :     struct huge_child *pLast;
     113             :     struct huge_parent *pNext;
     114             : };
     115             : 
     116             : // Internal class supporting GML resolver for Huge Files (based on SQLite).
     117             : class huge_helper
     118             : {
     119             :   public:
     120           2 :     huge_helper()
     121           2 :         : hDB(nullptr), hNodes(nullptr), hEdges(nullptr), nodeSrs(nullptr),
     122             :           pFirst(nullptr), pLast(nullptr), pFirstHref(nullptr),
     123           2 :           pLastHref(nullptr), pFirstParent(nullptr), pLastParent(nullptr)
     124             :     {
     125           2 :     }
     126             : 
     127             :     sqlite3 *hDB;
     128             :     sqlite3_stmt *hNodes;
     129             :     sqlite3_stmt *hEdges;
     130             :     CPLString *nodeSrs;
     131             :     struct huge_tag *pFirst;
     132             :     struct huge_tag *pLast;
     133             :     struct huge_href *pFirstHref;
     134             :     struct huge_href *pLastHref;
     135             :     struct huge_parent *pFirstParent;
     136             :     struct huge_parent *pLastParent;
     137             : };
     138             : 
     139           2 : static bool gmlHugeFileSQLiteInit(huge_helper *helper)
     140             : {
     141             :     // Attempting to create SQLite tables.
     142           2 :     char *pszErrMsg = nullptr;
     143           2 :     sqlite3 *hDB = helper->hDB;
     144             : 
     145             :     // DB table: NODES.
     146             :     {
     147           2 :         const char osCommand[] = "CREATE TABLE nodes ("
     148             :                                  "     gml_id VARCHAR PRIMARY KEY, "
     149             :                                  "     x DOUBLE, "
     150             :                                  "     y DOUBLE, "
     151             :                                  "     z DOUBLE)";
     152             :         const int rc =
     153           2 :             sqlite3_exec(hDB, osCommand, nullptr, nullptr, &pszErrMsg);
     154           2 :         if (rc != SQLITE_OK)
     155             :         {
     156           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     157             :                      "Unable to create table nodes: %s", pszErrMsg);
     158           0 :             sqlite3_free(pszErrMsg);
     159           0 :             return false;
     160             :         }
     161             :     }
     162             : 
     163             :     // DB table: GML_EDGES.
     164             :     {
     165           2 :         const char osCommand[] = "CREATE TABLE gml_edges ("
     166             :                                  "     gml_id VARCHAR PRIMARY KEY, "
     167             :                                  "     gml_string BLOB, "
     168             :                                  "     gml_resolved BLOB, "
     169             :                                  "     node_from_id TEXT, "
     170             :                                  "     node_from_x DOUBLE, "
     171             :                                  "     node_from_y DOUBLE, "
     172             :                                  "     node_from_z DOUBLE, "
     173             :                                  "     node_to_id TEXT, "
     174             :                                  "     node_to_x DOUBLE, "
     175             :                                  "     node_to_y DOUBLE, "
     176             :                                  "     node_to_z DOUBLE)";
     177             :         const int rc =
     178           2 :             sqlite3_exec(hDB, osCommand, nullptr, nullptr, &pszErrMsg);
     179           2 :         if (rc != SQLITE_OK)
     180             :         {
     181           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     182             :                      "Unable to create table gml_edges: %s", pszErrMsg);
     183           0 :             sqlite3_free(pszErrMsg);
     184           0 :             return false;
     185             :         }
     186             :     }
     187             : 
     188             :     // DB table: NODES / Insert cursor.
     189             :     {
     190           2 :         const char osCommand[] =
     191             :             "INSERT OR IGNORE INTO nodes (gml_id, x, y, z) VALUES (?, ?, ?, ?)";
     192           2 :         sqlite3_stmt *hStmt = nullptr;
     193           2 :         const int rc = sqlite3_prepare_v2(hDB, osCommand, -1, &hStmt, nullptr);
     194           2 :         if (rc != SQLITE_OK)
     195             :         {
     196           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     197             :                      "Unable to create INSERT stmt for: nodes");
     198           0 :             return false;
     199             :         }
     200           2 :         helper->hNodes = hStmt;
     201             :     }
     202             : 
     203             :     // DB table: GML_EDGES / Insert cursor.
     204             :     {
     205           2 :         const char osCommand[] = "INSERT INTO gml_edges "
     206             :                                  "(gml_id, gml_string, gml_resolved, "
     207             :                                  "node_from_id, node_from_x, node_from_y, "
     208             :                                  "node_from_z, node_to_id, node_to_x, "
     209             :                                  "node_to_y, node_to_z) "
     210             :                                  "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
     211           2 :         sqlite3_stmt *hStmt = nullptr;
     212           2 :         const int rc = sqlite3_prepare_v2(hDB, osCommand, -1, &hStmt, nullptr);
     213           2 :         if (rc != SQLITE_OK)
     214             :         {
     215           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     216             :                      "Unable to create INSERT stmt for: gml_edges");
     217           0 :             return false;
     218             :         }
     219           2 :         helper->hEdges = hStmt;
     220             :     }
     221             : 
     222             :     // Starting a TRANSACTION.
     223           2 :     const int rc = sqlite3_exec(hDB, "BEGIN", nullptr, nullptr, &pszErrMsg);
     224           2 :     if (rc != SQLITE_OK)
     225             :     {
     226           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     227             :                  "Unable to perform BEGIN TRANSACTION: %s", pszErrMsg);
     228           0 :         sqlite3_free(pszErrMsg);
     229           0 :         return false;
     230             :     }
     231             : 
     232           2 :     return true;
     233             : }
     234             : 
     235          70 : static bool gmlHugeResolveEdgeNodes(const CPLXMLNode *psNode,
     236             :                                     const char *pszFromId, const char *pszToId)
     237             : {
     238          70 :     if (psNode->eType != CXT_Element || !EQUAL(psNode->pszValue, "Edge"))
     239             :     {
     240           0 :         return false;
     241             :     }
     242             : 
     243             :     // Resolves an Edge definition.
     244          70 :     CPLXMLNode *psDirNode_1 = nullptr;
     245          70 :     CPLXMLNode *psDirNode_2 = nullptr;
     246          70 :     CPLXMLNode *psOldNode_1 = nullptr;
     247          70 :     CPLXMLNode *psOldNode_2 = nullptr;
     248          70 :     CPLXMLNode *psNewNode_1 = nullptr;
     249          70 :     CPLXMLNode *psNewNode_2 = nullptr;
     250          70 :     int iToBeReplaced = 0;
     251          70 :     int iReplaced = 0;
     252             : 
     253          70 :     CPLXMLNode *psChild = psNode->psChild;
     254         350 :     while (psChild != nullptr)
     255             :     {
     256         280 :         if (psChild->eType == CXT_Element &&
     257         210 :             EQUAL(psChild->pszValue, "directedNode"))
     258             :         {
     259         140 :             char cOrientation = '+';
     260         140 :             CPLXMLNode *psOldNode = nullptr;
     261         140 :             CPLXMLNode *psAttr = psChild->psChild;
     262         350 :             while (psAttr != nullptr)
     263             :             {
     264         210 :                 if (psAttr->eType == CXT_Attribute &&
     265         166 :                     EQUAL(psAttr->pszValue, "xlink:href"))
     266          96 :                     psOldNode = psAttr;
     267         210 :                 if (psAttr->eType == CXT_Attribute &&
     268         166 :                     EQUAL(psAttr->pszValue, "orientation"))
     269             :                 {
     270          70 :                     const CPLXMLNode *psOrientation = psAttr->psChild;
     271          70 :                     if (psOrientation != nullptr)
     272             :                     {
     273          70 :                         if (psOrientation->eType == CXT_Text)
     274          70 :                             cOrientation = *(psOrientation->pszValue);
     275             :                     }
     276             :                 }
     277         210 :                 psAttr = psAttr->psNext;
     278             :             }
     279         140 :             if (psOldNode != nullptr)
     280             :             {
     281             :                 CPLXMLNode *psNewNode =
     282          96 :                     CPLCreateXMLNode(nullptr, CXT_Element, "Node");
     283             :                 CPLXMLNode *psGMLIdNode =
     284          96 :                     CPLCreateXMLNode(psNewNode, CXT_Attribute, "gml:id");
     285          96 :                 if (cOrientation == '-')
     286          49 :                     CPLCreateXMLNode(psGMLIdNode, CXT_Text, pszFromId);
     287             :                 else
     288          47 :                     CPLCreateXMLNode(psGMLIdNode, CXT_Text, pszToId);
     289          96 :                 if (iToBeReplaced == 0)
     290             :                 {
     291          70 :                     psDirNode_1 = psChild;
     292          70 :                     psOldNode_1 = psOldNode;
     293          70 :                     psNewNode_1 = psNewNode;
     294             :                 }
     295             :                 else
     296             :                 {
     297          26 :                     psDirNode_2 = psChild;
     298          26 :                     psOldNode_2 = psOldNode;
     299          26 :                     psNewNode_2 = psNewNode;
     300             :                 }
     301          96 :                 iToBeReplaced++;
     302             :             }
     303             :         }
     304         280 :         psChild = psChild->psNext;
     305             :     }
     306             : 
     307             :     // Rewriting the Edge GML definition.
     308          70 :     if (psDirNode_1 != nullptr)
     309             :     {
     310          70 :         if (psOldNode_1 != nullptr)
     311             :         {
     312          70 :             CPLRemoveXMLChild(psDirNode_1, psOldNode_1);
     313          70 :             CPLDestroyXMLNode(psOldNode_1);
     314          70 :             if (psNewNode_1 != nullptr)
     315             :             {
     316          70 :                 CPLAddXMLChild(psDirNode_1, psNewNode_1);
     317          70 :                 iReplaced++;
     318             :             }
     319             :         }
     320             :     }
     321          70 :     if (psDirNode_2 != nullptr)
     322             :     {
     323          26 :         if (psOldNode_2 != nullptr)
     324             :         {
     325          26 :             CPLRemoveXMLChild(psDirNode_2, psOldNode_2);
     326          26 :             CPLDestroyXMLNode(psOldNode_2);
     327          26 :             if (psNewNode_2 != nullptr)
     328             :             {
     329          26 :                 CPLAddXMLChild(psDirNode_2, psNewNode_2);
     330          26 :                 iReplaced++;
     331             :             }
     332             :         }
     333             :     }
     334             : 
     335          70 :     return iToBeReplaced == iReplaced;
     336             : }
     337             : 
     338           2 : static bool gmlHugeFileResolveEdges(huge_helper *helper)
     339             : {
     340             :     // Identifying any not yet resolved <Edge> GML string.
     341           2 :     sqlite3 *hDB = helper->hDB;
     342             : 
     343             :     // Query cursor.
     344           2 :     sqlite3_stmt *hQueryStmt = nullptr;
     345             :     {
     346           2 :         const char osCommand[] =
     347             :             "SELECT e.gml_id, e.gml_string, e.node_from_id, "
     348             :             "e.node_from_x, e.node_from_y, e.node_from_z, "
     349             :             "n1.gml_id, n1.x, n1.y, n1.z, e.node_to_id, "
     350             :             "e.node_to_x, e.node_to_y, e.node_to_z, "
     351             :             "n2.gml_id, n2.x, n2.y, n2.z "
     352             :             "FROM gml_edges AS e "
     353             :             "LEFT JOIN nodes AS n1 ON (n1.gml_id = e.node_from_id) "
     354             :             "LEFT JOIN nodes AS n2 ON (n2.gml_id = e.node_to_id)";
     355             :         const int rc =
     356           2 :             sqlite3_prepare_v2(hDB, osCommand, -1, &hQueryStmt, nullptr);
     357           2 :         if (rc != SQLITE_OK)
     358             :         {
     359           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     360             :                      "Unable to create QUERY stmt for Edge resolver");
     361           0 :             return false;
     362             :         }
     363             :     }
     364             : 
     365             :     // Update cursor.
     366           2 :     sqlite3_stmt *hUpdateStmt = nullptr;
     367             :     {
     368           2 :         const char osCommand[] = "UPDATE gml_edges "
     369             :                                  "SET gml_resolved = ?, "
     370             :                                  "gml_string = NULL "
     371             :                                  "WHERE gml_id = ?";
     372             :         const int rc =
     373           2 :             sqlite3_prepare_v2(hDB, osCommand, -1, &hUpdateStmt, nullptr);
     374           2 :         if (rc != SQLITE_OK)
     375             :         {
     376           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     377             :                      "Unable to create UPDATE stmt for resolved Edges");
     378           0 :             sqlite3_finalize(hQueryStmt);
     379           0 :             return false;
     380             :         }
     381             :     }
     382             : 
     383             :     // Starting a TRANSACTION.
     384           2 :     char *pszErrMsg = nullptr;
     385             :     {
     386           2 :         const int rc = sqlite3_exec(hDB, "BEGIN", nullptr, nullptr, &pszErrMsg);
     387           2 :         if (rc != SQLITE_OK)
     388             :         {
     389           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     390             :                      "Unable to perform BEGIN TRANSACTION: %s", pszErrMsg);
     391           0 :             sqlite3_free(pszErrMsg);
     392           0 :             sqlite3_finalize(hQueryStmt);
     393           0 :             sqlite3_finalize(hUpdateStmt);
     394           0 :             return false;
     395             :         }
     396             :     }
     397             : 
     398           2 :     int iCount = 0;
     399           2 :     bool bError = false;
     400             : 
     401             :     // Looping on the QUERY result-set.
     402             :     while (true)
     403             :     {
     404          83 :         const char *pszGmlId = nullptr;
     405          83 :         const char *pszGmlString = nullptr;
     406          83 :         const char *pszFromId = nullptr;
     407          83 :         double xFrom = std::numeric_limits<double>::quiet_NaN();
     408          83 :         double yFrom = std::numeric_limits<double>::quiet_NaN();
     409          83 :         double zFrom = std::numeric_limits<double>::quiet_NaN();
     410          83 :         const char *pszNodeFromId = nullptr;
     411          83 :         double xNodeFrom = std::numeric_limits<double>::quiet_NaN();
     412          83 :         double yNodeFrom = std::numeric_limits<double>::quiet_NaN();
     413          83 :         double zNodeFrom = std::numeric_limits<double>::quiet_NaN();
     414          83 :         const char *pszToId = nullptr;
     415          83 :         double xTo = std::numeric_limits<double>::quiet_NaN();
     416          83 :         double yTo = std::numeric_limits<double>::quiet_NaN();
     417          83 :         double zTo = std::numeric_limits<double>::quiet_NaN();
     418          83 :         const char *pszNodeToId = nullptr;
     419          83 :         double xNodeTo = std::numeric_limits<double>::quiet_NaN();
     420          83 :         double yNodeTo = std::numeric_limits<double>::quiet_NaN();
     421          83 :         double zNodeTo = std::numeric_limits<double>::quiet_NaN();
     422             : 
     423          83 :         const int rc2 = sqlite3_step(hQueryStmt);
     424          83 :         if (rc2 == SQLITE_DONE)
     425           2 :             break;
     426             : 
     427          81 :         if (rc2 == SQLITE_ROW)
     428             :         {
     429          81 :             bError = false;
     430             :             pszGmlId = reinterpret_cast<const char *>(
     431          81 :                 sqlite3_column_text(hQueryStmt, 0));
     432          81 :             if (sqlite3_column_type(hQueryStmt, 1) != SQLITE_NULL)
     433             :             {
     434             :                 pszGmlString = static_cast<const char *>(
     435          70 :                     sqlite3_column_blob(hQueryStmt, 1));
     436             :             }
     437          81 :             if (sqlite3_column_type(hQueryStmt, 2) != SQLITE_NULL)
     438             :             {
     439             :                 pszFromId = reinterpret_cast<const char *>(
     440          81 :                     sqlite3_column_text(hQueryStmt, 2));
     441             :             }
     442          81 :             if (sqlite3_column_type(hQueryStmt, 3) != SQLITE_NULL)
     443             :             {
     444          81 :                 xFrom = sqlite3_column_double(hQueryStmt, 3);
     445             :             }
     446          81 :             if (sqlite3_column_type(hQueryStmt, 4) != SQLITE_NULL)
     447             :             {
     448          81 :                 yFrom = sqlite3_column_double(hQueryStmt, 4);
     449             :             }
     450          81 :             if (sqlite3_column_type(hQueryStmt, 5) != SQLITE_NULL)
     451             :             {
     452           0 :                 zFrom = sqlite3_column_double(hQueryStmt, 5);
     453             :             }
     454          81 :             if (sqlite3_column_type(hQueryStmt, 6) != SQLITE_NULL)
     455             :             {
     456             :                 pszNodeFromId = reinterpret_cast<const char *>(
     457          81 :                     sqlite3_column_text(hQueryStmt, 6));
     458             :             }
     459          81 :             if (sqlite3_column_type(hQueryStmt, 7) != SQLITE_NULL)
     460             :             {
     461          81 :                 xNodeFrom = sqlite3_column_double(hQueryStmt, 7);
     462             :             }
     463          81 :             if (sqlite3_column_type(hQueryStmt, 8) != SQLITE_NULL)
     464             :             {
     465          81 :                 yNodeFrom = sqlite3_column_double(hQueryStmt, 8);
     466             :             }
     467          81 :             if (sqlite3_column_type(hQueryStmt, 9) != SQLITE_NULL)
     468             :             {
     469           0 :                 zNodeFrom = sqlite3_column_double(hQueryStmt, 9);
     470             :             }
     471          81 :             if (sqlite3_column_type(hQueryStmt, 10) != SQLITE_NULL)
     472             :             {
     473             :                 pszToId = reinterpret_cast<const char *>(
     474          81 :                     sqlite3_column_text(hQueryStmt, 10));
     475             :             }
     476          81 :             if (sqlite3_column_type(hQueryStmt, 11) != SQLITE_NULL)
     477             :             {
     478          81 :                 xTo = sqlite3_column_double(hQueryStmt, 11);
     479             :             }
     480          81 :             if (sqlite3_column_type(hQueryStmt, 12) != SQLITE_NULL)
     481             :             {
     482          81 :                 yTo = sqlite3_column_double(hQueryStmt, 12);
     483             :             }
     484          81 :             if (sqlite3_column_type(hQueryStmt, 13) != SQLITE_NULL)
     485             :             {
     486           0 :                 zTo = sqlite3_column_double(hQueryStmt, 13);
     487             :             }
     488          81 :             if (sqlite3_column_type(hQueryStmt, 14) != SQLITE_NULL)
     489             :             {
     490             :                 pszNodeToId = reinterpret_cast<const char *>(
     491          81 :                     sqlite3_column_text(hQueryStmt, 14));
     492             :             }
     493          81 :             if (sqlite3_column_type(hQueryStmt, 15) != SQLITE_NULL)
     494             :             {
     495          81 :                 xNodeTo = sqlite3_column_double(hQueryStmt, 15);
     496             :             }
     497          81 :             if (sqlite3_column_type(hQueryStmt, 16) != SQLITE_NULL)
     498             :             {
     499          81 :                 yNodeTo = sqlite3_column_double(hQueryStmt, 16);
     500             :             }
     501          81 :             if (sqlite3_column_type(hQueryStmt, 17) != SQLITE_NULL)
     502             :             {
     503           0 :                 zNodeTo = sqlite3_column_double(hQueryStmt, 17);
     504             :             }
     505             : 
     506             :             // Checking for consistency.
     507          81 :             if (pszFromId == nullptr || std::isnan(xFrom) || std::isnan(yFrom))
     508             :             {
     509           0 :                 bError = true;
     510           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     511             :                          "Edge gml:id=\"%s\": invalid Node-from", pszGmlId);
     512             :             }
     513             :             else
     514             :             {
     515          81 :                 if (pszNodeFromId == nullptr)
     516             :                 {
     517           0 :                     bError = true;
     518           0 :                     CPLError(
     519             :                         CE_Failure, CPLE_AppDefined,
     520             :                         "Edge gml:id=\"%s\": undeclared Node gml:id=\"%s\"",
     521             :                         pszGmlId, pszFromId);
     522             :                 }
     523          81 :                 else if (std::isnan(xNodeFrom) || std::isnan(yNodeFrom))
     524             :                 {
     525           0 :                     bError = true;
     526           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     527             :                              "Edge gml:id=\"%s\": "
     528             :                              "unknown coords for Node gml:id=\"%s\"",
     529             :                              pszGmlId, pszFromId);
     530             :                 }
     531          81 :                 else if (xFrom != xNodeFrom || yFrom != yNodeFrom)
     532             :                 {
     533           0 :                     bError = true;
     534           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     535             :                              "Edge gml:id=\"%s\": mismatching coords for Node "
     536             :                              "gml:id=\"%s\"",
     537             :                              pszGmlId, pszFromId);
     538             :                 }
     539             :                 else
     540             :                 {
     541          81 :                     if (std::isnan(zFrom) && std::isnan(zNodeFrom))
     542             :                     {
     543             :                         ;
     544             :                     }
     545           0 :                     else if (std::isnan(zFrom) || std::isnan(zNodeFrom))
     546             :                     {
     547           0 :                         bError = true;
     548           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     549             :                                  "Edge gml:id=\"%s\": mismatching 2D/3D for "
     550             :                                  "Node gml:id=\"%s\"",
     551             :                                  pszGmlId, pszFromId);
     552             :                     }
     553           0 :                     else if (zFrom != zNodeFrom)
     554             :                     {
     555           0 :                         bError = true;
     556           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     557             :                                  "Edge gml:id=\"%s\": mismatching Z coord for "
     558             :                                  "Node gml:id=\"%s\"",
     559             :                                  pszGmlId, pszFromId);
     560             :                     }
     561             :                 }
     562             :             }
     563          81 :             if (pszToId == nullptr || std::isnan(xTo) || std::isnan(yTo))
     564             :             {
     565           0 :                 bError = true;
     566           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     567             :                          "Edge gml:id=\"%s\": invalid Node-to", pszGmlId);
     568             :             }
     569             :             else
     570             :             {
     571          81 :                 if (pszNodeToId == nullptr)
     572             :                 {
     573           0 :                     bError = true;
     574           0 :                     CPLError(
     575             :                         CE_Failure, CPLE_AppDefined,
     576             :                         "Edge gml:id=\"%s\": undeclared Node gml:id=\"%s\"",
     577             :                         pszGmlId, pszToId);
     578             :                 }
     579          81 :                 else if (std::isnan(xNodeTo) || std::isnan(yNodeTo))
     580             :                 {
     581           0 :                     bError = true;
     582           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     583             :                              "Edge gml:id=\"%s\": "
     584             :                              "unknown coords for Node gml:id=\"%s\"",
     585             :                              pszGmlId, pszToId);
     586             :                 }
     587          81 :                 else if (xTo != xNodeTo || yTo != yNodeTo)
     588             :                 {
     589           0 :                     bError = true;
     590           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     591             :                              "Edge gml:id=\"%s\": mismatching coords for Node "
     592             :                              "gml:id=\"%s\"",
     593             :                              pszGmlId, pszToId);
     594             :                 }
     595             :                 else
     596             :                 {
     597          81 :                     if (std::isnan(zTo) && std::isnan(zNodeTo))
     598             :                     {
     599             :                         ;
     600             :                     }
     601           0 :                     else if (std::isnan(zTo) || std::isnan(zNodeTo))
     602             :                     {
     603           0 :                         bError = true;
     604           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     605             :                                  "Edge gml:id=\"%s\": mismatching 2D/3D for "
     606             :                                  "Node gml:id=\"%s\"",
     607             :                                  pszGmlId, pszToId);
     608             :                     }
     609           0 :                     else if (zTo != zNodeTo)
     610             :                     {
     611           0 :                         bError = true;
     612           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     613             :                                  "Edge gml:id=\"%s\": mismatching Z coord for "
     614             :                                  "Node gml:id=\"%s\"",
     615             :                                  pszGmlId, pszToId);
     616             :                     }
     617             :                 }
     618             :             }
     619             : 
     620             :             // Updating the resolved Node.
     621          81 :             if (bError == false && pszGmlString != nullptr &&
     622          70 :                 pszFromId != nullptr && pszToId != nullptr)
     623             :             {
     624             :                 // coverity[tainted_data]
     625          70 :                 CPLXMLNode *psNode = CPLParseXMLString(pszGmlString);
     626          70 :                 if (psNode != nullptr)
     627             :                 {
     628          70 :                     if (gmlHugeResolveEdgeNodes(psNode, pszFromId, pszToId))
     629             :                     {
     630          70 :                         char *gmlText = CPLSerializeXMLTree(psNode);
     631          70 :                         sqlite3_reset(hUpdateStmt);
     632          70 :                         sqlite3_clear_bindings(hUpdateStmt);
     633          70 :                         sqlite3_bind_blob(hUpdateStmt, 1, gmlText,
     634          70 :                                           static_cast<int>(strlen(gmlText)),
     635             :                                           SQLITE_STATIC);
     636          70 :                         sqlite3_bind_text(hUpdateStmt, 2, pszGmlId, -1,
     637             :                                           SQLITE_STATIC);
     638             :                         {
     639          70 :                             const int rc = sqlite3_step(hUpdateStmt);
     640          70 :                             if (rc != SQLITE_OK && rc != SQLITE_DONE)
     641             :                             {
     642           0 :                                 CPLError(CE_Failure, CPLE_AppDefined,
     643             :                                          "UPDATE resolved Edge \"%s\" "
     644             :                                          "sqlite3_step() failed:\n  %s",
     645             :                                          pszGmlId, sqlite3_errmsg(hDB));
     646             :                             }
     647             :                         }
     648          70 :                         CPLFree(gmlText);
     649          70 :                         iCount++;
     650          70 :                         if ((iCount % 1024) == 1023)
     651             :                         {
     652             :                             // Committing the current TRANSACTION.
     653           0 :                             const int rc3 = sqlite3_exec(hDB, "COMMIT", nullptr,
     654             :                                                          nullptr, &pszErrMsg);
     655           0 :                             if (rc3 != SQLITE_OK)
     656             :                             {
     657           0 :                                 CPLError(
     658             :                                     CE_Failure, CPLE_AppDefined,
     659             :                                     "Unable to perform COMMIT TRANSACTION: %s",
     660             :                                     pszErrMsg);
     661           0 :                                 sqlite3_free(pszErrMsg);
     662           0 :                                 return false;
     663             :                             }
     664             :                             // Restarting a new TRANSACTION.
     665           0 :                             const int rc4 = sqlite3_exec(hDB, "BEGIN", nullptr,
     666             :                                                          nullptr, &pszErrMsg);
     667           0 :                             if (rc4 != SQLITE_OK)
     668             :                             {
     669           0 :                                 CPLError(
     670             :                                     CE_Failure, CPLE_AppDefined,
     671             :                                     "Unable to perform BEGIN TRANSACTION: %s",
     672             :                                     pszErrMsg);
     673           0 :                                 sqlite3_free(pszErrMsg);
     674           0 :                                 sqlite3_finalize(hQueryStmt);
     675           0 :                                 sqlite3_finalize(hUpdateStmt);
     676           0 :                                 return false;
     677             :                             }
     678             :                         }
     679             :                     }
     680          70 :                     CPLDestroyXMLNode(psNode);
     681             :                 }
     682             :             }
     683             :         }
     684             :         else
     685             :         {
     686           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     687             :                      "Edge resolver QUERY: sqlite3_step(%s)",
     688             :                      sqlite3_errmsg(hDB));
     689           0 :             sqlite3_finalize(hQueryStmt);
     690           0 :             sqlite3_finalize(hUpdateStmt);
     691           0 :             return false;
     692             :         }
     693          81 :     }
     694           2 :     sqlite3_finalize(hQueryStmt);
     695           2 :     sqlite3_finalize(hUpdateStmt);
     696             : 
     697             :     // Committing the current TRANSACTION.
     698           2 :     const int rc = sqlite3_exec(hDB, "COMMIT", nullptr, nullptr, &pszErrMsg);
     699           2 :     if (rc != SQLITE_OK)
     700             :     {
     701           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     702             :                  "Unable to perform COMMIT TRANSACTION: %s", pszErrMsg);
     703           0 :         sqlite3_free(pszErrMsg);
     704           0 :         return false;
     705             :     }
     706             : 
     707           2 :     return !bError;
     708             : }
     709             : 
     710          72 : static bool gmlHugeFileSQLiteInsert(huge_helper *helper)
     711             : {
     712             :     // Inserting any appropriate row into the SQLite DB.
     713             : 
     714             :     // Looping on GML tags.
     715          72 :     struct huge_tag *pItem = helper->pFirst;
     716         153 :     while (pItem != nullptr)
     717             :     {
     718          81 :         if (pItem->bHasCoords)
     719             :         {
     720          81 :             if (pItem->gmlNodeFrom != nullptr)
     721             :             {
     722          81 :                 sqlite3_reset(helper->hNodes);
     723          81 :                 sqlite3_clear_bindings(helper->hNodes);
     724          81 :                 sqlite3_bind_text(helper->hNodes, 1,
     725          81 :                                   pItem->gmlNodeFrom->c_str(), -1,
     726             :                                   SQLITE_STATIC);
     727          81 :                 sqlite3_bind_double(helper->hNodes, 2, pItem->xNodeFrom);
     728          81 :                 sqlite3_bind_double(helper->hNodes, 3, pItem->yNodeFrom);
     729          81 :                 if (pItem->bHasZ)
     730           0 :                     sqlite3_bind_double(helper->hNodes, 4, pItem->zNodeFrom);
     731          81 :                 sqlite3_bind_null(helper->hNodes, 5);
     732          81 :                 const int rc = sqlite3_step(helper->hNodes);
     733          81 :                 if (rc != SQLITE_OK && rc != SQLITE_DONE)
     734             :                 {
     735           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     736             :                              "sqlite3_step() failed:\n  %s (gmlNodeFrom id=%s)",
     737             :                              sqlite3_errmsg(helper->hDB),
     738           0 :                              pItem->gmlNodeFrom->c_str());
     739           0 :                     return false;
     740             :                 }
     741             :             }
     742          81 :             if (pItem->gmlNodeTo != nullptr)
     743             :             {
     744          81 :                 sqlite3_reset(helper->hNodes);
     745          81 :                 sqlite3_clear_bindings(helper->hNodes);
     746          81 :                 sqlite3_bind_text(helper->hNodes, 1, pItem->gmlNodeTo->c_str(),
     747             :                                   -1, SQLITE_STATIC);
     748          81 :                 sqlite3_bind_double(helper->hNodes, 2, pItem->xNodeTo);
     749          81 :                 sqlite3_bind_double(helper->hNodes, 3, pItem->yNodeTo);
     750          81 :                 if (pItem->bHasZ)
     751           0 :                     sqlite3_bind_double(helper->hNodes, 4, pItem->zNodeTo);
     752          81 :                 sqlite3_bind_null(helper->hNodes, 5);
     753          81 :                 const int rc = sqlite3_step(helper->hNodes);
     754          81 :                 if (rc != SQLITE_OK && rc != SQLITE_DONE)
     755             :                 {
     756           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     757             :                              "sqlite3_step() failed:\n  %s (gmlNodeTo id=%s)",
     758             :                              sqlite3_errmsg(helper->hDB),
     759           0 :                              pItem->gmlNodeTo->c_str());
     760           0 :                     return false;
     761             :                 }
     762             :             }
     763             :         }
     764             : 
     765             :         // gml:id
     766          81 :         sqlite3_reset(helper->hEdges);
     767          81 :         sqlite3_clear_bindings(helper->hEdges);
     768          81 :         sqlite3_bind_text(helper->hEdges, 1, pItem->gmlId->c_str(), -1,
     769             :                           SQLITE_STATIC);
     770          81 :         if (pItem->bIsNodeFromHref == false && pItem->bIsNodeToHref == false)
     771             :         {
     772          11 :             sqlite3_bind_null(helper->hEdges, 2);
     773          11 :             sqlite3_bind_blob(
     774          11 :                 helper->hEdges, 3, pItem->gmlTagValue->c_str(),
     775          11 :                 static_cast<int>(strlen(pItem->gmlTagValue->c_str())),
     776             :                 SQLITE_STATIC);
     777             :         }
     778             :         else
     779             :         {
     780          70 :             sqlite3_bind_blob(
     781          70 :                 helper->hEdges, 2, pItem->gmlTagValue->c_str(),
     782          70 :                 static_cast<int>(strlen(pItem->gmlTagValue->c_str())),
     783             :                 SQLITE_STATIC);
     784          70 :             sqlite3_bind_null(helper->hEdges, 3);
     785             :         }
     786          81 :         if (pItem->gmlNodeFrom != nullptr)
     787          81 :             sqlite3_bind_text(helper->hEdges, 4, pItem->gmlNodeFrom->c_str(),
     788             :                               -1, SQLITE_STATIC);
     789             :         else
     790           0 :             sqlite3_bind_null(helper->hEdges, 4);
     791          81 :         if (pItem->bHasCoords)
     792             :         {
     793          81 :             sqlite3_bind_double(helper->hEdges, 5, pItem->xNodeFrom);
     794          81 :             sqlite3_bind_double(helper->hEdges, 6, pItem->yNodeFrom);
     795          81 :             if (pItem->bHasZ)
     796           0 :                 sqlite3_bind_double(helper->hEdges, 7, pItem->zNodeFrom);
     797             :             else
     798          81 :                 sqlite3_bind_null(helper->hEdges, 7);
     799             :         }
     800             :         else
     801             :         {
     802           0 :             sqlite3_bind_null(helper->hEdges, 5);
     803           0 :             sqlite3_bind_null(helper->hEdges, 6);
     804           0 :             sqlite3_bind_null(helper->hEdges, 7);
     805             :         }
     806          81 :         if (pItem->gmlNodeTo != nullptr)
     807          81 :             sqlite3_bind_text(helper->hEdges, 8, pItem->gmlNodeTo->c_str(), -1,
     808             :                               SQLITE_STATIC);
     809             :         else
     810           0 :             sqlite3_bind_null(helper->hEdges, 8);
     811          81 :         if (pItem->bHasCoords)
     812             :         {
     813          81 :             sqlite3_bind_double(helper->hEdges, 9, pItem->xNodeTo);
     814          81 :             sqlite3_bind_double(helper->hEdges, 10, pItem->yNodeTo);
     815          81 :             if (pItem->bHasZ)
     816           0 :                 sqlite3_bind_double(helper->hEdges, 11, pItem->zNodeTo);
     817             :             else
     818          81 :                 sqlite3_bind_null(helper->hEdges, 11);
     819             :         }
     820             :         else
     821             :         {
     822           0 :             sqlite3_bind_null(helper->hEdges, 9);
     823           0 :             sqlite3_bind_null(helper->hEdges, 10);
     824           0 :             sqlite3_bind_null(helper->hEdges, 11);
     825             :         }
     826          81 :         const int rc = sqlite3_step(helper->hEdges);
     827          81 :         if (rc != SQLITE_OK && rc != SQLITE_DONE)
     828             :         {
     829           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     830             :                      "sqlite3_step() failed:\n  %s (edge gml:id=%s)",
     831           0 :                      sqlite3_errmsg(helper->hDB), pItem->gmlId->c_str());
     832           0 :             return false;
     833             :         }
     834          81 :         pItem = pItem->pNext;
     835             :     }
     836          72 :     return true;
     837             : }
     838             : 
     839          72 : static void gmlHugeFileReset(huge_helper *helper)
     840             : {
     841             :     // Resetting an empty helper struct.
     842          72 :     struct huge_tag *p = helper->pFirst;
     843             : 
     844             :     // Cleaning any previous item.
     845         153 :     while (p != nullptr)
     846             :     {
     847          81 :         struct huge_tag *pNext = p->pNext;
     848          81 :         if (p->gmlTagValue != nullptr)
     849          81 :             delete p->gmlTagValue;
     850          81 :         if (p->gmlId != nullptr)
     851          81 :             delete p->gmlId;
     852          81 :         if (p->gmlNodeFrom != nullptr)
     853          81 :             delete p->gmlNodeFrom;
     854          81 :         if (p->gmlNodeTo != nullptr)
     855          81 :             delete p->gmlNodeTo;
     856          81 :         delete p;
     857          81 :         p = pNext;
     858             :     }
     859          72 :     helper->pFirst = nullptr;
     860          72 :     helper->pLast = nullptr;
     861          72 : }
     862             : 
     863          14 : static void gmlHugeFileHrefReset(huge_helper *helper)
     864             : {
     865             :     // Resetting an empty helper struct.
     866          14 :     struct huge_href *p = helper->pFirstHref;
     867             : 
     868             :     // Cleaning any previous item.
     869         137 :     while (p != nullptr)
     870             :     {
     871         123 :         struct huge_href *pNext = p->pNext;
     872         123 :         if (p->gmlId != nullptr)
     873         123 :             delete p->gmlId;
     874         123 :         if (p->gmlText != nullptr)
     875         123 :             delete p->gmlText;
     876         123 :         delete p;
     877         123 :         p = pNext;
     878             :     }
     879          14 :     helper->pFirstHref = nullptr;
     880          14 :     helper->pLastHref = nullptr;
     881          14 : }
     882             : 
     883          14 : static bool gmlHugeFileHrefCheck(huge_helper *helper)
     884             : {
     885             :     // Testing for unresolved items.
     886          14 :     bool bError = false;
     887          14 :     struct huge_href *p = helper->pFirstHref;
     888         137 :     while (p != nullptr)
     889             :     {
     890         123 :         if (p->gmlText == nullptr)
     891             :         {
     892           0 :             bError = true;
     893           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     894             :                      "Edge xlink:href\"%s\": unresolved match",
     895           0 :                      p->gmlId->c_str());
     896             :         }
     897         123 :         p = p->pNext;
     898             :     }
     899             : 
     900          14 :     return !bError;
     901             : }
     902             : 
     903          14 : static void gmlHugeFileRewiterReset(huge_helper *helper)
     904             : {
     905             :     // Resetting an empty helper struct.
     906          14 :     struct huge_parent *p = helper->pFirstParent;
     907             : 
     908             :     // Cleaning any previous item.
     909          40 :     while (p != nullptr)
     910             :     {
     911          26 :         struct huge_parent *pNext = p->pNext;
     912          26 :         struct huge_child *pChild = p->pFirst;
     913         190 :         while (pChild != nullptr)
     914             :         {
     915         164 :             struct huge_child *pChildNext = pChild->pNext;
     916         164 :             delete pChild;
     917         164 :             pChild = pChildNext;
     918             :         }
     919          26 :         delete p;
     920          26 :         p = pNext;
     921             :     }
     922          14 :     helper->pFirstParent = nullptr;
     923          14 :     helper->pLastParent = nullptr;
     924          14 : }
     925             : 
     926          97 : static struct huge_tag *gmlHugeAddToHelper(huge_helper *helper,
     927             :                                            CPLString *gmlId,
     928             :                                            CPLString *gmlFragment)
     929             : {
     930             :     // Adding an item into the linked list.
     931             : 
     932             :     // Checking against duplicates.
     933          97 :     struct huge_tag *pItem = helper->pFirst;
     934         242 :     while (pItem != nullptr)
     935             :     {
     936         161 :         if (EQUAL(pItem->gmlId->c_str(), gmlId->c_str()))
     937          16 :             return nullptr;
     938         145 :         pItem = pItem->pNext;
     939             :     }
     940             : 
     941          81 :     pItem = new struct huge_tag;
     942          81 :     pItem->gmlId = gmlId;
     943          81 :     pItem->gmlTagValue = gmlFragment;
     944          81 :     pItem->gmlNodeFrom = nullptr;
     945          81 :     pItem->gmlNodeTo = nullptr;
     946          81 :     pItem->bIsNodeFromHref = false;
     947          81 :     pItem->bIsNodeToHref = false;
     948          81 :     pItem->bHasCoords = false;
     949          81 :     pItem->bHasZ = false;
     950          81 :     pItem->pNext = nullptr;
     951             : 
     952             :     // Appending the item to the linked list.
     953          81 :     if (helper->pFirst == nullptr)
     954          59 :         helper->pFirst = pItem;
     955          81 :     if (helper->pLast != nullptr)
     956          22 :         helper->pLast->pNext = pItem;
     957          81 :     helper->pLast = pItem;
     958          81 :     return pItem;
     959             : }
     960             : 
     961         123 : static void gmlHugeAddPendingToHelper(huge_helper *helper, CPLString *gmlId,
     962             :                                       const CPLXMLNode *psParent,
     963             :                                       const CPLXMLNode *psNode,
     964             :                                       // bool bIsDirectedEdge,
     965             :                                       char cOrientation)
     966             : {
     967             :     // Inserting an item into the linked list.
     968             : 
     969             :     // Checking against duplicates.
     970         123 :     struct huge_href *pItem = helper->pFirstHref;
     971         697 :     while (pItem != nullptr)
     972             :     {
     973         574 :         if( EQUAL(pItem->gmlId->c_str(), gmlId->c_str()) &&
     974           0 :             pItem->psParent == psParent  &&
     975         574 :             pItem->psNode == psNode &&
     976           0 :             pItem->cOrientation == cOrientation /* &&
     977             :             pItem->bIsDirectedEdge == bIsDirectedEdge */ )
     978             :         {
     979           0 :             delete gmlId;
     980           0 :             return;
     981             :         }
     982         574 :         pItem = pItem->pNext;
     983             :     }
     984             : 
     985         123 :     pItem = new struct huge_href;
     986         123 :     pItem->gmlId = gmlId;
     987         123 :     pItem->gmlText = nullptr;
     988         123 :     pItem->psParent = psParent;
     989         123 :     pItem->psNode = psNode;
     990             :     // pItem->bIsDirectedEdge = bIsDirectedEdge;
     991         123 :     pItem->cOrientation = cOrientation;
     992         123 :     pItem->pNext = nullptr;
     993             : 
     994             :     // Appending the item to the linked list.
     995         123 :     if (helper->pFirstHref == nullptr)
     996          14 :         helper->pFirstHref = pItem;
     997         123 :     if (helper->pLastHref != nullptr)
     998         109 :         helper->pLastHref->pNext = pItem;
     999         123 :     helper->pLastHref = pItem;
    1000             : }
    1001             : 
    1002          97 : static int gmlHugeFindGmlId(const CPLXMLNode *psNode, CPLString **gmlId)
    1003             : {
    1004             :     // Attempting to identify a gml:id value.
    1005          97 :     *gmlId = nullptr;
    1006          97 :     const CPLXMLNode *psChild = psNode->psChild;
    1007          97 :     while (psChild != nullptr)
    1008             :     {
    1009          97 :         if (psChild->eType == CXT_Attribute &&
    1010          97 :             EQUAL(psChild->pszValue, "gml:id"))
    1011             :         {
    1012          97 :             const CPLXMLNode *psIdValue = psChild->psChild;
    1013          97 :             if (psIdValue != nullptr)
    1014             :             {
    1015          97 :                 if (psIdValue->eType == CXT_Text)
    1016             :                 {
    1017          97 :                     *gmlId = new CPLString(psIdValue->pszValue);
    1018          97 :                     return true;
    1019             :                 }
    1020             :             }
    1021             :         }
    1022           0 :         psChild = psChild->psNext;
    1023             :     }
    1024           0 :     return false;
    1025             : }
    1026             : 
    1027          81 : static void gmlHugeFileNodeCoords(struct huge_tag *pItem,
    1028             :                                   const CPLXMLNode *psNode,
    1029             :                                   CPL_UNUSED CPLString **nodeSrs)
    1030             : {
    1031             :     // This function attempts to set coordinates for <Node> items
    1032             :     // when required (an <Edge> is expected to be processed).
    1033             : 
    1034             :     // Attempting to fetch Node coordinates.
    1035             :     CPLXMLNode *psTopoCurve =
    1036          81 :         CPLCreateXMLNode(nullptr, CXT_Element, "TopoCurve");
    1037             :     CPLXMLNode *psDirEdge =
    1038          81 :         CPLCreateXMLNode(psTopoCurve, CXT_Element, "directedEdge");
    1039          81 :     CPLXMLNode *psEdge = CPLCloneXMLTree((CPLXMLNode *)psNode);
    1040          81 :     CPLAddXMLChild(psDirEdge, psEdge);
    1041          81 :     OGRGeometry *poTopoCurve = GML2OGRGeometry_XMLNode(psTopoCurve, FALSE);
    1042          81 :     CPLDestroyXMLNode(psTopoCurve);
    1043          81 :     if (poTopoCurve != nullptr)
    1044             :     {
    1045          81 :         OGRGeometryCollection *poColl = poTopoCurve->toGeometryCollection();
    1046          81 :         const int iCount = poColl->getNumGeometries();
    1047          81 :         if (iCount == 1)
    1048             :         {
    1049          81 :             OGRGeometry *poChild = poColl->getGeometryRef(0);
    1050          81 :             int type = wkbFlatten(poChild->getGeometryType());
    1051          81 :             if (type == wkbLineString)
    1052             :             {
    1053          81 :                 OGRLineString *poLine = poChild->toLineString();
    1054          81 :                 const int iPoints = poLine->getNumPoints();
    1055          81 :                 if (iPoints >= 2)
    1056             :                 {
    1057          81 :                     pItem->bHasCoords = true;
    1058          81 :                     pItem->xNodeFrom = poLine->getX(0);
    1059          81 :                     pItem->yNodeFrom = poLine->getY(0);
    1060          81 :                     pItem->xNodeTo = poLine->getX(iPoints - 1);
    1061          81 :                     pItem->yNodeTo = poLine->getY(iPoints - 1);
    1062          81 :                     if (poLine->getCoordinateDimension() == 3)
    1063             :                     {
    1064           0 :                         pItem->zNodeFrom = poLine->getZ(0);
    1065           0 :                         pItem->zNodeTo = poLine->getZ(iPoints - 1);
    1066           0 :                         pItem->bHasZ = true;
    1067             :                     }
    1068             :                     else
    1069             :                     {
    1070          81 :                         pItem->bHasZ = false;
    1071             :                     }
    1072             :                 }
    1073             :             }
    1074             :         }
    1075          81 :         delete poTopoCurve;
    1076             :     }
    1077             : 
    1078             :     // Searching the <directedNode> sub-tags.
    1079          81 :     const CPLXMLNode *psChild = psNode->psChild;
    1080         405 :     while (psChild != nullptr)
    1081             :     {
    1082         324 :         if (psChild->eType == CXT_Element &&
    1083         243 :             EQUAL(psChild->pszValue, "directedNode"))
    1084             :         {
    1085         162 :             char cOrientation = '+';
    1086         162 :             const char *pszGmlId = nullptr;
    1087         162 :             bool bIsHref = false;
    1088         162 :             const CPLXMLNode *psAttr = psChild->psChild;
    1089         405 :             while (psAttr != nullptr)
    1090             :             {
    1091         243 :                 if (psAttr->eType == CXT_Attribute &&
    1092         177 :                     EQUAL(psAttr->pszValue, "xlink:href"))
    1093             :                 {
    1094          96 :                     const CPLXMLNode *psHref = psAttr->psChild;
    1095          96 :                     if (psHref != nullptr)
    1096             :                     {
    1097          96 :                         if (psHref->eType == CXT_Text)
    1098             :                         {
    1099          96 :                             pszGmlId = psHref->pszValue;
    1100          96 :                             bIsHref = true;
    1101             :                         }
    1102             :                     }
    1103             :                 }
    1104         243 :                 if (psAttr->eType == CXT_Attribute &&
    1105         177 :                     EQUAL(psAttr->pszValue, "orientation"))
    1106             :                 {
    1107          81 :                     const CPLXMLNode *psOrientation = psAttr->psChild;
    1108          81 :                     if (psOrientation != nullptr)
    1109             :                     {
    1110          81 :                         if (psOrientation->eType == CXT_Text)
    1111             :                         {
    1112          81 :                             cOrientation = *(psOrientation->pszValue);
    1113             :                         }
    1114             :                     }
    1115             :                 }
    1116         243 :                 if (psAttr->eType == CXT_Element &&
    1117          66 :                     EQUAL(psAttr->pszValue, "Node"))
    1118             :                 {
    1119          66 :                     const CPLXMLNode *psId = psAttr->psChild;
    1120         132 :                     while (psId != nullptr)
    1121             :                     {
    1122          66 :                         if (psId->eType == CXT_Attribute &&
    1123          66 :                             EQUAL(psId->pszValue, "gml:id"))
    1124             :                         {
    1125          66 :                             const CPLXMLNode *psIdGml = psId->psChild;
    1126          66 :                             if (psIdGml != nullptr)
    1127             :                             {
    1128          66 :                                 if (psIdGml->eType == CXT_Text)
    1129             :                                 {
    1130          66 :                                     pszGmlId = psIdGml->pszValue;
    1131          66 :                                     bIsHref = false;
    1132             :                                 }
    1133             :                             }
    1134             :                         }
    1135          66 :                         psId = psId->psNext;
    1136             :                     }
    1137             :                 }
    1138         243 :                 psAttr = psAttr->psNext;
    1139             :             }
    1140         162 :             if (pszGmlId != nullptr)
    1141             :             {
    1142         162 :                 CPLString *posNode = nullptr;
    1143         162 :                 if (bIsHref)
    1144             :                 {
    1145          96 :                     if (pszGmlId[0] != '#')
    1146             :                     {
    1147           0 :                         CPLError(CE_Warning, CPLE_NotSupported,
    1148             :                                  "Only values of xlink:href element starting "
    1149             :                                  "with '#' are supported, "
    1150             :                                  "so %s will not be properly recognized",
    1151             :                                  pszGmlId);
    1152             :                     }
    1153          96 :                     posNode = new CPLString(pszGmlId + 1);
    1154             :                 }
    1155             :                 else
    1156             :                 {
    1157          66 :                     posNode = new CPLString(pszGmlId);
    1158             :                 }
    1159         162 :                 if (cOrientation == '-')
    1160             :                 {
    1161          81 :                     pItem->gmlNodeFrom = posNode;
    1162          81 :                     pItem->bIsNodeFromHref = bIsHref;
    1163             :                 }
    1164             :                 else
    1165             :                 {
    1166          81 :                     pItem->gmlNodeTo = posNode;
    1167          81 :                     pItem->bIsNodeToHref = bIsHref;
    1168             :                 }
    1169             :                 // pszGmlId = NULL;
    1170             :                 // *bIsHref = false;
    1171             :                 // cOrientation = '+';
    1172             :             }
    1173             :         }
    1174         324 :         psChild = psChild->psNext;
    1175             :     }
    1176          81 : }
    1177             : 
    1178         251 : static void gmlHugeFileCheckXrefs(huge_helper *helper, const CPLXMLNode *psNode)
    1179             : {
    1180             :     // Identifying <Edge> GML nodes.
    1181         251 :     if (psNode->eType == CXT_Element)
    1182             :     {
    1183         251 :         if (EQUAL(psNode->pszValue, "Edge"))
    1184             :         {
    1185          97 :             CPLString *gmlId = nullptr;
    1186          97 :             if (gmlHugeFindGmlId(psNode, &gmlId))
    1187             :             {
    1188          97 :                 char *gmlText = CPLSerializeXMLTree(psNode);
    1189          97 :                 CPLString *gmlValue = new CPLString(gmlText);
    1190          97 :                 CPLFree(gmlText);
    1191             :                 struct huge_tag *pItem =
    1192          97 :                     gmlHugeAddToHelper(helper, gmlId, gmlValue);
    1193          97 :                 if (pItem != nullptr)
    1194             :                 {
    1195          81 :                     gmlHugeFileNodeCoords(pItem, psNode, &(helper->nodeSrs));
    1196             :                 }
    1197             :                 else
    1198             :                 {
    1199          16 :                     delete gmlId;
    1200          16 :                     delete gmlValue;
    1201             :                 }
    1202             :             }
    1203             :         }
    1204             :     }
    1205             : 
    1206             :     // Recursively scanning each Child GML node.
    1207         251 :     const CPLXMLNode *psChild = psNode->psChild;
    1208         813 :     while (psChild != nullptr)
    1209             :     {
    1210         562 :         if (psChild->eType == CXT_Element)
    1211             :         {
    1212         465 :             if (EQUAL(psChild->pszValue, "Edge") ||
    1213         383 :                 EQUAL(psChild->pszValue, "directedEdge"))
    1214             :             {
    1215         148 :                 gmlHugeFileCheckXrefs(helper, psChild);
    1216             :             }
    1217         465 :             if (EQUAL(psChild->pszValue, "directedFace"))
    1218             :             {
    1219          26 :                 const CPLXMLNode *psFace = psChild->psChild;
    1220          26 :                 if (psFace != nullptr)
    1221             :                 {
    1222          26 :                     if (psFace->eType == CXT_Element &&
    1223          24 :                         EQUAL(psFace->pszValue, "Face"))
    1224             :                     {
    1225          24 :                         const CPLXMLNode *psDirEdge = psFace->psChild;
    1226         181 :                         while (psDirEdge != nullptr)
    1227             :                         {
    1228         157 :                             const CPLXMLNode *psEdge = psDirEdge->psChild;
    1229         379 :                             while (psEdge != nullptr)
    1230             :                             {
    1231         222 :                                 if (psEdge->eType == CXT_Element &&
    1232          15 :                                     EQUAL(psEdge->pszValue, "Edge"))
    1233          15 :                                     gmlHugeFileCheckXrefs(helper, psEdge);
    1234         222 :                                 psEdge = psEdge->psNext;
    1235             :                             }
    1236         157 :                             psDirEdge = psDirEdge->psNext;
    1237             :                         }
    1238             :                     }
    1239             :                 }
    1240             :             }
    1241             :         }
    1242         562 :         psChild = psChild->psNext;
    1243             :     }
    1244             : 
    1245             :     // Recursively scanning each GML of the same level.
    1246         251 :     const CPLXMLNode *psNext = psNode->psNext;
    1247         267 :     while (psNext != nullptr)
    1248             :     {
    1249          16 :         if (psNext->eType == CXT_Element)
    1250             :         {
    1251          16 :             if (EQUAL(psNext->pszValue, "Edge") ||
    1252          16 :                 EQUAL(psNext->pszValue, "directedEdge"))
    1253             :             {
    1254          16 :                 gmlHugeFileCheckXrefs(helper, psNext);
    1255             :             }
    1256             :         }
    1257          16 :         psNext = psNext->psNext;
    1258             :     }
    1259         251 : }
    1260             : 
    1261           2 : static void gmlHugeFileCleanUp(huge_helper *helper)
    1262             : {
    1263             :     // Cleaning up any SQLite handle.
    1264           2 :     if (helper->hNodes != nullptr)
    1265           0 :         sqlite3_finalize(helper->hNodes);
    1266           2 :     if (helper->hEdges != nullptr)
    1267           0 :         sqlite3_finalize(helper->hEdges);
    1268           2 :     if (helper->hDB != nullptr)
    1269           2 :         sqlite3_close(helper->hDB);
    1270           2 :     if (helper->nodeSrs != nullptr)
    1271           0 :         delete helper->nodeSrs;
    1272           2 : }
    1273             : 
    1274         328 : static void gmlHugeFileCheckPendingHrefs(huge_helper *helper,
    1275             :                                          const CPLXMLNode *psParent,
    1276             :                                          const CPLXMLNode *psNode)
    1277             : {
    1278             :     // Identifying any xlink:href to be replaced.
    1279         328 :     if (psNode->eType == CXT_Element)
    1280             :     {
    1281         328 :         if (EQUAL(psNode->pszValue, "directedEdge"))
    1282             :         {
    1283         204 :             char cOrientation = '+';
    1284         204 :             CPLXMLNode *psAttr = psNode->psChild;
    1285         476 :             while (psAttr != nullptr)
    1286             :             {
    1287         272 :                 if (psAttr->eType == CXT_Attribute &&
    1288         191 :                     EQUAL(psAttr->pszValue, "orientation"))
    1289             :                 {
    1290          68 :                     const CPLXMLNode *psOrientation = psAttr->psChild;
    1291          68 :                     if (psOrientation != nullptr)
    1292             :                     {
    1293          68 :                         if (psOrientation->eType == CXT_Text)
    1294          68 :                             cOrientation = *(psOrientation->pszValue);
    1295             :                     }
    1296             :                 }
    1297         272 :                 psAttr = psAttr->psNext;
    1298             :             }
    1299         204 :             psAttr = psNode->psChild;
    1300         476 :             while (psAttr != nullptr)
    1301             :             {
    1302         272 :                 if (psAttr->eType == CXT_Attribute &&
    1303         191 :                     EQUAL(psAttr->pszValue, "xlink:href"))
    1304             :                 {
    1305         123 :                     const CPLXMLNode *pszHref = psAttr->psChild;
    1306         123 :                     if (pszHref != nullptr)
    1307             :                     {
    1308         123 :                         if (pszHref->eType == CXT_Text)
    1309             :                         {
    1310         123 :                             if (pszHref->pszValue[0] != '#')
    1311             :                             {
    1312           0 :                                 CPLError(
    1313             :                                     CE_Warning, CPLE_NotSupported,
    1314             :                                     "Only values of xlink:href element "
    1315             :                                     "starting with '#' are supported, "
    1316             :                                     "so %s will not be properly recognized",
    1317           0 :                                     pszHref->pszValue);
    1318             :                             }
    1319             :                             CPLString *gmlId =
    1320         123 :                                 new CPLString(pszHref->pszValue + 1);
    1321         123 :                             gmlHugeAddPendingToHelper(
    1322             :                                 helper, gmlId, psParent, psNode,
    1323             :                                 // /*bDirectedEdge=*/ true,
    1324             :                                 cOrientation);
    1325             :                         }
    1326             :                     }
    1327             :                 }
    1328         272 :                 psAttr = psAttr->psNext;
    1329             :             }
    1330             :         }
    1331             :     }
    1332             : 
    1333             :     // Recursively scanning each Child GML node.
    1334         328 :     const CPLXMLNode *psChild = psNode->psChild;
    1335         884 :     while (psChild != nullptr)
    1336             :     {
    1337         556 :         if (psChild->eType == CXT_Element)
    1338             :         {
    1339         337 :             if (EQUAL(psChild->pszValue, "directedEdge") ||
    1340         133 :                 EQUAL(psChild->pszValue, "directedFace") ||
    1341         107 :                 EQUAL(psChild->pszValue, "Face"))
    1342             :             {
    1343         256 :                 gmlHugeFileCheckPendingHrefs(helper, psNode, psChild);
    1344             :             }
    1345             :         }
    1346         556 :         psChild = psChild->psNext;
    1347             :     }
    1348             : 
    1349             :     // Recursively scanning each GML of the same level.
    1350         328 :     const CPLXMLNode *psNext = psNode->psNext;
    1351         776 :     while (psNext != nullptr)
    1352             :     {
    1353         448 :         if (psNext->eType == CXT_Element)
    1354             :         {
    1355         448 :             if (EQUAL(psNext->pszValue, "Face"))
    1356             :             {
    1357           0 :                 gmlHugeFileCheckPendingHrefs(helper, psParent, psNext);
    1358             :             }
    1359             :         }
    1360         448 :         psNext = psNext->psNext;
    1361             :     }
    1362         328 : }
    1363             : 
    1364         123 : static void gmlHugeSetHrefGmlText(huge_helper *helper, const char *pszGmlId,
    1365             :                                   const char *pszGmlText)
    1366             : {
    1367             :     // Setting the GML text for the corresponding gml:id.
    1368         123 :     struct huge_href *pItem = helper->pFirstHref;
    1369         697 :     while (pItem != nullptr)
    1370             :     {
    1371         697 :         if (EQUAL(pItem->gmlId->c_str(), pszGmlId))
    1372             :         {
    1373         123 :             if (pItem->gmlText != nullptr)
    1374           0 :                 delete pItem->gmlText;
    1375         123 :             pItem->gmlText = new CPLString(pszGmlText);
    1376         123 :             return;
    1377             :         }
    1378         574 :         pItem = pItem->pNext;
    1379             :     }
    1380             : }
    1381             : 
    1382         123 : static struct huge_parent *gmlHugeFindParent(huge_helper *helper,
    1383             :                                              CPLXMLNode *psParent)
    1384             : {
    1385             :     // Inserting a GML Node (parent) to be rewritten.
    1386         123 :     struct huge_parent *pItem = helper->pFirstParent;
    1387             : 
    1388             :     // Checking if already exists.
    1389         195 :     while (pItem != nullptr)
    1390             :     {
    1391         169 :         if (pItem->psParent == psParent)
    1392          97 :             return pItem;
    1393          72 :         pItem = pItem->pNext;
    1394             :     }
    1395             : 
    1396             :     // Creating a new Parent Node.
    1397          26 :     pItem = new struct huge_parent;
    1398          26 :     pItem->psParent = psParent;
    1399          26 :     pItem->pFirst = nullptr;
    1400          26 :     pItem->pLast = nullptr;
    1401          26 :     pItem->pNext = nullptr;
    1402          26 :     if (helper->pFirstParent == nullptr)
    1403          14 :         helper->pFirstParent = pItem;
    1404          26 :     if (helper->pLastParent != nullptr)
    1405          12 :         helper->pLastParent->pNext = pItem;
    1406          26 :     helper->pLastParent = pItem;
    1407             : 
    1408             :     // Inserting any Child node into the Parent.
    1409          26 :     CPLXMLNode *psChild = psParent->psChild;
    1410         190 :     while (psChild != nullptr)
    1411             :     {
    1412         164 :         struct huge_child *pChildItem = new struct huge_child;
    1413         164 :         pChildItem->psChild = psChild;
    1414         164 :         pChildItem->pItem = nullptr;
    1415         164 :         pChildItem->pNext = nullptr;
    1416         164 :         if (pItem->pFirst == nullptr)
    1417          26 :             pItem->pFirst = pChildItem;
    1418         164 :         if (pItem->pLast != nullptr)
    1419         138 :             pItem->pLast->pNext = pChildItem;
    1420         164 :         pItem->pLast = pChildItem;
    1421         164 :         psChild = psChild->psNext;
    1422             :     }
    1423          26 :     return pItem;
    1424             : }
    1425             : 
    1426         123 : static bool gmlHugeSetChild(struct huge_parent *pParent,
    1427             :                             struct huge_href *pItem)
    1428             : {
    1429             :     // Setting a Child Node to be rewritten.
    1430         123 :     struct huge_child *pChild = pParent->pFirst;
    1431         589 :     while (pChild != nullptr)
    1432             :     {
    1433         589 :         if (pChild->psChild == pItem->psNode)
    1434             :         {
    1435         123 :             pChild->pItem = pItem;
    1436         123 :             return true;
    1437             :         }
    1438         466 :         pChild = pChild->pNext;
    1439             :     }
    1440           0 :     return false;
    1441             : }
    1442             : 
    1443          14 : static bool gmlHugeResolveEdges(CPL_UNUSED huge_helper *helper,
    1444             :                                 CPL_UNUSED CPLXMLNode *psNode, sqlite3 *hDB)
    1445             : {
    1446             :     // Resolving GML <Edge> xlink:href.
    1447          28 :     CPLString osCommand;
    1448          14 :     bool bIsComma = false;
    1449          14 :     bool bError = false;
    1450             : 
    1451             :     // query cursor [Edges] */
    1452             :     osCommand = "SELECT gml_id, gml_resolved "
    1453             :                 "FROM gml_edges "
    1454          14 :                 "WHERE gml_id IN (";
    1455          14 :     struct huge_href *pItem = helper->pFirstHref;
    1456         137 :     while (pItem != nullptr)
    1457             :     {
    1458         123 :         if (bIsComma)
    1459         109 :             osCommand += ", ";
    1460             :         else
    1461          14 :             bIsComma = true;
    1462         123 :         osCommand += "'";
    1463         123 :         osCommand += pItem->gmlId->c_str();
    1464         123 :         osCommand += "'";
    1465         123 :         pItem = pItem->pNext;
    1466             :     }
    1467          14 :     osCommand += ")";
    1468          14 :     sqlite3_stmt *hStmtEdges = nullptr;
    1469             :     {
    1470          14 :         const int rc = sqlite3_prepare_v2(hDB, osCommand.c_str(), -1,
    1471             :                                           &hStmtEdges, nullptr);
    1472          14 :         if (rc != SQLITE_OK)
    1473             :         {
    1474           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1475             :                      "Unable to create QUERY stmt for EDGES");
    1476           0 :             return false;
    1477             :         }
    1478             :     }
    1479             :     while (true)
    1480             :     {
    1481         137 :         const int rc = sqlite3_step(hStmtEdges);
    1482         137 :         if (rc == SQLITE_DONE)
    1483          14 :             break;
    1484         123 :         if (rc == SQLITE_ROW)
    1485             :         {
    1486             :             const char *pszGmlId = reinterpret_cast<const char *>(
    1487         123 :                 sqlite3_column_text(hStmtEdges, 0));
    1488         123 :             if (sqlite3_column_type(hStmtEdges, 1) != SQLITE_NULL)
    1489             :             {
    1490             :                 const char *pszGmlText = reinterpret_cast<const char *>(
    1491         123 :                     sqlite3_column_text(hStmtEdges, 1));
    1492         123 :                 gmlHugeSetHrefGmlText(helper, pszGmlId, pszGmlText);
    1493             :             }
    1494             :         }
    1495             :         else
    1496             :         {
    1497           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1498             :                      "Edge xlink:href QUERY: sqlite3_step(%s)",
    1499             :                      sqlite3_errmsg(hDB));
    1500           0 :             bError = true;
    1501           0 :             break;
    1502             :         }
    1503         123 :     }
    1504          14 :     sqlite3_finalize(hStmtEdges);
    1505          14 :     if (bError)
    1506           0 :         return false;
    1507             : 
    1508             :     // Identifying any GML node to be rewritten.
    1509          14 :     pItem = helper->pFirstHref;
    1510          14 :     struct huge_parent *pParent = nullptr;
    1511         137 :     while (pItem != nullptr)
    1512             :     {
    1513         123 :         if (pItem->gmlText == nullptr || pItem->psParent == nullptr ||
    1514         123 :             pItem->psNode == nullptr)
    1515             :         {
    1516           0 :             bError = true;
    1517           0 :             break;
    1518             :         }
    1519         246 :         pParent = gmlHugeFindParent(helper,
    1520         123 :                                     const_cast<CPLXMLNode *>(pItem->psParent));
    1521         123 :         if (gmlHugeSetChild(pParent, pItem) == false)
    1522             :         {
    1523           0 :             bError = true;
    1524           0 :             break;
    1525             :         }
    1526         123 :         pItem = pItem->pNext;
    1527             :     }
    1528             : 
    1529          14 :     if (bError == false)
    1530             :     {
    1531             :         // Rewriting GML nodes.
    1532          14 :         pParent = helper->pFirstParent;
    1533          40 :         while (pParent != nullptr)
    1534             :         {
    1535             : 
    1536             :             // Removing any Child node from the Parent.
    1537          26 :             struct huge_child *pChild = pParent->pFirst;
    1538         190 :             while (pChild != nullptr)
    1539             :             {
    1540         164 :                 CPLRemoveXMLChild(pParent->psParent, pChild->psChild);
    1541             : 
    1542             :                 // Destroying any Child Node to be rewritten.
    1543         164 :                 if (pChild->pItem != nullptr)
    1544         123 :                     CPLDestroyXMLNode(pChild->psChild);
    1545         164 :                 pChild = pChild->pNext;
    1546             :             }
    1547             : 
    1548             :             // Rewriting the Parent Node.
    1549          26 :             pChild = pParent->pFirst;
    1550         190 :             while (pChild != nullptr)
    1551             :             {
    1552         164 :                 if (pChild->pItem == nullptr)
    1553             :                 {
    1554             :                     // Reinserting any untouched Child Node.
    1555          41 :                     CPLAddXMLChild(pParent->psParent, pChild->psChild);
    1556             :                 }
    1557             :                 else
    1558             :                 {
    1559             :                     // Rewriting a Child Node.
    1560             :                     CPLXMLNode *psNewNode =
    1561         123 :                         CPLCreateXMLNode(nullptr, CXT_Element, "directedEdge");
    1562         123 :                     if (pChild->pItem->cOrientation == '-')
    1563             :                     {
    1564          67 :                         CPLXMLNode *psOrientationNode = CPLCreateXMLNode(
    1565             :                             psNewNode, CXT_Attribute, "orientation");
    1566          67 :                         CPLCreateXMLNode(psOrientationNode, CXT_Text, "-");
    1567             :                     }
    1568             :                     CPLXMLNode *psEdge =
    1569         123 :                         CPLParseXMLString(pChild->pItem->gmlText->c_str());
    1570         123 :                     if (psEdge != nullptr)
    1571         123 :                         CPLAddXMLChild(psNewNode, psEdge);
    1572         123 :                     CPLAddXMLChild(pParent->psParent, psNewNode);
    1573             :                 }
    1574         164 :                 pChild = pChild->pNext;
    1575             :             }
    1576          26 :             pParent = pParent->pNext;
    1577             :         }
    1578             :     }
    1579             : 
    1580             :     // Resetting the Rewrite Helper to an empty state.
    1581          14 :     gmlHugeFileRewiterReset(helper);
    1582             : 
    1583          14 :     return !bError;
    1584             : }
    1585             : 
    1586           2 : static bool gmlHugeFileWriteResolved(huge_helper *helper,
    1587             :                                      const char *pszOutputFilename,
    1588             :                                      GMLReader *pReader,
    1589             :                                      int *m_nHasSequentialLayers)
    1590             : {
    1591             :     // Open the resolved GML file for writing.
    1592           2 :     VSILFILE *fp = VSIFOpenL(pszOutputFilename, "w");
    1593           2 :     if (fp == nullptr)
    1594             :     {
    1595           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open %.500s to write.",
    1596             :                  pszOutputFilename);
    1597           0 :         return false;
    1598             :     }
    1599             : 
    1600           2 :     sqlite3 *hDB = helper->hDB;
    1601             : 
    1602             :     // Query cursor [Nodes].
    1603           2 :     const char *osCommand = "SELECT gml_id, x, y, z FROM nodes";
    1604           2 :     sqlite3_stmt *hStmtNodes = nullptr;
    1605             :     const int rc1 =
    1606           2 :         sqlite3_prepare_v2(hDB, osCommand, -1, &hStmtNodes, nullptr);
    1607           2 :     if (rc1 != SQLITE_OK)
    1608             :     {
    1609           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1610             :                  "Unable to create QUERY stmt for NODES");
    1611           0 :         VSIFCloseL(fp);
    1612           0 :         return false;
    1613             :     }
    1614             : 
    1615           2 :     VSIFPrintfL(fp, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
    1616           2 :     VSIFPrintfL(fp, "<ResolvedTopoFeatureCollection  "
    1617             :                     "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
    1618             :                     "xmlns:gml=\"http://www.opengis.net/gml\">\n");
    1619           2 :     VSIFPrintfL(fp, "  <ResolvedTopoFeatureMembers>\n");
    1620             : 
    1621           2 :     int iOutCount = 0;
    1622             : 
    1623             :     // Exporting Nodes.
    1624           2 :     GFSTemplateList *pCC = new GFSTemplateList();
    1625             :     while (true)
    1626             :     {
    1627          68 :         const int rc = sqlite3_step(hStmtNodes);
    1628          68 :         if (rc == SQLITE_DONE)
    1629           2 :             break;
    1630             : 
    1631          66 :         if (rc == SQLITE_ROW)
    1632             :         {
    1633          66 :             bool bHasZ = false;
    1634             :             const char *pszGmlId = reinterpret_cast<const char *>(
    1635          66 :                 sqlite3_column_text(hStmtNodes, 0));
    1636          66 :             const double x = sqlite3_column_double(hStmtNodes, 1);
    1637          66 :             const double y = sqlite3_column_double(hStmtNodes, 2);
    1638          66 :             double z = 0.0;
    1639          66 :             if (sqlite3_column_type(hStmtNodes, 3) == SQLITE_FLOAT)
    1640             :             {
    1641           0 :                 z = sqlite3_column_double(hStmtNodes, 3);
    1642           0 :                 bHasZ = true;
    1643             :             }
    1644             : 
    1645             :             // Inserting a node into the resolved GML file.
    1646          66 :             pCC->Update("ResolvedNodes", true);
    1647          66 :             VSIFPrintfL(fp, "    <ResolvedNodes>\n");
    1648          66 :             char *pszEscaped = CPLEscapeString(pszGmlId, -1, CPLES_XML);
    1649          66 :             VSIFPrintfL(fp, "      <NodeGmlId>%s</NodeGmlId>\n", pszEscaped);
    1650          66 :             CPLFree(pszEscaped);
    1651          66 :             VSIFPrintfL(fp, "      <ResolvedGeometry> \n");
    1652          66 :             if (helper->nodeSrs == nullptr)
    1653             :             {
    1654          66 :                 VSIFPrintfL(fp, "        <gml:Point srsDimension=\"%d\">",
    1655             :                             bHasZ ? 3 : 2);
    1656             :             }
    1657             :             else
    1658             :             {
    1659             :                 pszEscaped =
    1660           0 :                     CPLEscapeString(helper->nodeSrs->c_str(), -1, CPLES_XML);
    1661           0 :                 VSIFPrintfL(fp,
    1662             :                             "        <gml:Point srsDimension=\"%d\""
    1663             :                             " srsName=\"%s\">",
    1664             :                             bHasZ ? 3 : 2, pszEscaped);
    1665           0 :                 CPLFree(pszEscaped);
    1666             :             }
    1667          66 :             if (bHasZ)
    1668           0 :                 VSIFPrintfL(fp,
    1669             :                             "<gml:pos>%1.8f %1.8f %1.8f</gml:pos>"
    1670             :                             "</gml:Point>\n",
    1671             :                             x, y, z);
    1672             :             else
    1673          66 :                 VSIFPrintfL(fp,
    1674             :                             "<gml:pos>%1.8f %1.8f</gml:pos>"
    1675             :                             "</gml:Point>\n",
    1676             :                             x, y);
    1677          66 :             VSIFPrintfL(fp, "      </ResolvedGeometry> \n");
    1678          66 :             VSIFPrintfL(fp, "    </ResolvedNodes>\n");
    1679          66 :             iOutCount++;
    1680             :         }
    1681             :         else
    1682             :         {
    1683           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1684             :                      "ResolvedNodes QUERY: sqlite3_step(%s)",
    1685             :                      sqlite3_errmsg(hDB));
    1686           0 :             sqlite3_finalize(hStmtNodes);
    1687           0 :             delete pCC;
    1688           0 :             return false;
    1689             :         }
    1690          66 :     }
    1691           2 :     sqlite3_finalize(hStmtNodes);
    1692             : 
    1693             :     // Processing GML features.
    1694           2 :     GMLFeature *poFeature = nullptr;
    1695           2 :     bool bError = false;
    1696             : 
    1697          74 :     while ((poFeature = pReader->NextFeature()) != nullptr)
    1698             :     {
    1699          72 :         GMLFeatureClass *poClass = poFeature->GetClass();
    1700          72 :         const CPLXMLNode *const *papsGeomList = poFeature->GetGeometryList();
    1701          72 :         const int iPropCount = poClass->GetPropertyCount();
    1702             : 
    1703          72 :         bool b_has_geom = false;
    1704          72 :         VSIFPrintfL(fp, "    <%s>\n", poClass->GetElementName());
    1705             : 
    1706         252 :         for (int iProp = 0; iProp < iPropCount; iProp++)
    1707             :         {
    1708         180 :             GMLPropertyDefn *poPropDefn = poClass->GetProperty(iProp);
    1709         180 :             const char *pszPropName = poPropDefn->GetName();
    1710         180 :             const GMLProperty *poProp = poFeature->GetProperty(iProp);
    1711             : 
    1712         180 :             if (poProp != nullptr)
    1713             :             {
    1714         360 :                 for (int iSub = 0; iSub < poProp->nSubProperties; iSub++)
    1715             :                 {
    1716         360 :                     char *gmlText = CPLEscapeString(
    1717         180 :                         poProp->papszSubProperties[iSub], -1, CPLES_XML);
    1718         180 :                     VSIFPrintfL(fp, "      <%s>%s</%s>\n", pszPropName, gmlText,
    1719             :                                 pszPropName);
    1720         180 :                     CPLFree(gmlText);
    1721             :                 }
    1722             :             }
    1723             :         }
    1724             : 
    1725          72 :         if (papsGeomList != nullptr)
    1726             :         {
    1727          72 :             int i = 0;
    1728          72 :             const CPLXMLNode *psNode = papsGeomList[i];
    1729         144 :             while (psNode != nullptr)
    1730             :             {
    1731          72 :                 char *pszResolved = nullptr;
    1732          72 :                 bool bNotToBeResolved = false;
    1733          72 :                 if (psNode->eType != CXT_Element)
    1734             :                 {
    1735           0 :                     bNotToBeResolved = true;
    1736             :                 }
    1737             :                 else
    1738             :                 {
    1739          72 :                     bNotToBeResolved =
    1740          86 :                         !(EQUAL(psNode->pszValue, "TopoCurve") ||
    1741          14 :                           EQUAL(psNode->pszValue, "TopoSurface"));
    1742             :                 }
    1743          72 :                 if (bNotToBeResolved)
    1744             :                 {
    1745           0 :                     VSIFPrintfL(fp, "      <ResolvedGeometry> \n");
    1746           0 :                     pszResolved = CPLSerializeXMLTree((CPLXMLNode *)psNode);
    1747           0 :                     VSIFPrintfL(fp, "        %s\n", pszResolved);
    1748           0 :                     CPLFree(pszResolved);
    1749           0 :                     VSIFPrintfL(fp, "      </ResolvedGeometry>\n");
    1750           0 :                     b_has_geom = true;
    1751             :                 }
    1752             :                 else
    1753             :                 {
    1754          72 :                     gmlHugeFileCheckPendingHrefs(helper, psNode, psNode);
    1755          72 :                     if (helper->pFirstHref == nullptr)
    1756             :                     {
    1757          58 :                         VSIFPrintfL(fp, "      <ResolvedGeometry> \n");
    1758          58 :                         pszResolved = CPLSerializeXMLTree(psNode);
    1759          58 :                         VSIFPrintfL(fp, "        %s\n", pszResolved);
    1760          58 :                         CPLFree(pszResolved);
    1761          58 :                         VSIFPrintfL(fp, "      </ResolvedGeometry>\n");
    1762          58 :                         b_has_geom = true;
    1763             :                     }
    1764             :                     else
    1765             :                     {
    1766          14 :                         if (gmlHugeResolveEdges(
    1767             :                                 helper, const_cast<CPLXMLNode *>(psNode),
    1768          14 :                                 hDB) == false)
    1769           0 :                             bError = true;
    1770          14 :                         if (gmlHugeFileHrefCheck(helper) == false)
    1771           0 :                             bError = true;
    1772          14 :                         VSIFPrintfL(fp, "      <ResolvedGeometry> \n");
    1773          14 :                         pszResolved = CPLSerializeXMLTree(psNode);
    1774          14 :                         VSIFPrintfL(fp, "        %s\n", pszResolved);
    1775          14 :                         CPLFree(pszResolved);
    1776          14 :                         VSIFPrintfL(fp, "      </ResolvedGeometry>\n");
    1777          14 :                         b_has_geom = true;
    1778          14 :                         gmlHugeFileHrefReset(helper);
    1779             :                     }
    1780             :                 }
    1781          72 :                 i++;
    1782          72 :                 psNode = papsGeomList[i];
    1783             :             }
    1784             :         }
    1785          72 :         pCC->Update(poClass->GetElementName(), b_has_geom);
    1786             : 
    1787          72 :         VSIFPrintfL(fp, "    </%s>\n", poClass->GetElementName());
    1788             : 
    1789          72 :         delete poFeature;
    1790          72 :         iOutCount++;
    1791             :     }
    1792             : 
    1793           2 :     VSIFPrintfL(fp, "  </ResolvedTopoFeatureMembers>\n");
    1794           2 :     VSIFPrintfL(fp, "</ResolvedTopoFeatureCollection>\n");
    1795             : 
    1796           2 :     VSIFCloseL(fp);
    1797             : 
    1798           2 :     gmlUpdateFeatureClasses(pCC, pReader, m_nHasSequentialLayers);
    1799           2 :     if (*m_nHasSequentialLayers)
    1800           2 :         pReader->ReArrangeTemplateClasses(pCC);
    1801           2 :     delete pCC;
    1802             : 
    1803           2 :     return !(bError || iOutCount == 0);
    1804             : }
    1805             : 
    1806             : /**************************************************************/
    1807             : /*                                                            */
    1808             : /* private member(s):                                         */
    1809             : /* any other function is implemented as "internal" static,    */
    1810             : /* so to make all the SQLite own stuff nicely "invisible"     */
    1811             : /*                                                            */
    1812             : /**************************************************************/
    1813             : 
    1814           2 : bool GMLReader::ParseXMLHugeFile(const char *pszOutputFilename,
    1815             :                                  const bool bSqliteIsTempFile,
    1816             :                                  const int iSqliteCacheMB)
    1817             : 
    1818             : {
    1819             :     /* -------------------------------------------------------------------- */
    1820             :     /*      Creating/Opening the SQLite DB file                             */
    1821             :     /* -------------------------------------------------------------------- */
    1822             :     const CPLString osSQLiteFilename =
    1823           4 :         CPLResetExtension(m_pszFilename, "sqlite");
    1824           2 :     const char *pszSQLiteFilename = osSQLiteFilename.c_str();
    1825             : 
    1826             :     VSIStatBufL statBufL;
    1827           2 :     if (VSIStatExL(pszSQLiteFilename, &statBufL, VSI_STAT_EXISTS_FLAG) == 0)
    1828             :     {
    1829           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1830             :                  "sqlite3_open(%s) failed: DB-file already exists",
    1831             :                  pszSQLiteFilename);
    1832           0 :         return false;
    1833             :     }
    1834             : 
    1835           2 :     sqlite3 *hDB = nullptr;
    1836             :     {
    1837           2 :         const int rc = sqlite3_open(pszSQLiteFilename, &hDB);
    1838           2 :         if (rc != SQLITE_OK)
    1839             :         {
    1840           0 :             CPLError(CE_Failure, CPLE_OpenFailed, "sqlite3_open(%s) failed: %s",
    1841             :                      pszSQLiteFilename, sqlite3_errmsg(hDB));
    1842           0 :             sqlite3_close(hDB);
    1843           0 :             return false;
    1844             :         }
    1845             :     }
    1846             : 
    1847           2 :     huge_helper helper;
    1848           2 :     helper.hDB = hDB;
    1849             : 
    1850           2 :     char *pszErrMsg = nullptr;
    1851             : 
    1852             :     // Setting SQLite for max speed; this is intrinsically unsafe.
    1853             :     // The DB file could be potentially damaged.
    1854             :     // But, this is a temporary file, so there is no real risk.
    1855             :     {
    1856           2 :         const int rc = sqlite3_exec(hDB, "PRAGMA synchronous = OFF", nullptr,
    1857             :                                     nullptr, &pszErrMsg);
    1858           2 :         if (rc != SQLITE_OK)
    1859             :         {
    1860           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1861             :                      "Unable to set PRAGMA synchronous = OFF: %s", pszErrMsg);
    1862           0 :             sqlite3_free(pszErrMsg);
    1863             :         }
    1864             :     }
    1865             :     {
    1866           2 :         const int rc = sqlite3_exec(hDB, "PRAGMA journal_mode = OFF", nullptr,
    1867             :                                     nullptr, &pszErrMsg);
    1868           2 :         if (rc != SQLITE_OK)
    1869             :         {
    1870           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1871             :                      "Unable to set PRAGMA journal_mode = OFF: %s", pszErrMsg);
    1872           0 :             sqlite3_free(pszErrMsg);
    1873             :         }
    1874             :     }
    1875             :     {
    1876           2 :         const int rc = sqlite3_exec(hDB, "PRAGMA locking_mode = EXCLUSIVE",
    1877             :                                     nullptr, nullptr, &pszErrMsg);
    1878           2 :         if (rc != SQLITE_OK)
    1879             :         {
    1880           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1881             :                      "Unable to set PRAGMA locking_mode = EXCLUSIVE: %s",
    1882             :                      pszErrMsg);
    1883           0 :             sqlite3_free(pszErrMsg);
    1884             :         }
    1885             :     }
    1886             : 
    1887             :     // Setting the SQLite cache.
    1888           2 :     if (iSqliteCacheMB > 0)
    1889             :     {
    1890             :         // Refuse to allocate more than 1GB.
    1891           0 :         const int cache_size = std::min(iSqliteCacheMB * 1024, 1024 * 1024);
    1892             : 
    1893           0 :         char sqlPragma[64] = {};
    1894           0 :         snprintf(sqlPragma, sizeof(sqlPragma), "PRAGMA cache_size = %d",
    1895             :                  cache_size);
    1896             :         const int rc =
    1897           0 :             sqlite3_exec(hDB, sqlPragma, nullptr, nullptr, &pszErrMsg);
    1898           0 :         if (rc != SQLITE_OK)
    1899             :         {
    1900           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Unable to set %s: %s",
    1901             :                      sqlPragma, pszErrMsg);
    1902           0 :             sqlite3_free(pszErrMsg);
    1903             :         }
    1904             :     }
    1905             : 
    1906           2 :     if (!SetupParser())
    1907             :     {
    1908           0 :         gmlHugeFileCleanUp(&helper);
    1909           0 :         return false;
    1910             :     }
    1911             : 
    1912             :     // Creating SQLite tables and Insert cursors.
    1913           2 :     if (gmlHugeFileSQLiteInit(&helper) == false)
    1914             :     {
    1915           0 :         gmlHugeFileCleanUp(&helper);
    1916           0 :         return false;
    1917             :     }
    1918             : 
    1919             :     // Processing GML features.
    1920           2 :     GMLFeature *poFeature = nullptr;
    1921          74 :     while ((poFeature = NextFeature()) != nullptr)
    1922             :     {
    1923          72 :         const CPLXMLNode *const *papsGeomList = poFeature->GetGeometryList();
    1924          72 :         if (papsGeomList != nullptr)
    1925             :         {
    1926          72 :             int i = 0;
    1927          72 :             const CPLXMLNode *psNode = papsGeomList[i];
    1928         144 :             while (psNode)
    1929             :             {
    1930          72 :                 gmlHugeFileCheckXrefs(&helper, psNode);
    1931             :                 // Inserting into the SQLite DB any appropriate row.
    1932          72 :                 gmlHugeFileSQLiteInsert(&helper);
    1933             :                 // Resetting an empty helper struct.
    1934          72 :                 gmlHugeFileReset(&helper);
    1935          72 :                 i++;
    1936          72 :                 psNode = papsGeomList[i];
    1937             :             }
    1938             :         }
    1939          72 :         delete poFeature;
    1940             :     }
    1941             : 
    1942             :     // Finalizing any SQLite Insert cursor.
    1943           2 :     if (helper.hNodes != nullptr)
    1944           2 :         sqlite3_finalize(helper.hNodes);
    1945           2 :     helper.hNodes = nullptr;
    1946           2 :     if (helper.hEdges != nullptr)
    1947           2 :         sqlite3_finalize(helper.hEdges);
    1948           2 :     helper.hEdges = nullptr;
    1949             : 
    1950             :     // Confirming the still pending TRANSACTION.
    1951           2 :     const int rc = sqlite3_exec(hDB, "COMMIT", nullptr, nullptr, &pszErrMsg);
    1952           2 :     if (rc != SQLITE_OK)
    1953             :     {
    1954           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1955             :                  "Unable to perform COMMIT TRANSACTION: %s", pszErrMsg);
    1956           0 :         sqlite3_free(pszErrMsg);
    1957           0 :         return false;
    1958             :     }
    1959             : 
    1960             :     // Attempting to resolve GML strings.
    1961           2 :     if (gmlHugeFileResolveEdges(&helper) == false)
    1962             :     {
    1963           0 :         gmlHugeFileCleanUp(&helper);
    1964           0 :         return false;
    1965             :     }
    1966             : 
    1967             :     // Restarting the GML parser.
    1968           2 :     if (!SetupParser())
    1969             :     {
    1970           0 :         gmlHugeFileCleanUp(&helper);
    1971           0 :         return false;
    1972             :     }
    1973             : 
    1974             :     // Output: writing the revolved GML file.
    1975           2 :     if (gmlHugeFileWriteResolved(&helper, pszOutputFilename, this,
    1976           2 :                                  &m_nHasSequentialLayers) == false)
    1977             :     {
    1978           0 :         gmlHugeFileCleanUp(&helper);
    1979           0 :         return false;
    1980             :     }
    1981             : 
    1982           2 :     gmlHugeFileCleanUp(&helper);
    1983           2 :     if (bSqliteIsTempFile)
    1984           2 :         VSIUnlink(pszSQLiteFilename);
    1985           2 :     return true;
    1986             : }
    1987             : 
    1988             : /**************************************************************/
    1989             : /*                                                            */
    1990             : /* an alternative <xlink:href> resolver based on SQLite       */
    1991             : /*                                                            */
    1992             : /**************************************************************/
    1993           2 : bool GMLReader::HugeFileResolver(const char *pszFile, bool bSqliteIsTempFile,
    1994             :                                  int iSqliteCacheMB)
    1995             : 
    1996             : {
    1997             :     // Check if the original source file is set.
    1998           2 :     if (m_pszFilename == nullptr)
    1999             :     {
    2000           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2001             :                  "GML source file needs to be set first with "
    2002             :                  "GMLReader::SetSourceFile().");
    2003           0 :         return false;
    2004             :     }
    2005           2 :     if (ParseXMLHugeFile(pszFile, bSqliteIsTempFile, iSqliteCacheMB) == false)
    2006           0 :         return false;
    2007             : 
    2008             :     // Set the source file to the resolved file.
    2009           2 :     CleanupParser();
    2010           2 :     if (fpGML)
    2011           2 :         VSIFCloseL(fpGML);
    2012           2 :     fpGML = nullptr;
    2013           2 :     CPLFree(m_pszFilename);
    2014           2 :     m_pszFilename = CPLStrdup(pszFile);
    2015           2 :     return true;
    2016             : }
    2017             : 
    2018             : #else  // HAVE_SQLITE
    2019             : 
    2020             : /**************************************************/
    2021             : /*    if SQLite support isn't available we'll     */
    2022             : /*    simply output an error message              */
    2023             : /**************************************************/
    2024             : 
    2025             : bool GMLReader::HugeFileResolver(CPL_UNUSED const char *pszFile,
    2026             :                                  CPL_UNUSED bool bSqliteIsTempFile,
    2027             :                                  CPL_UNUSED int iSqliteCacheMB)
    2028             : 
    2029             : {
    2030             :     CPLError(CE_Failure, CPLE_NotSupported,
    2031             :              "OGR was built without SQLite3 support. "
    2032             :              "Sorry, the HUGE GML resolver is unsupported.");
    2033             :     return false;
    2034             : }
    2035             : 
    2036             : bool GMLReader::ParseXMLHugeFile(CPL_UNUSED const char *pszOutputFilename,
    2037             :                                  CPL_UNUSED const bool bSqliteIsTempFile,
    2038             :                                  CPL_UNUSED const int iSqliteCacheMB)
    2039             : {
    2040             :     CPLError(CE_Failure, CPLE_NotSupported,
    2041             :              "OGR was built without SQLite3 support. "
    2042             :              "Sorry, the HUGE GML resolver is unsupported.");
    2043             :     return false;
    2044             : }
    2045             : 
    2046             : #endif  // HAVE_SQLITE

Generated by: LCOV version 1.14