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

Generated by: LCOV version 1.14