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

Generated by: LCOV version 1.14