LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/vrt - ogrvrtlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1099 1191 92.3 %
Date: 2026-04-19 18:43:50 Functions: 33 33 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements OGRVRTLayer class.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2003, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2009-2014, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "ogr_vrt.h"
      16             : 
      17             : #include <cassert>
      18             : #include <cmath>
      19             : #include <cstddef>
      20             : #include <cstdio>
      21             : #include <cstdlib>
      22             : #include <cstring>
      23             : #include <limits>
      24             : #include <string>
      25             : #include <vector>
      26             : 
      27             : #include "cpl_conv.h"
      28             : #include "cpl_error.h"
      29             : #include "cpl_minixml.h"
      30             : #include "cpl_string.h"
      31             : #include "gdal.h"
      32             : #include "gdal_priv.h"
      33             : #include "ogr_api.h"
      34             : #include "ogr_core.h"
      35             : #include "ogr_feature.h"
      36             : #include "ogr_geometry.h"
      37             : #include "ogr_spatialref.h"
      38             : #include "ogrpgeogeometry.h"
      39             : #include "ogrsf_frmts.h"
      40             : #include "ogrvrtgeometrytypes.h"
      41             : #include "memdataset.h"
      42             : 
      43             : #define UNSUPPORTED_OP_READ_ONLY                                               \
      44             :     "%s : unsupported operation on a read-only datasource."
      45             : 
      46             : /************************************************************************/
      47             : /*                  GetFieldIndexCaseSensitiveFirst()                   */
      48             : /************************************************************************/
      49             : 
      50        1653 : static int GetFieldIndexCaseSensitiveFirst(const OGRFeatureDefn *poFDefn,
      51             :                                            const char *pszFieldName)
      52             : {
      53        1653 :     int idx = poFDefn->GetFieldIndexCaseSensitive(pszFieldName);
      54        1653 :     if (idx < 0)
      55         470 :         idx = poFDefn->GetFieldIndex(pszFieldName);
      56        1653 :     return idx;
      57             : }
      58             : 
      59             : /************************************************************************/
      60             : /*                        OGRVRTGeomFieldProps()                        */
      61             : /************************************************************************/
      62             : 
      63             : OGRVRTGeomFieldProps::OGRVRTGeomFieldProps() = default;
      64             : 
      65             : /************************************************************************/
      66             : /*                       ~OGRVRTGeomFieldProps()                        */
      67             : /************************************************************************/
      68             : 
      69             : OGRVRTGeomFieldProps::~OGRVRTGeomFieldProps() = default;
      70             : 
      71             : /************************************************************************/
      72             : /*                            OGRVRTLayer()                             */
      73             : /************************************************************************/
      74             : 
      75         911 : OGRVRTLayer::OGRVRTLayer(OGRVRTDataSource *poDSIn) : poDS(poDSIn)
      76             : {
      77         911 : }
      78             : 
      79             : /************************************************************************/
      80             : /*                            ~OGRVRTLayer()                            */
      81             : /************************************************************************/
      82             : 
      83        1822 : OGRVRTLayer::~OGRVRTLayer()
      84             : 
      85             : {
      86         911 :     if (m_nFeaturesRead > 0 && poFeatureDefn != nullptr)
      87             :     {
      88         242 :         CPLDebug("VRT", "%d features read on layer '%s'.",
      89         121 :                  static_cast<int>(m_nFeaturesRead), poFeatureDefn->GetName());
      90             :     }
      91             : 
      92         911 :     if (poSrcDS != nullptr)
      93             :     {
      94         444 :         if (poSrcLayer)
      95             :         {
      96         441 :             poSrcLayer->SetIgnoredFields(nullptr);
      97         441 :             poSrcLayer->SetAttributeFilter(nullptr);
      98         441 :             poSrcLayer->SetSpatialFilter(nullptr);
      99             :         }
     100             : 
     101         444 :         if (bSrcLayerFromSQL && poSrcLayer)
     102           2 :             poSrcDS->ReleaseResultSet(poSrcLayer);
     103             :     }
     104             : 
     105         911 :     CPLFree(pszAttrFilter);
     106        1822 : }
     107             : 
     108             : /************************************************************************/
     109             : /*                          GetSrcLayerDefn()                           */
     110             : /************************************************************************/
     111             : 
     112       40535 : OGRFeatureDefn *OGRVRTLayer::GetSrcLayerDefn()
     113             : {
     114       40535 :     if (poSrcFeatureDefn)
     115       40095 :         return poSrcFeatureDefn;
     116             : 
     117         440 :     if (poSrcLayer)
     118         440 :         poSrcFeatureDefn = poSrcLayer->GetLayerDefn();
     119             : 
     120         440 :     return poSrcFeatureDefn;
     121             : }
     122             : 
     123             : /************************************************************************/
     124             : /*                           FastInitialize()                           */
     125             : /************************************************************************/
     126             : 
     127         911 : bool OGRVRTLayer::FastInitialize(CPLXMLNode *psLTreeIn,
     128             :                                  const char *pszVRTDirectory, int bUpdateIn)
     129             : 
     130             : {
     131         911 :     psLTree = psLTreeIn;
     132         911 :     bUpdate = CPL_TO_BOOL(bUpdateIn);
     133         911 :     osVRTDirectory = pszVRTDirectory;
     134             : 
     135         911 :     if (!EQUAL(psLTree->pszValue, "OGRVRTLayer"))
     136           0 :         return FALSE;
     137             : 
     138             :     // Get layer name.
     139         911 :     const char *pszLayerName = CPLGetXMLValue(psLTree, "name", nullptr);
     140             : 
     141         911 :     if (pszLayerName == nullptr)
     142             :     {
     143           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     144             :                  "Missing name attribute on OGRVRTLayer");
     145           1 :         return FALSE;
     146             :     }
     147             : 
     148         910 :     osName = pszLayerName;
     149         910 :     SetDescription(pszLayerName);
     150             : 
     151             :     // Do we have a fixed geometry type?  If so, use it.
     152         910 :     CPLXMLNode *psGeometryFieldNode = CPLGetXMLNode(psLTree, "GeometryField");
     153         910 :     const char *pszGType = CPLGetXMLValue(psLTree, "GeometryType", nullptr);
     154         910 :     if (pszGType == nullptr && psGeometryFieldNode != nullptr)
     155          31 :         pszGType = CPLGetXMLValue(psGeometryFieldNode, "GeometryType", nullptr);
     156         910 :     if (pszGType != nullptr)
     157             :     {
     158         359 :         int l_bError = FALSE;
     159             :         const OGRwkbGeometryType eGeomType =
     160         359 :             OGRVRTGetGeometryType(pszGType, &l_bError);
     161         359 :         if (l_bError)
     162             :         {
     163           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     164             :                      "GeometryType %s not recognised.", pszGType);
     165           1 :             return FALSE;
     166             :         }
     167         358 :         if (eGeomType != wkbNone)
     168             :         {
     169         351 :             apoGeomFieldProps.push_back(
     170         702 :                 std::make_unique<OGRVRTGeomFieldProps>());
     171         351 :             apoGeomFieldProps[0]->eGeomType = eGeomType;
     172             :         }
     173             :     }
     174             : 
     175             :     // Apply a spatial reference system if provided.
     176         909 :     const char *pszLayerSRS = CPLGetXMLValue(psLTree, "LayerSRS", nullptr);
     177         909 :     if (pszLayerSRS == nullptr && psGeometryFieldNode != nullptr)
     178         142 :         pszLayerSRS = CPLGetXMLValue(psGeometryFieldNode, "SRS", nullptr);
     179         909 :     if (pszLayerSRS != nullptr)
     180             :     {
     181         184 :         if (apoGeomFieldProps.empty())
     182             :         {
     183           3 :             apoGeomFieldProps.push_back(
     184           6 :                 std::make_unique<OGRVRTGeomFieldProps>());
     185             :         }
     186         184 :         if (!(EQUAL(pszLayerSRS, "NULL")))
     187             :         {
     188         184 :             OGRSpatialReference oSRS;
     189         184 :             oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     190             : 
     191         184 :             if (oSRS.SetFromUserInput(
     192             :                     pszLayerSRS,
     193             :                     OGRSpatialReference::
     194         184 :                         SET_FROM_USER_INPUT_LIMITATIONS_get()) != OGRERR_NONE)
     195             :             {
     196           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     197             :                          "Failed to import LayerSRS `%s'.", pszLayerSRS);
     198           1 :                 return FALSE;
     199             :             }
     200         183 :             apoGeomFieldProps[0]->poSRS =
     201         366 :                 OGRSpatialReferenceRefCountedPtr::makeClone(&oSRS);
     202             :         }
     203             :     }
     204             : 
     205             :     // Set FeatureCount if provided.
     206             :     const char *pszFeatureCount =
     207         908 :         CPLGetXMLValue(psLTree, "FeatureCount", nullptr);
     208         908 :     if (pszFeatureCount != nullptr)
     209             :     {
     210           1 :         nFeatureCount = CPLAtoGIntBig(pszFeatureCount);
     211             :     }
     212             : 
     213             :     // Set Extent if provided.
     214         908 :     const char *pszExtentXMin = CPLGetXMLValue(psLTree, "ExtentXMin", nullptr);
     215         908 :     const char *pszExtentYMin = CPLGetXMLValue(psLTree, "ExtentYMin", nullptr);
     216         908 :     const char *pszExtentXMax = CPLGetXMLValue(psLTree, "ExtentXMax", nullptr);
     217         908 :     const char *pszExtentYMax = CPLGetXMLValue(psLTree, "ExtentYMax", nullptr);
     218         908 :     if (pszExtentXMin == nullptr && psGeometryFieldNode != nullptr)
     219             :     {
     220             :         pszExtentXMin =
     221         312 :             CPLGetXMLValue(psGeometryFieldNode, "ExtentXMin", nullptr);
     222             :         pszExtentYMin =
     223         312 :             CPLGetXMLValue(psGeometryFieldNode, "ExtentYMin", nullptr);
     224             :         pszExtentXMax =
     225         312 :             CPLGetXMLValue(psGeometryFieldNode, "ExtentXMax", nullptr);
     226             :         pszExtentYMax =
     227         312 :             CPLGetXMLValue(psGeometryFieldNode, "ExtentYMax", nullptr);
     228             :     }
     229         908 :     if (pszExtentXMin != nullptr && pszExtentYMin != nullptr &&
     230           8 :         pszExtentXMax != nullptr && pszExtentYMax != nullptr)
     231             :     {
     232           8 :         if (apoGeomFieldProps.empty())
     233             :         {
     234           0 :             apoGeomFieldProps.push_back(
     235           0 :                 std::make_unique<OGRVRTGeomFieldProps>());
     236           0 :             assert(!apoGeomFieldProps.empty());
     237             :         }
     238           8 :         apoGeomFieldProps[0]->sStaticEnvelope.MinX = CPLAtof(pszExtentXMin);
     239           8 :         apoGeomFieldProps[0]->sStaticEnvelope.MinY = CPLAtof(pszExtentYMin);
     240           8 :         apoGeomFieldProps[0]->sStaticEnvelope.MaxX = CPLAtof(pszExtentXMax);
     241           8 :         apoGeomFieldProps[0]->sStaticEnvelope.MaxY = CPLAtof(pszExtentYMax);
     242             :     }
     243             : 
     244         908 :     return TRUE;
     245             : }
     246             : 
     247             : /************************************************************************/
     248             : /*                         ParseGeometryField()                         */
     249             : /************************************************************************/
     250             : 
     251         145 : bool OGRVRTLayer::ParseGeometryField(CPLXMLNode *psNode,
     252             :                                      CPLXMLNode *psNodeParent,
     253             :                                      OGRVRTGeomFieldProps *poProps)
     254             : {
     255         145 :     const char *pszName = CPLGetXMLValue(psNode, "name", nullptr);
     256         145 :     poProps->osName = pszName ? pszName : "";
     257         145 :     if (pszName == nullptr && apoGeomFieldProps.size() > 1 &&
     258           0 :         poProps != apoGeomFieldProps[0].get())
     259             :     {
     260           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     261             :                  "A 'name' attribute should be defined when there are "
     262             :                  "several geometry fields");
     263             :     }
     264             : 
     265             :     // Do we have a fixed geometry type?
     266         145 :     const char *pszGType = CPLGetXMLValue(psNode, "GeometryType", nullptr);
     267         145 :     if (pszGType == nullptr && poProps == apoGeomFieldProps[0].get())
     268         132 :         pszGType = CPLGetXMLValue(psNodeParent, "GeometryType", nullptr);
     269         145 :     if (pszGType != nullptr)
     270             :     {
     271         116 :         int l_bError = FALSE;
     272         116 :         poProps->eGeomType = OGRVRTGetGeometryType(pszGType, &l_bError);
     273         116 :         if (l_bError)
     274             :         {
     275           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     276             :                      "GeometryType %s not recognised.", pszGType);
     277           0 :             return false;
     278             :         }
     279             :     }
     280             : 
     281             :     // Determine which field(s) to get the geometry from.
     282         145 :     const char *pszEncoding = CPLGetXMLValue(psNode, "encoding", "direct");
     283             : 
     284         145 :     if (EQUAL(pszEncoding, "Direct"))
     285          28 :         poProps->eGeometryStyle = VGS_Direct;
     286         117 :     else if (EQUAL(pszEncoding, "None"))
     287           0 :         poProps->eGeometryStyle = VGS_None;
     288         117 :     else if (EQUAL(pszEncoding, "WKT"))
     289          52 :         poProps->eGeometryStyle = VGS_WKT;
     290          65 :     else if (EQUAL(pszEncoding, "WKB"))
     291           1 :         poProps->eGeometryStyle = VGS_WKB;
     292          64 :     else if (EQUAL(pszEncoding, "Shape"))
     293           1 :         poProps->eGeometryStyle = VGS_Shape;
     294          63 :     else if (EQUAL(pszEncoding, "PointFromColumns"))
     295             :     {
     296          62 :         poProps->eGeometryStyle = VGS_PointFromColumns;
     297          62 :         poProps->bUseSpatialSubquery = CPLTestBool(
     298             :             CPLGetXMLValue(psNode, "GeometryField.useSpatialSubquery", "TRUE"));
     299             : 
     300          62 :         poProps->iGeomXField = GetFieldIndexCaseSensitiveFirst(
     301          62 :             GetSrcLayerDefn(), CPLGetXMLValue(psNode, "x", "missing"));
     302          62 :         poProps->iGeomYField = GetFieldIndexCaseSensitiveFirst(
     303          62 :             GetSrcLayerDefn(), CPLGetXMLValue(psNode, "y", "missing"));
     304          62 :         poProps->iGeomZField = GetFieldIndexCaseSensitiveFirst(
     305          62 :             GetSrcLayerDefn(), CPLGetXMLValue(psNode, "z", "missing"));
     306          62 :         poProps->iGeomMField = GetFieldIndexCaseSensitiveFirst(
     307          62 :             GetSrcLayerDefn(), CPLGetXMLValue(psNode, "m", "missing"));
     308             : 
     309          62 :         if (poProps->iGeomXField == -1 || poProps->iGeomYField == -1)
     310             :         {
     311           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     312             :                      "Unable to identify source X or Y field for "
     313             :                      "PointFromColumns encoding.");
     314           1 :             return false;
     315             :         }
     316             : 
     317          61 :         if (pszGType == nullptr)
     318             :         {
     319           7 :             poProps->eGeomType = wkbPoint;
     320           7 :             if (poProps->iGeomZField != -1)
     321           1 :                 poProps->eGeomType = OGR_GT_SetZ(poProps->eGeomType);
     322           7 :             if (poProps->iGeomMField != -1)
     323           1 :                 poProps->eGeomType = OGR_GT_SetM(poProps->eGeomType);
     324             :         }
     325             :     }
     326             :     else
     327             :     {
     328           1 :         CPLError(CE_Failure, CPLE_AppDefined, "encoding=\"%s\" not recognised.",
     329             :                  pszEncoding);
     330           1 :         return false;
     331             :     }
     332             : 
     333         143 :     if (poProps->eGeometryStyle == VGS_WKT ||
     334          91 :         poProps->eGeometryStyle == VGS_WKB ||
     335          90 :         poProps->eGeometryStyle == VGS_Shape)
     336             :     {
     337          54 :         const char *pszFieldName = CPLGetXMLValue(psNode, "field", "missing");
     338             : 
     339          54 :         poProps->iGeomField =
     340          54 :             GetFieldIndexCaseSensitiveFirst(GetSrcLayerDefn(), pszFieldName);
     341             : 
     342          54 :         if (poProps->iGeomField == -1)
     343             :         {
     344           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     345             :                      "Unable to identify source field '%s' for geometry.",
     346             :                      pszFieldName);
     347           1 :             return false;
     348          53 :         }
     349             :     }
     350          89 :     else if (poProps->eGeometryStyle == VGS_Direct)
     351             :     {
     352          28 :         const char *pszFieldName = CPLGetXMLValue(psNode, "field", nullptr);
     353             : 
     354          49 :         if (pszFieldName != nullptr ||
     355          21 :             GetSrcLayerDefn()->GetGeomFieldCount() > 1)
     356             :         {
     357          13 :             if (pszFieldName == nullptr)
     358           6 :                 pszFieldName = poProps->osName;
     359          13 :             poProps->iGeomField =
     360          13 :                 GetSrcLayerDefn()->GetGeomFieldIndex(pszFieldName);
     361             : 
     362          13 :             if (poProps->iGeomField == -1)
     363             :             {
     364           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     365             :                          "Unable to identify source geometry field '%s' "
     366             :                          "for geometry.",
     367             :                          pszFieldName);
     368           1 :                 return false;
     369             :             }
     370             :         }
     371          15 :         else if (GetSrcLayerDefn()->GetGeomFieldCount() == 1)
     372             :         {
     373          15 :             poProps->iGeomField = 0;
     374             :         }
     375           0 :         else if (psNode != nullptr)
     376             :         {
     377           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     378             :                      "Unable to identify source geometry field.");
     379           0 :             return false;
     380             :         }
     381             :     }
     382             : 
     383         141 :     poProps->bReportSrcColumn =
     384         141 :         CPLTestBool(CPLGetXMLValue(psNode, "reportSrcColumn", "YES"));
     385             : 
     386             :     // Guess geometry type if not explicitly provided (or computed).
     387         141 :     if (pszGType == nullptr && poProps->eGeomType == wkbUnknown)
     388             :     {
     389          18 :         if (GetSrcLayerDefn()->GetGeomFieldCount() == 1)
     390           7 :             poProps->eGeomType = poSrcLayer->GetGeomType();
     391          11 :         else if (poProps->eGeometryStyle == VGS_Direct &&
     392           6 :                  poProps->iGeomField >= 0)
     393             :         {
     394          12 :             poProps->eGeomType = GetSrcLayerDefn()
     395           6 :                                      ->GetGeomFieldDefn(poProps->iGeomField)
     396           6 :                                      ->GetType();
     397             :         }
     398             :     }
     399             : 
     400             :     // Copy spatial reference system from source if not provided.
     401         141 :     const char *pszSRS = CPLGetXMLValue(psNode, "SRS", nullptr);
     402         141 :     if (pszSRS == nullptr && poProps == apoGeomFieldProps[0].get())
     403         129 :         pszSRS = CPLGetXMLValue(psNodeParent, "LayerSRS", nullptr);
     404         141 :     if (pszSRS == nullptr)
     405             :     {
     406          40 :         const OGRSpatialReference *poSRS = nullptr;
     407          40 :         if (GetSrcLayerDefn()->GetGeomFieldCount() == 1)
     408             :         {
     409          20 :             poSRS = poSrcLayer->GetSpatialRef();
     410             :         }
     411          20 :         else if (poProps->eGeometryStyle == VGS_Direct &&
     412           6 :                  poProps->iGeomField >= 0)
     413             :         {
     414           6 :             poSRS = GetSrcLayerDefn()
     415           6 :                         ->GetGeomFieldDefn(poProps->iGeomField)
     416           6 :                         ->GetSpatialRef();
     417             :         }
     418          40 :         if (poSRS != nullptr)
     419          11 :             poProps->poSRS = OGRSpatialReferenceRefCountedPtr::makeClone(poSRS);
     420             :     }
     421         101 :     else if (poProps->poSRS == nullptr)
     422             :     {
     423          98 :         if (!(EQUAL(pszSRS, "NULL")))
     424             :         {
     425          98 :             OGRSpatialReference oSRS;
     426          98 :             oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     427             : 
     428          98 :             if (oSRS.SetFromUserInput(
     429             :                     pszSRS,
     430             :                     OGRSpatialReference::
     431          98 :                         SET_FROM_USER_INPUT_LIMITATIONS_get()) != OGRERR_NONE)
     432             :             {
     433           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     434             :                          "Failed to import SRS `%s'.", pszSRS);
     435           0 :                 return false;
     436             :             }
     437          98 :             poProps->poSRS = OGRSpatialReferenceRefCountedPtr::makeClone(&oSRS);
     438             :         }
     439             :     }
     440             : 
     441             :     // Do we have a SrcRegion?
     442         141 :     const CPLXMLNode *psSrcRegionNode = CPLGetXMLNode(psNode, "SrcRegion");
     443         141 :     if (psSrcRegionNode == nullptr && poProps == apoGeomFieldProps[0].get())
     444         129 :         psSrcRegionNode = CPLGetXMLNode(psNodeParent, "SrcRegion");
     445         141 :     const char *pszSrcRegion = CPLGetXMLValue(psSrcRegionNode, "", nullptr);
     446         141 :     if (pszSrcRegion != nullptr)
     447             :     {
     448             :         poProps->poSrcRegion =
     449          12 :             OGRGeometryFactory::createFromWkt(pszSrcRegion).first;
     450             : 
     451          12 :         if (poProps->poSrcRegion == nullptr)
     452             :         {
     453           1 :             CPLError(CE_Warning, CPLE_AppDefined,
     454             :                      "Ignoring SrcRegion. It must be a valid WKT geometry");
     455             :         }
     456             : 
     457          12 :         poProps->bSrcClip =
     458          12 :             CPLTestBool(CPLGetXMLValue(psSrcRegionNode, "clip", "FALSE"));
     459             :     }
     460             : 
     461             :     // Set Extent if provided.
     462         141 :     const char *pszExtentXMin = CPLGetXMLValue(psNode, "ExtentXMin", nullptr);
     463         141 :     const char *pszExtentYMin = CPLGetXMLValue(psNode, "ExtentYMin", nullptr);
     464         141 :     const char *pszExtentXMax = CPLGetXMLValue(psNode, "ExtentXMax", nullptr);
     465         141 :     const char *pszExtentYMax = CPLGetXMLValue(psNode, "ExtentYMax", nullptr);
     466         141 :     if (pszExtentXMin != nullptr && pszExtentYMin != nullptr &&
     467           6 :         pszExtentXMax != nullptr && pszExtentYMax != nullptr)
     468             :     {
     469           6 :         poProps->sStaticEnvelope.MinX = CPLAtof(pszExtentXMin);
     470           6 :         poProps->sStaticEnvelope.MinY = CPLAtof(pszExtentYMin);
     471           6 :         poProps->sStaticEnvelope.MaxX = CPLAtof(pszExtentXMax);
     472           6 :         poProps->sStaticEnvelope.MaxY = CPLAtof(pszExtentYMax);
     473             :     }
     474             : 
     475         141 :     poProps->bNullable =
     476         141 :         CPLTestBool(CPLGetXMLValue(psNode, "nullable", "TRUE"));
     477             : 
     478         141 :     if (GetSrcLayerDefn()->GetGeomFieldCount() == 1)
     479             :     {
     480             :         poProps->sCoordinatePrecision =
     481          41 :             GetSrcLayerDefn()->GetGeomFieldDefn(0)->GetCoordinatePrecision();
     482             :     }
     483         100 :     else if (poProps->eGeometryStyle == VGS_Direct && poProps->iGeomField >= 0)
     484             :     {
     485             :         poProps->sCoordinatePrecision =
     486          12 :             GetSrcLayerDefn()
     487          12 :                 ->GetGeomFieldDefn(poProps->iGeomField)
     488          12 :                 ->GetCoordinatePrecision();
     489             :     }
     490         141 :     if (const char *pszXYResolution =
     491         141 :             CPLGetXMLValue(psNode, "XYResolution", nullptr))
     492             :     {
     493           1 :         poProps->sCoordinatePrecision.dfXYResolution = CPLAtof(pszXYResolution);
     494             :     }
     495         141 :     if (const char *pszZResolution =
     496         141 :             CPLGetXMLValue(psNode, "ZResolution", nullptr))
     497             :     {
     498           1 :         poProps->sCoordinatePrecision.dfZResolution = CPLAtof(pszZResolution);
     499             :     }
     500         141 :     if (const char *pszMResolution =
     501         141 :             CPLGetXMLValue(psNode, "MResolution", nullptr))
     502             :     {
     503           1 :         poProps->sCoordinatePrecision.dfMResolution = CPLAtof(pszMResolution);
     504             :     }
     505             : 
     506         141 :     return true;
     507             : }
     508             : 
     509             : /************************************************************************/
     510             : /*                           FullInitialize()                           */
     511             : /************************************************************************/
     512             : 
     513             : // TODO(schwehr): Remove gotos.
     514         508 : bool OGRVRTLayer::FullInitialize()
     515             : {
     516         508 :     if (bHasFullInitialized)
     517           0 :         return true;
     518             : 
     519         508 :     const char *pszSharedSetting = nullptr;
     520         508 :     const char *pszSQL = nullptr;
     521         508 :     const char *pszStyleFieldName = nullptr;
     522         508 :     CPLXMLNode *psChild = nullptr;
     523         508 :     bool bFoundGeometryField = false;
     524             : 
     525         508 :     bHasFullInitialized = true;
     526             : 
     527         508 :     poFeatureDefn = OGRFeatureDefnRefCountedPtr::makeInstance(osName);
     528             : 
     529         508 :     if (poDS->GetRecursionDetected())
     530          34 :         return false;
     531             : 
     532             :     // Figure out the data source name.  It may be treated relative
     533             :     // to vrt filename, but normally it is used directly.
     534         948 :     std::string osSrcDSName = CPLGetXMLValue(psLTree, "SrcDataSource", "");
     535             : 
     536         474 :     if (osSrcDSName.empty())
     537             :     {
     538          20 :         CPLError(CE_Failure, CPLE_AppDefined,
     539             :                  "Missing SrcDataSource for layer %s.", osName.c_str());
     540          20 :         goto error;
     541             :     }
     542             : 
     543         454 :     if (CPLTestBool(
     544         454 :             CPLGetXMLValue(psLTree, "SrcDataSource.relativetoVRT", "0")))
     545             :     {
     546             :         static const char *const apszPrefixes[] = {"CSV:", "GPSBABEL:"};
     547         261 :         bool bDone = false;
     548         783 :         for (size_t i = 0; i < sizeof(apszPrefixes) / sizeof(apszPrefixes[0]);
     549             :              i++)
     550             :         {
     551         522 :             const char *pszPrefix = apszPrefixes[i];
     552         522 :             if (EQUALN(osSrcDSName.c_str(), pszPrefix, strlen(pszPrefix)))
     553             :             {
     554           0 :                 auto nLastPart = osSrcDSName.find(':') + 1;
     555             :                 // CSV:z:/foo.xyz
     556           0 :                 if ((osSrcDSName[nLastPart] == '/' ||
     557           0 :                      osSrcDSName[nLastPart] == '\\') &&
     558           0 :                     nLastPart >= 3 && osSrcDSName[nLastPart - 3] == ':')
     559           0 :                     nLastPart -= 2;
     560           0 :                 CPLString osPrefix(osSrcDSName);
     561           0 :                 osPrefix.resize(nLastPart);
     562           0 :                 osSrcDSName = osPrefix + CPLProjectRelativeFilenameSafe(
     563             :                                              osVRTDirectory,
     564           0 :                                              osSrcDSName.c_str() + nLastPart);
     565           0 :                 bDone = true;
     566           0 :                 break;
     567             :             }
     568             :         }
     569         261 :         if (!bDone)
     570             :         {
     571         522 :             osSrcDSName = CPLProjectRelativeFilenameSafe(osVRTDirectory,
     572         261 :                                                          osSrcDSName.c_str());
     573             :         }
     574             :     }
     575             : 
     576             :     // Are we accessing this datasource in shared mode?  We default
     577             :     // to shared for SrcSQL requests, but we also allow the XML to
     578             :     // control our shared setting with an attribute on the
     579             :     // datasource element.
     580         454 :     pszSharedSetting = CPLGetXMLValue(psLTree, "SrcDataSource.shared", nullptr);
     581         454 :     if (pszSharedSetting == nullptr)
     582             :     {
     583         377 :         if (CPLGetXMLValue(psLTree, "SrcSQL", nullptr) == nullptr)
     584         374 :             pszSharedSetting = "OFF";
     585             :         else
     586           3 :             pszSharedSetting = "ON";
     587             :     }
     588             : 
     589         454 :     bSrcDSShared = CPLTestBool(pszSharedSetting);
     590             : 
     591             :     // Update mode doesn't make sense if we have a SrcSQL element.
     592         454 :     if (CPLGetXMLValue(psLTree, "SrcSQL", nullptr) != nullptr)
     593           3 :         bUpdate = false;
     594             : 
     595             :     // Try to access the datasource.
     596         454 : try_again:
     597         454 :     CPLErrorReset();
     598         454 :     if (EQUAL(osSrcDSName.c_str(), "@dummy@"))
     599             :     {
     600           0 :         poSrcDS.reset(
     601           0 :             MEMDataset::Create("@dummy@", 0, 0, 0, GDT_Unknown, nullptr));
     602           0 :         poSrcDS->CreateLayer("@dummy@");
     603             :     }
     604         454 :     else if (bSrcDSShared)
     605             :     {
     606          79 :         if (poDS->IsInForbiddenNames(osSrcDSName.c_str()))
     607             :         {
     608           4 :             CPLError(CE_Failure, CPLE_AppDefined,
     609             :                      "Cyclic VRT opening detected!");
     610           4 :             poDS->SetRecursionDetected();
     611             :         }
     612             :         else
     613             :         {
     614             :             CPLStringList aosOpenOptions(
     615         150 :                 GDALDeserializeOpenOptionsFromXML(psLTree));
     616          75 :             int l_nFlags = GDAL_OF_VECTOR | GDAL_OF_SHARED;
     617          75 :             if (bUpdate)
     618           0 :                 l_nFlags |= GDAL_OF_UPDATE;
     619          75 :             poSrcDS.reset(GDALDataset::Open(osSrcDSName.c_str(), l_nFlags,
     620          75 :                                             nullptr, aosOpenOptions.List(),
     621             :                                             nullptr));
     622             :             // Is it a VRT datasource?
     623          75 :             if (poSrcDS != nullptr && poSrcDS->GetDriver() == poDS->GetDriver())
     624             :             {
     625             :                 OGRVRTDataSource *poVRTSrcDS =
     626           3 :                     cpl::down_cast<OGRVRTDataSource *>(poSrcDS.get());
     627           3 :                 poVRTSrcDS->AddForbiddenNames(poDS->GetDescription());
     628             :             }
     629             :         }
     630             :     }
     631             :     else
     632             :     {
     633         375 :         if (poDS->GetCallLevel() < 32)
     634             :         {
     635             :             CPLStringList aosOpenOptions(
     636         746 :                 GDALDeserializeOpenOptionsFromXML(psLTree));
     637         373 :             int l_nFlags = GDAL_OF_VECTOR;
     638         373 :             if (bUpdate)
     639          44 :                 l_nFlags |= GDAL_OF_UPDATE;
     640         373 :             poSrcDS.reset(GDALDataset::Open(osSrcDSName.c_str(), l_nFlags,
     641         373 :                                             nullptr, aosOpenOptions.List(),
     642             :                                             nullptr));
     643             :             // Is it a VRT datasource?
     644         373 :             if (poSrcDS != nullptr && poSrcDS->GetDriver() == poDS->GetDriver())
     645             :             {
     646             :                 OGRVRTDataSource *poVRTSrcDS =
     647          66 :                     dynamic_cast<OGRVRTDataSource *>(poSrcDS.get());
     648          66 :                 if (poVRTSrcDS)
     649             :                 {
     650          66 :                     poVRTSrcDS->SetCallLevel(poDS->GetCallLevel() + 1);
     651          66 :                     poVRTSrcDS->SetParentDS(poDS);
     652             :                 }
     653             :             }
     654             :         }
     655             :         else
     656             :         {
     657           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     658             :                      "Trying to open a VRT from a VRT from a VRT from ... "
     659             :                      "[32 times] a VRT!");
     660             : 
     661           2 :             poDS->SetRecursionDetected();
     662             : 
     663           2 :             OGRVRTDataSource *poParent = poDS->GetParentDS();
     664          66 :             while (poParent != nullptr)
     665             :             {
     666          64 :                 poParent->SetRecursionDetected();
     667          64 :                 poParent = poParent->GetParentDS();
     668             :             }
     669             :         }
     670             :     }
     671             : 
     672         454 :     if (poSrcDS == nullptr)
     673             :     {
     674          10 :         if (bUpdate)
     675             :         {
     676           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     677             :                      "Cannot open datasource `%s' in update mode. "
     678             :                      "Trying again in read-only mode",
     679             :                      osSrcDSName.c_str());
     680           0 :             bUpdate = false;
     681           0 :             goto try_again;
     682             :         }
     683          10 :         if (strlen(CPLGetLastErrorMsg()) == 0)
     684           4 :             CPLError(CE_Failure, CPLE_AppDefined,
     685             :                      "Failed to open datasource `%s'.", osSrcDSName.c_str());
     686          10 :         goto error;
     687             :     }
     688             : 
     689             :     // Apply any metadata.
     690         444 :     oMDMD.XMLInit(psLTree, TRUE);
     691             : 
     692             :     // Is this layer derived from an SQL query result?
     693         444 :     pszSQL = CPLGetXMLValue(psLTree, "SrcSQL", nullptr);
     694             : 
     695         444 :     if (pszSQL != nullptr)
     696             :     {
     697             :         const char *pszDialect =
     698           3 :             CPLGetXMLValue(psLTree, "SrcSQL.dialect", nullptr);
     699           3 :         if (pszDialect != nullptr && pszDialect[0] == '\0')
     700           0 :             pszDialect = nullptr;
     701           3 :         poSrcLayer = poSrcDS->ExecuteSQL(pszSQL, nullptr, pszDialect);
     702           3 :         if (poSrcLayer == nullptr)
     703             :         {
     704           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     705             :                      "SQL statement failed, or returned no layer result:\n%s",
     706             :                      pszSQL);
     707           1 :             goto error;
     708             :         }
     709           2 :         bSrcLayerFromSQL = true;
     710             :     }
     711             : 
     712             :     // Fetch the layer if it is a regular layer.
     713         443 :     if (poSrcLayer == nullptr)
     714             :     {
     715             :         const char *pszSrcLayerName =
     716         441 :             CPLGetXMLValue(psLTree, "SrcLayer", osName);
     717             : 
     718         441 :         poSrcLayer = poSrcDS->GetLayerByName(pszSrcLayerName);
     719         441 :         if (poSrcLayer == nullptr)
     720             :         {
     721           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     722             :                      "Failed to find layer '%s' on datasource '%s'.",
     723             :                      pszSrcLayerName, osSrcDSName.c_str());
     724           2 :             goto error;
     725             :         }
     726             :     }
     727             : 
     728             :     // Search for GeometryField definitions.
     729             : 
     730             :     // Create as many OGRVRTGeomFieldProps as there are
     731             :     // GeometryField elements.
     732        1973 :     for (psChild = psLTree->psChild; psChild != nullptr;
     733        1532 :          psChild = psChild->psNext)
     734             :     {
     735        1536 :         if (psChild->eType == CXT_Element &&
     736        1044 :             EQUAL(psChild->pszValue, "GeometryField"))
     737             :         {
     738         137 :             if (!bFoundGeometryField)
     739             :             {
     740         131 :                 bFoundGeometryField = true;
     741             : 
     742             :                 // Recreate the first one if already taken into account in
     743             :                 // FastInitialize().
     744         131 :                 if (apoGeomFieldProps.size() == 1)
     745             :                 {
     746         111 :                     apoGeomFieldProps.clear();
     747             :                 }
     748             :             }
     749             : 
     750         137 :             apoGeomFieldProps.push_back(
     751         274 :                 std::make_unique<OGRVRTGeomFieldProps>());
     752         137 :             if (!ParseGeometryField(psChild, psLTree,
     753         137 :                                     apoGeomFieldProps.back().get()))
     754             :             {
     755           4 :                 goto error;
     756             :             }
     757             :         }
     758             :     }
     759             : 
     760         747 :     if (!bFoundGeometryField &&
     761         310 :         CPLGetXMLValue(psLTree, "SrcRegion", nullptr) != nullptr)
     762             :     {
     763           3 :         apoGeomFieldProps.push_back(std::make_unique<OGRVRTGeomFieldProps>());
     764             :     }
     765             : 
     766         437 :     if (!bFoundGeometryField && apoGeomFieldProps.size() == 1)
     767             :     {
     768             :         // Otherwise use the top-level elements such as SrcRegion.
     769           8 :         if (!ParseGeometryField(nullptr, psLTree, apoGeomFieldProps[0].get()))
     770           0 :             goto error;
     771             :     }
     772             : 
     773         739 :     if (apoGeomFieldProps.empty() &&
     774         302 :         CPLGetXMLValue(psLTree, "GeometryType", nullptr) == nullptr)
     775             :     {
     776             :         // If no GeometryField is found but source geometry fields
     777             :         // exist, use them.
     778         297 :         for (int iGeomField = 0;
     779         604 :              iGeomField < GetSrcLayerDefn()->GetGeomFieldCount(); iGeomField++)
     780             :         {
     781         307 :             apoGeomFieldProps.push_back(
     782         614 :                 std::make_unique<OGRVRTGeomFieldProps>());
     783         307 :             OGRVRTGeomFieldProps *poProps = apoGeomFieldProps.back().get();
     784             :             OGRGeomFieldDefn *poFDefn =
     785         307 :                 GetSrcLayerDefn()->GetGeomFieldDefn(iGeomField);
     786         307 :             poProps->osName = poFDefn->GetNameRef();
     787         307 :             poProps->eGeomType = poFDefn->GetType();
     788         614 :             poProps->poSRS = OGRSpatialReferenceRefCountedPtr::makeClone(
     789         614 :                 poFDefn->GetSpatialRef());
     790         307 :             poProps->iGeomField = iGeomField;
     791         307 :             poProps->bNullable = CPL_TO_BOOL(poFDefn->IsNullable());
     792             :         }
     793             :     }
     794             : 
     795             :     // Instantiate real geometry fields from VRT properties.
     796         437 :     poFeatureDefn->SetGeomType(wkbNone);
     797         885 :     for (const auto &poProp : apoGeomFieldProps)
     798             :     {
     799         896 :         OGRGeomFieldDefn oFieldDefn(poProp->osName, poProp->eGeomType);
     800         448 :         oFieldDefn.SetSpatialRef(poProp->poSRS);
     801         448 :         oFieldDefn.SetNullable(poProp->bNullable);
     802         448 :         oFieldDefn.SetCoordinatePrecision(poProp->sCoordinatePrecision);
     803         448 :         poFeatureDefn->AddGeomFieldDefn(&oFieldDefn);
     804             :     }
     805             : 
     806         437 :     bAttrFilterPassThrough = true;
     807             : 
     808             :     // Figure out what should be used as an FID.
     809             :     {
     810         437 :         CPLXMLNode *psFIDNode = CPLGetXMLNode(psLTree, "FID");
     811         437 :         if (psFIDNode != nullptr)
     812             :         {
     813             :             const char *pszSrcFIDFieldName =
     814          31 :                 CPLGetXMLValue(psFIDNode, nullptr, "");
     815          31 :             if (!EQUAL(pszSrcFIDFieldName, ""))
     816             :             {
     817          31 :                 iFIDField = GetFieldIndexCaseSensitiveFirst(GetSrcLayerDefn(),
     818             :                                                             pszSrcFIDFieldName);
     819          31 :                 if (iFIDField == -1)
     820             :                 {
     821           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
     822             :                              "Unable to identify FID field '%s'.",
     823             :                              pszSrcFIDFieldName);
     824           1 :                     goto error;
     825             :                 }
     826             :             }
     827             : 
     828             :             // User facing FID column name.
     829             :             osFIDFieldName =
     830          30 :                 CPLGetXMLValue(psFIDNode, "name", pszSrcFIDFieldName);
     831          30 :             if (!EQUAL(osFIDFieldName, poSrcLayer->GetFIDColumn()))
     832             :             {
     833          30 :                 bAttrFilterPassThrough = false;
     834             :             }
     835             :         }
     836             :         else
     837             :         {
     838         406 :             osFIDFieldName = poSrcLayer->GetFIDColumn();
     839             :         }
     840             :     }
     841             : 
     842             :     // Figure out what should be used as a Style.
     843         436 :     pszStyleFieldName = CPLGetXMLValue(psLTree, "Style", nullptr);
     844             : 
     845         436 :     if (pszStyleFieldName != nullptr)
     846             :     {
     847           4 :         iStyleField = GetFieldIndexCaseSensitiveFirst(GetSrcLayerDefn(),
     848             :                                                       pszStyleFieldName);
     849           4 :         if (iStyleField == -1)
     850             :         {
     851           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     852             :                      "Unable to identify Style field '%s'.", pszStyleFieldName);
     853           1 :             goto error;
     854             :         }
     855             : 
     856           3 :         if (!EQUAL(pszStyleFieldName, "OGR_STYLE"))
     857             :         {
     858           3 :             bAttrFilterPassThrough = false;
     859             :         }
     860             :     }
     861             : 
     862             :     // Search for schema definitions in the VRT.
     863        1946 :     for (psChild = psLTree->psChild; psChild != nullptr;
     864        1511 :          psChild = psChild->psNext)
     865             :     {
     866        1518 :         if (psChild->eType == CXT_Element && EQUAL(psChild->pszValue, "Field"))
     867             :         {
     868             :             // Field name.
     869          84 :             const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
     870          84 :             if (pszName == nullptr)
     871             :             {
     872           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     873             :                          "Unable to identify Field name.");
     874           7 :                 goto error;
     875             :             }
     876             : 
     877          83 :             OGRFieldDefn oFieldDefn(pszName, OFTString);
     878             : 
     879             :             // Type.
     880          83 :             const char *pszArg = CPLGetXMLValue(psChild, "type", nullptr);
     881             : 
     882          83 :             if (pszArg != nullptr)
     883             :             {
     884          69 :                 int iType = 0;  // Used after for.
     885             : 
     886         339 :                 for (; iType <= static_cast<int>(OFTMaxType); iType++)
     887             :                 {
     888         338 :                     if (EQUAL(pszArg, OGRFieldDefn::GetFieldTypeName(
     889             :                                           static_cast<OGRFieldType>(iType))))
     890             :                     {
     891          68 :                         oFieldDefn.SetType(static_cast<OGRFieldType>(iType));
     892          68 :                         break;
     893             :                     }
     894             :                 }
     895             : 
     896          69 :                 if (iType > static_cast<int>(OFTMaxType))
     897             :                 {
     898           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
     899             :                              "Unable to identify Field type '%s'.", pszArg);
     900           1 :                     goto error;
     901             :                 }
     902             :             }
     903             : 
     904             :             // Subtype.
     905          82 :             pszArg = CPLGetXMLValue(psChild, "subtype", nullptr);
     906          82 :             if (pszArg != nullptr)
     907             :             {
     908           7 :                 OGRFieldSubType eSubType = OFSTNone;
     909             : 
     910           7 :                 int iType = 0;  // Used after for.
     911          19 :                 for (; iType <= static_cast<int>(OFSTMaxSubType); iType++)
     912             :                 {
     913          18 :                     if (EQUAL(pszArg, OGRFieldDefn::GetFieldSubTypeName(
     914             :                                           static_cast<OGRFieldSubType>(iType))))
     915             :                     {
     916           6 :                         eSubType = static_cast<OGRFieldSubType>(iType);
     917           6 :                         break;
     918             :                     }
     919             :                 }
     920             : 
     921           7 :                 if (iType > static_cast<int>(OFSTMaxSubType))
     922             :                 {
     923           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
     924             :                              "Unable to identify Field subtype '%s'.", pszArg);
     925           1 :                     goto error;
     926             :                 }
     927             : 
     928           6 :                 if (!OGR_AreTypeSubTypeCompatible(oFieldDefn.GetType(),
     929             :                                                   eSubType))
     930             :                 {
     931           1 :                     CPLError(
     932             :                         CE_Failure, CPLE_AppDefined,
     933             :                         "Invalid subtype '%s' for type '%s'.", pszArg,
     934             :                         OGRFieldDefn::GetFieldTypeName(oFieldDefn.GetType()));
     935           1 :                     goto error;
     936             :                 }
     937             : 
     938           5 :                 oFieldDefn.SetSubType(eSubType);
     939             :             }
     940             : 
     941             :             // Width and precision.
     942          80 :             int nWidth = atoi(CPLGetXMLValue(psChild, "width", "0"));
     943          80 :             if (nWidth < 0)
     944             :             {
     945           1 :                 CPLError(CE_Failure, CPLE_IllegalArg,
     946             :                          "Invalid width for field %s.", pszName);
     947           1 :                 goto error;
     948             :             }
     949          79 :             oFieldDefn.SetWidth(nWidth);
     950             : 
     951          79 :             int nPrecision = atoi(CPLGetXMLValue(psChild, "precision", "0"));
     952          79 :             if (nPrecision < 0 || nPrecision > 1024)
     953             :             {
     954           1 :                 CPLError(CE_Failure, CPLE_IllegalArg,
     955             :                          "Invalid precision for field %s.", pszName);
     956           1 :                 goto error;
     957             :             }
     958          78 :             oFieldDefn.SetPrecision(nPrecision);
     959             : 
     960             :             // Nullable attribute.
     961             :             const bool bNullable =
     962          78 :                 CPLTestBool(CPLGetXMLValue(psChild, "nullable", "true"));
     963          78 :             oFieldDefn.SetNullable(bNullable);
     964             : 
     965             :             // Unique attribute.
     966             :             const bool bUnique =
     967          78 :                 CPLTestBool(CPLGetXMLValue(psChild, "unique", "false"));
     968          78 :             oFieldDefn.SetUnique(bUnique);
     969             : 
     970             :             // Default attribute.
     971          78 :             oFieldDefn.SetDefault(CPLGetXMLValue(psChild, "default", nullptr));
     972             : 
     973             :             const char *pszAlternativeName =
     974          78 :                 CPLGetXMLValue(psChild, "alternativeName", nullptr);
     975          78 :             if (pszAlternativeName)
     976           1 :                 oFieldDefn.SetAlternativeName(pszAlternativeName);
     977             : 
     978             :             const char *pszComment =
     979          78 :                 CPLGetXMLValue(psChild, "comment", nullptr);
     980          78 :             if (pszComment)
     981           1 :                 oFieldDefn.SetComment(pszComment);
     982             : 
     983             :             // Create the field.
     984          78 :             poFeatureDefn->AddFieldDefn(&oFieldDefn);
     985             : 
     986          78 :             abDirectCopy.push_back(FALSE);
     987             : 
     988             :             // Source field.
     989             :             int iSrcField =
     990          78 :                 GetFieldIndexCaseSensitiveFirst(GetSrcLayerDefn(), pszName);
     991             : 
     992          78 :             pszArg = CPLGetXMLValue(psChild, "src", nullptr);
     993             : 
     994          78 :             if (pszArg != nullptr)
     995             :             {
     996          55 :                 iSrcField =
     997          55 :                     GetFieldIndexCaseSensitiveFirst(GetSrcLayerDefn(), pszArg);
     998          55 :                 if (iSrcField == -1)
     999             :                 {
    1000           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1001             :                              "Unable to find source field '%s'.", pszArg);
    1002           1 :                     goto error;
    1003             :                 }
    1004             :             }
    1005             : 
    1006          77 :             if (iSrcField < 0 ||
    1007          54 :                 (pszArg != nullptr && strcmp(pszArg, pszName) != 0))
    1008             :             {
    1009           7 :                 bAttrFilterPassThrough = false;
    1010             :             }
    1011             :             else
    1012             :             {
    1013             :                 OGRFieldDefn *poSrcFieldDefn =
    1014          70 :                     GetSrcLayerDefn()->GetFieldDefn(iSrcField);
    1015          70 :                 if (poSrcFieldDefn->GetType() != oFieldDefn.GetType())
    1016          46 :                     bAttrFilterPassThrough = false;
    1017             :             }
    1018             : 
    1019          77 :             anSrcField.push_back(iSrcField);
    1020             :         }
    1021             :     }
    1022             : 
    1023         428 :     CPLAssert(poFeatureDefn->GetFieldCount() ==
    1024             :               static_cast<int>(anSrcField.size()));
    1025             : 
    1026             :     // Create the schema, if it was not explicitly in the VRT.
    1027         428 :     if (poFeatureDefn->GetFieldCount() == 0)
    1028             :     {
    1029         400 :         const int nSrcFieldCount = GetSrcLayerDefn()->GetFieldCount();
    1030             : 
    1031        1368 :         for (int iSrcField = 0; iSrcField < nSrcFieldCount; iSrcField++)
    1032             :         {
    1033         968 :             bool bSkip = false;
    1034        2199 :             for (const auto &poProp : apoGeomFieldProps)
    1035             :             {
    1036        1259 :                 if (!poProp->bReportSrcColumn &&
    1037          20 :                     (iSrcField == poProp->iGeomXField ||
    1038          16 :                      iSrcField == poProp->iGeomYField ||
    1039          12 :                      iSrcField == poProp->iGeomZField ||
    1040          12 :                      iSrcField == poProp->iGeomMField ||
    1041          12 :                      (poProp->eGeometryStyle != VGS_Direct &&
    1042          12 :                       iSrcField == poProp->iGeomField)))
    1043             :                 {
    1044           8 :                     bSkip = true;
    1045           8 :                     break;
    1046             :                 }
    1047             :             }
    1048         968 :             if (bSkip)
    1049           8 :                 continue;
    1050             : 
    1051         960 :             poFeatureDefn->AddFieldDefn(
    1052         960 :                 GetSrcLayerDefn()->GetFieldDefn(iSrcField));
    1053         960 :             anSrcField.push_back(iSrcField);
    1054         960 :             abDirectCopy.push_back(TRUE);
    1055             :         }
    1056             : 
    1057         400 :         bAttrFilterPassThrough = true;
    1058             :     }
    1059             : 
    1060             :     // Is VRT layer definition identical to the source layer defn?
    1061             :     // If so, use it directly, and save the translation of features.
    1062         823 :     if (GetSrcLayerDefn() != nullptr && iFIDField == -1 && iStyleField == -1 &&
    1063         395 :         GetSrcLayerDefn()->IsSame(poFeatureDefn.get()))
    1064             :     {
    1065         242 :         bool bSame = true;
    1066         534 :         for (size_t i = 0; i < apoGeomFieldProps.size(); i++)
    1067             :         {
    1068         584 :             if (apoGeomFieldProps[i]->eGeometryStyle != VGS_Direct ||
    1069         292 :                 apoGeomFieldProps[i]->iGeomField != static_cast<int>(i))
    1070             :             {
    1071           0 :                 bSame = false;
    1072           0 :                 break;
    1073             :             }
    1074             :         }
    1075         242 :         if (bSame)
    1076             :         {
    1077         242 :             CPLDebug("VRT", "Source feature definition is identical to VRT "
    1078             :                             "feature definition. Use optimized path");
    1079         242 :             poFeatureDefn.reset(GetSrcLayerDefn(), /* add_ref = */ true);
    1080         534 :             for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    1081             :             {
    1082         292 :                 apoGeomFieldProps[i]->poSRS.reset(
    1083             :                     const_cast<OGRSpatialReference *>(
    1084         292 :                         poFeatureDefn->GetGeomFieldDefn(i)->GetSpatialRef()),
    1085             :                     /* add_ref = */ true);
    1086             :             }
    1087             :         }
    1088             :     }
    1089             : 
    1090         428 :     CPLAssert(poFeatureDefn->GetGeomFieldCount() ==
    1091             :               static_cast<int>(apoGeomFieldProps.size()));
    1092             : 
    1093             :     // Allow vrt to override whether attribute filters should be
    1094             :     // passed through.
    1095         428 :     if (CPLGetXMLValue(psLTree, "attrFilterPassThrough", nullptr) != nullptr)
    1096           0 :         bAttrFilterPassThrough = CPLTestBool(
    1097           0 :             CPLGetXMLValue(psLTree, "attrFilterPassThrough", "TRUE"));
    1098             : 
    1099         428 :     SetIgnoredFields(nullptr);
    1100             : 
    1101         428 :     return true;
    1102             : 
    1103          46 : error:
    1104          46 :     bError = true;
    1105          46 :     poFeatureDefn = OGRFeatureDefnRefCountedPtr::makeInstance(osName);
    1106          46 :     poFeatureDefn->SetGeomType(wkbNone);
    1107          46 :     apoGeomFieldProps.clear();
    1108          46 :     return false;
    1109             : }
    1110             : 
    1111             : /************************************************************************/
    1112             : /*                            ResetReading()                            */
    1113             : /************************************************************************/
    1114             : 
    1115        3403 : void OGRVRTLayer::ResetReading()
    1116             : {
    1117        3403 :     bNeedReset = true;
    1118        3403 : }
    1119             : 
    1120             : /************************************************************************/
    1121             : /*                         ResetSourceReading()                         */
    1122             : /************************************************************************/
    1123             : 
    1124        1444 : bool OGRVRTLayer::ResetSourceReading()
    1125             : 
    1126             : {
    1127        1444 :     bool bSuccess = true;
    1128             : 
    1129             :     // Do we want to let source layer do spatial restriction?
    1130        1444 :     char *pszFilter = nullptr;
    1131        4317 :     for (size_t i = 0; i < apoGeomFieldProps.size(); i++)
    1132             :     {
    1133        2067 :         if ((m_poFilterGeom || apoGeomFieldProps[i]->poSrcRegion) &&
    1134        5004 :             apoGeomFieldProps[i]->bUseSpatialSubquery &&
    1135          32 :             apoGeomFieldProps[i]->eGeometryStyle == VGS_PointFromColumns)
    1136             :         {
    1137          64 :             OGRFieldDefn *poXField = poSrcLayer->GetLayerDefn()->GetFieldDefn(
    1138          32 :                 apoGeomFieldProps[i]->iGeomXField);
    1139          64 :             OGRFieldDefn *poYField = poSrcLayer->GetLayerDefn()->GetFieldDefn(
    1140          32 :                 apoGeomFieldProps[i]->iGeomYField);
    1141             : 
    1142          32 :             const char *pszXField = poXField->GetNameRef();
    1143          32 :             const char *pszYField = poYField->GetNameRef();
    1144             : 
    1145          32 :             OGRFieldType xType = poXField->GetType();
    1146          32 :             OGRFieldType yType = poYField->GetType();
    1147          32 :             if (!((xType == OFTReal || xType == OFTInteger ||
    1148             :                    xType == OFTInteger64) &&
    1149           0 :                   (yType == OFTReal || yType == OFTInteger ||
    1150             :                    yType == OFTInteger64)))
    1151             :             {
    1152           2 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1153             :                          "The '%s' and/or '%s' fields of the source layer "
    1154             :                          "are not declared as numeric fields, "
    1155             :                          "so the spatial filter cannot be turned into an "
    1156             :                          "attribute filter on them",
    1157             :                          pszXField, pszYField);
    1158           2 :                 apoGeomFieldProps[i]->bUseSpatialSubquery = false;
    1159             :             }
    1160             : 
    1161          32 :             if (apoGeomFieldProps[i]->bUseSpatialSubquery)
    1162             :             {
    1163          30 :                 OGREnvelope sEnvelope;
    1164          60 :                 CPLString osFilter;
    1165             : 
    1166          30 :                 if (apoGeomFieldProps[i]->poSrcRegion != nullptr)
    1167             :                 {
    1168           5 :                     if (m_poFilterGeom == nullptr)
    1169             :                     {
    1170           3 :                         apoGeomFieldProps[i]->poSrcRegion->getEnvelope(
    1171           3 :                             &sEnvelope);
    1172             :                     }
    1173             :                     else
    1174             :                     {
    1175             :                         auto poIntersection = std::unique_ptr<OGRGeometry>(
    1176           2 :                             apoGeomFieldProps[i]->poSrcRegion->Intersection(
    1177           4 :                                 m_poFilterGeom));
    1178           2 :                         if (poIntersection && !poIntersection->IsEmpty())
    1179             :                         {
    1180           2 :                             poIntersection->getEnvelope(&sEnvelope);
    1181             :                         }
    1182             :                         else
    1183             :                         {
    1184           0 :                             sEnvelope.MinX = 0;
    1185           0 :                             sEnvelope.MaxX = 0;
    1186           0 :                             sEnvelope.MinY = 0;
    1187           0 :                             sEnvelope.MaxY = 0;
    1188             :                         }
    1189             :                     }
    1190             :                 }
    1191             :                 else
    1192             :                 {
    1193          25 :                     CPLAssert(m_poFilterGeom);
    1194          25 :                     m_poFilterGeom->getEnvelope(&sEnvelope);
    1195             :                 }
    1196             : 
    1197          30 :                 if (!std::isinf(sEnvelope.MinX))
    1198             :                     osFilter +=
    1199          28 :                         CPLSPrintf("\"%s\" > %.15g", pszXField, sEnvelope.MinX);
    1200           2 :                 else if (sEnvelope.MinX > 0)
    1201           0 :                     osFilter += "0 = 1";
    1202             : 
    1203          30 :                 if (!std::isinf(sEnvelope.MaxX))
    1204             :                 {
    1205          28 :                     if (!osFilter.empty())
    1206          28 :                         osFilter += " AND ";
    1207             :                     osFilter +=
    1208          28 :                         CPLSPrintf("\"%s\" < %.15g", pszXField, sEnvelope.MaxX);
    1209             :                 }
    1210           2 :                 else if (sEnvelope.MaxX < 0)
    1211             :                 {
    1212           0 :                     if (!osFilter.empty())
    1213           0 :                         osFilter += " AND ";
    1214           0 :                     osFilter += "0 = 1";
    1215             :                 }
    1216             : 
    1217          30 :                 if (!std::isinf(sEnvelope.MinY))
    1218             :                 {
    1219          28 :                     if (!osFilter.empty())
    1220          28 :                         osFilter += " AND ";
    1221             :                     osFilter +=
    1222          28 :                         CPLSPrintf("\"%s\" > %.15g", pszYField, sEnvelope.MinY);
    1223             :                 }
    1224           2 :                 else if (sEnvelope.MinY > 0)
    1225             :                 {
    1226           0 :                     if (!osFilter.empty())
    1227           0 :                         osFilter += " AND ";
    1228           0 :                     osFilter += "0 = 1";
    1229             :                 }
    1230             : 
    1231          30 :                 if (!std::isinf(sEnvelope.MaxY))
    1232             :                 {
    1233          28 :                     if (!osFilter.empty())
    1234          28 :                         osFilter += " AND ";
    1235             :                     osFilter +=
    1236          28 :                         CPLSPrintf("\"%s\" < %.15g", pszYField, sEnvelope.MaxY);
    1237             :                 }
    1238           2 :                 else if (sEnvelope.MaxY < 0)
    1239             :                 {
    1240           0 :                     if (!osFilter.empty())
    1241           0 :                         osFilter += " AND ";
    1242           0 :                     osFilter += "0 = 1";
    1243             :                 }
    1244             : 
    1245          30 :                 if (!osFilter.empty())
    1246             :                 {
    1247          28 :                     pszFilter = CPLStrdup(osFilter);
    1248             :                 }
    1249             :             }
    1250             : 
    1251             :             // Just do it on one geometry field. To complicated otherwise!
    1252          32 :             break;
    1253             :         }
    1254             :     }
    1255             : 
    1256             :     // Install spatial + attr filter query on source layer.
    1257        1444 :     if (pszFilter == nullptr && pszAttrFilter == nullptr)
    1258             :     {
    1259        1144 :         bSuccess = poSrcLayer->SetAttributeFilter(nullptr) == OGRERR_NONE;
    1260             :     }
    1261         300 :     else if (pszFilter != nullptr && pszAttrFilter == nullptr)
    1262             :     {
    1263          21 :         bSuccess = poSrcLayer->SetAttributeFilter(pszFilter) == OGRERR_NONE;
    1264             :     }
    1265         279 :     else if (pszFilter == nullptr && pszAttrFilter != nullptr)
    1266             :     {
    1267         272 :         bSuccess = poSrcLayer->SetAttributeFilter(pszAttrFilter) == OGRERR_NONE;
    1268             :     }
    1269             :     else
    1270             :     {
    1271           7 :         CPLString osMerged = pszFilter;
    1272             : 
    1273           7 :         osMerged += " AND (";
    1274           7 :         osMerged += pszAttrFilter;
    1275           7 :         osMerged += ")";
    1276             : 
    1277           7 :         bSuccess = poSrcLayer->SetAttributeFilter(osMerged) == OGRERR_NONE;
    1278             :     }
    1279             : 
    1280        1444 :     CPLFree(pszFilter);
    1281             : 
    1282        1444 :     m_bEmptyResultSet = false;
    1283             : 
    1284             :     // Clear spatial filter (to be safe) for non direct geometries
    1285             :     // and reset reading.
    1286        1444 :     if (m_iGeomFieldFilter < static_cast<int>(apoGeomFieldProps.size()) &&
    1287        2713 :         apoGeomFieldProps[m_iGeomFieldFilter]->eGeometryStyle == VGS_Direct &&
    1288        1269 :         apoGeomFieldProps[m_iGeomFieldFilter]->iGeomField >= 0)
    1289             :     {
    1290        1269 :         OGRGeometry *poNewSpatialGeom = nullptr;
    1291             :         OGRGeometry *poSrcRegion =
    1292        1269 :             apoGeomFieldProps[m_iGeomFieldFilter]->poSrcRegion.get();
    1293        1269 :         std::unique_ptr<OGRGeometry> poIntersection;
    1294             : 
    1295        1269 :         if (poSrcRegion == nullptr)
    1296             :         {
    1297        1210 :             poNewSpatialGeom = m_poFilterGeom;
    1298             :         }
    1299          59 :         else if (m_poFilterGeom == nullptr)
    1300             :         {
    1301          42 :             poNewSpatialGeom = poSrcRegion;
    1302             :         }
    1303             :         else
    1304             :         {
    1305          17 :             bool bDoIntersection = true;
    1306          17 :             if (m_bFilterIsEnvelope)
    1307             :             {
    1308          13 :                 OGREnvelope sEnvelope;
    1309          13 :                 m_poFilterGeom->getEnvelope(&sEnvelope);
    1310          15 :                 if (std::isinf(sEnvelope.MinX) && std::isinf(sEnvelope.MinY) &&
    1311           2 :                     std::isinf(sEnvelope.MaxX) && std::isinf(sEnvelope.MaxY) &&
    1312           1 :                     sEnvelope.MinX < 0 && sEnvelope.MinY < 0 &&
    1313          14 :                     sEnvelope.MaxX > 0 && sEnvelope.MaxY > 0)
    1314             :                 {
    1315           1 :                     poNewSpatialGeom = poSrcRegion;
    1316           1 :                     bDoIntersection = false;
    1317             :                 }
    1318             :             }
    1319          17 :             if (bDoIntersection)
    1320             :             {
    1321          16 :                 poIntersection.reset(m_poFilterGeom->Intersection(poSrcRegion));
    1322          16 :                 poNewSpatialGeom = poIntersection.get();
    1323          16 :                 if (!poIntersection)
    1324           0 :                     m_bEmptyResultSet = true;
    1325             :             }
    1326             :         }
    1327        1269 :         poSrcLayer->SetSpatialFilter(
    1328        1269 :             apoGeomFieldProps[m_iGeomFieldFilter]->iGeomField,
    1329             :             poNewSpatialGeom);
    1330             :     }
    1331             :     else
    1332             :     {
    1333         175 :         poSrcLayer->SetSpatialFilter(nullptr);
    1334             :     }
    1335        1444 :     poSrcLayer->ResetReading();
    1336        1444 :     bNeedReset = false;
    1337             : 
    1338        1444 :     return bSuccess;
    1339             : }
    1340             : 
    1341             : /************************************************************************/
    1342             : /*                           GetNextFeature()                           */
    1343             : /************************************************************************/
    1344             : 
    1345       25585 : OGRFeature *OGRVRTLayer::GetNextFeature()
    1346             : 
    1347             : {
    1348       25585 :     if (m_bEmptyResultSet)
    1349           0 :         return nullptr;
    1350       25585 :     if (!bHasFullInitialized)
    1351          40 :         FullInitialize();
    1352       25585 :     if (!poSrcLayer || poDS->GetRecursionDetected())
    1353           7 :         return nullptr;
    1354       25578 :     if (bError)
    1355          13 :         return nullptr;
    1356             : 
    1357       25565 :     if (bNeedReset)
    1358             :     {
    1359        1331 :         if (!ResetSourceReading())
    1360           2 :             return nullptr;
    1361             :     }
    1362             : 
    1363             :     for (; true;)
    1364             :     {
    1365             :         auto poSrcFeature =
    1366       25571 :             std::unique_ptr<OGRFeature>(poSrcLayer->GetNextFeature());
    1367       25571 :         if (poSrcFeature == nullptr)
    1368        1077 :             return nullptr;
    1369             : 
    1370           0 :         std::unique_ptr<OGRFeature> poFeature;
    1371       24494 :         if (poFeatureDefn.get() == GetSrcLayerDefn())
    1372             :         {
    1373        3593 :             poFeature = std::move(poSrcFeature);
    1374        3593 :             ClipAndAssignSRS(poFeature.get());
    1375             :         }
    1376             :         else
    1377             :         {
    1378       20901 :             poFeature = TranslateFeature(poSrcFeature, TRUE);
    1379             :         }
    1380             : 
    1381       24494 :         if (poFeature == nullptr)
    1382           4 :             return nullptr;
    1383             : 
    1384       24490 :         if (((m_iGeomFieldFilter < static_cast<int>(apoGeomFieldProps.size()) &&
    1385       24413 :               apoGeomFieldProps[m_iGeomFieldFilter]->eGeometryStyle ==
    1386       20556 :                   VGS_Direct) ||
    1387       20556 :              m_poFilterGeom == nullptr ||
    1388       49393 :              FilterGeometry(poFeature->GetGeomFieldRef(m_iGeomFieldFilter))) &&
    1389       24486 :             (m_poAttrQuery == nullptr ||
    1390           6 :              m_poAttrQuery->Evaluate(poFeature.get())))
    1391             :         {
    1392       24482 :             return poFeature.release();
    1393             :         }
    1394           8 :     }
    1395             : }
    1396             : 
    1397             : /************************************************************************/
    1398             : /*                          ClipAndAssignSRS()                          */
    1399             : /************************************************************************/
    1400             : 
    1401       24562 : void OGRVRTLayer::ClipAndAssignSRS(OGRFeature *poFeature)
    1402             : {
    1403       50173 :     for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    1404             :     {
    1405             :         // Clip the geometry to the SrcRegion if asked.
    1406       25611 :         OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
    1407       25611 :         if (apoGeomFieldProps[i]->poSrcRegion != nullptr &&
    1408       25611 :             apoGeomFieldProps[i]->bSrcClip && poGeom != nullptr)
    1409             :         {
    1410             :             poGeom =
    1411          39 :                 poGeom->Intersection(apoGeomFieldProps[i]->poSrcRegion.get());
    1412          39 :             if (poGeom != nullptr)
    1413          39 :                 poGeom->assignSpatialReference(
    1414          39 :                     GetLayerDefn()->GetGeomFieldDefn(i)->GetSpatialRef());
    1415             : 
    1416          39 :             poFeature->SetGeomFieldDirectly(i, poGeom);
    1417             :         }
    1418       25572 :         else if (poGeom != nullptr)
    1419       24778 :             poGeom->assignSpatialReference(
    1420       24778 :                 GetLayerDefn()->GetGeomFieldDefn(i)->GetSpatialRef());
    1421             :     }
    1422       24562 : }
    1423             : 
    1424             : /************************************************************************/
    1425             : /*                          TranslateFeature()                          */
    1426             : /*                                                                      */
    1427             : /*      Translate a source feature into a feature for this layer.       */
    1428             : /************************************************************************/
    1429             : 
    1430             : std::unique_ptr<OGRFeature>
    1431       20930 : OGRVRTLayer::TranslateFeature(std::unique_ptr<OGRFeature> &poSrcFeat,
    1432             :                               int bUseSrcRegion)
    1433             : 
    1434             : {
    1435       20930 : retry:
    1436       20930 :     auto poDstFeat = std::make_unique<OGRFeature>(poFeatureDefn.get());
    1437             : 
    1438       20930 :     m_nFeaturesRead++;
    1439             : 
    1440             :     // Handle FID.
    1441       20930 :     if (iFIDField == -1)
    1442       20902 :         poDstFeat->SetFID(poSrcFeat->GetFID());
    1443             :     else
    1444          28 :         poDstFeat->SetFID(poSrcFeat->GetFieldAsInteger64(iFIDField));
    1445             : 
    1446             :     // Handle style string.
    1447       20930 :     if (iStyleField != -1)
    1448             :     {
    1449           9 :         if (poSrcFeat->IsFieldSetAndNotNull(iStyleField))
    1450           9 :             poDstFeat->SetStyleString(poSrcFeat->GetFieldAsString(iStyleField));
    1451             :     }
    1452             :     else
    1453             :     {
    1454       20921 :         if (poSrcFeat->GetStyleString() != nullptr)
    1455           0 :             poDstFeat->SetStyleString(poSrcFeat->GetStyleString());
    1456             :     }
    1457             : 
    1458       41910 :     for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    1459             :     {
    1460             :         OGRVRTGeometryStyle eGeometryStyle =
    1461       20994 :             apoGeomFieldProps[i]->eGeometryStyle;
    1462       20994 :         int iGeomField = apoGeomFieldProps[i]->iGeomField;
    1463             : 
    1464             :         // Handle the geometry.  Eventually there will be several more
    1465             :         // supported options.
    1466       41988 :         if (eGeometryStyle == VGS_None ||
    1467       20994 :             GetLayerDefn()->GetGeomFieldDefn(i)->IsIgnored())
    1468             :         {
    1469             :             // Do nothing.
    1470             :         }
    1471       20966 :         else if (eGeometryStyle == VGS_WKT && iGeomField != -1)
    1472             :         {
    1473          63 :             const char *pszWKT = poSrcFeat->GetFieldAsString(iGeomField);
    1474             : 
    1475          63 :             if (pszWKT != nullptr)
    1476             :             {
    1477          63 :                 auto [poGeom, _] = OGRGeometryFactory::createFromWkt(pszWKT);
    1478          63 :                 if (poGeom == nullptr)
    1479           0 :                     CPLDebug("OGR_VRT", "Did not get geometry from %s", pszWKT);
    1480             : 
    1481          63 :                 poDstFeat->SetGeomField(i, std::move(poGeom));
    1482          63 :             }
    1483             :         }
    1484       20903 :         else if (eGeometryStyle == VGS_WKB && iGeomField != -1)
    1485             :         {
    1486           1 :             int nBytes = 0;
    1487           1 :             GByte *pabyWKB = nullptr;
    1488           1 :             bool bNeedFree = false;
    1489             : 
    1490           1 :             if (poSrcFeat->GetFieldDefnRef(iGeomField)->GetType() == OFTBinary)
    1491             :             {
    1492           0 :                 pabyWKB = poSrcFeat->GetFieldAsBinary(iGeomField, &nBytes);
    1493             :             }
    1494             :             else
    1495             :             {
    1496           1 :                 const char *pszWKT = poSrcFeat->GetFieldAsString(iGeomField);
    1497             : 
    1498           1 :                 pabyWKB = CPLHexToBinary(pszWKT, &nBytes);
    1499           1 :                 bNeedFree = true;
    1500             :             }
    1501             : 
    1502           1 :             if (pabyWKB != nullptr)
    1503             :             {
    1504           1 :                 OGRGeometry *poGeom = nullptr;
    1505             : 
    1506           1 :                 if (OGRGeometryFactory::createFromWkb(pabyWKB, nullptr, &poGeom,
    1507           1 :                                                       nBytes) == OGRERR_NONE)
    1508           1 :                     poDstFeat->SetGeomFieldDirectly(i, poGeom);
    1509             :             }
    1510             : 
    1511           1 :             if (bNeedFree)
    1512           1 :                 CPLFree(pabyWKB);
    1513             :         }
    1514       20902 :         else if (eGeometryStyle == VGS_Shape && iGeomField != -1)
    1515             :         {
    1516           3 :             int nBytes = 0;
    1517           3 :             GByte *pabyWKB = nullptr;
    1518           3 :             bool bNeedFree = false;
    1519             : 
    1520           3 :             if (poSrcFeat->GetFieldDefnRef(iGeomField)->GetType() == OFTBinary)
    1521             :             {
    1522           0 :                 pabyWKB = poSrcFeat->GetFieldAsBinary(iGeomField, &nBytes);
    1523             :             }
    1524             :             else
    1525             :             {
    1526           3 :                 const char *pszWKT = poSrcFeat->GetFieldAsString(iGeomField);
    1527             : 
    1528           3 :                 pabyWKB = CPLHexToBinary(pszWKT, &nBytes);
    1529           3 :                 bNeedFree = true;
    1530             :             }
    1531             : 
    1532           3 :             if (pabyWKB != nullptr)
    1533             :             {
    1534           3 :                 OGRGeometry *poGeom = nullptr;
    1535             : 
    1536           3 :                 if (OGRCreateFromShapeBin(pabyWKB, &poGeom, nBytes) ==
    1537             :                     OGRERR_NONE)
    1538           3 :                     poDstFeat->SetGeomFieldDirectly(i, poGeom);
    1539             :             }
    1540             : 
    1541           3 :             if (bNeedFree)
    1542           3 :                 CPLFree(pabyWKB);
    1543             :         }
    1544       20899 :         else if (eGeometryStyle == VGS_Direct && iGeomField != -1)
    1545             :         {
    1546         424 :             poDstFeat->SetGeomField(i, poSrcFeat->GetGeomFieldRef(iGeomField));
    1547             :         }
    1548       20475 :         else if (eGeometryStyle == VGS_PointFromColumns)
    1549             :         {
    1550       20475 :             OGRPoint *poPoint = nullptr;
    1551       20475 :             if (apoGeomFieldProps[i]->iGeomZField != -1)
    1552             :             {
    1553       61233 :                 poPoint = new OGRPoint(poSrcFeat->GetFieldAsDouble(
    1554       20411 :                                            apoGeomFieldProps[i]->iGeomXField),
    1555       40822 :                                        poSrcFeat->GetFieldAsDouble(
    1556       20411 :                                            apoGeomFieldProps[i]->iGeomYField),
    1557       40822 :                                        poSrcFeat->GetFieldAsDouble(
    1558       40822 :                                            apoGeomFieldProps[i]->iGeomZField));
    1559             :             }
    1560             :             else
    1561             :             {
    1562         192 :                 poPoint = new OGRPoint(poSrcFeat->GetFieldAsDouble(
    1563          64 :                                            apoGeomFieldProps[i]->iGeomXField),
    1564         128 :                                        poSrcFeat->GetFieldAsDouble(
    1565         128 :                                            apoGeomFieldProps[i]->iGeomYField));
    1566             :             }
    1567       20475 :             if (apoGeomFieldProps[i]->iGeomMField >= 0)
    1568             :             {
    1569           2 :                 poPoint->setM(poSrcFeat->GetFieldAsDouble(
    1570           1 :                     apoGeomFieldProps[i]->iGeomMField));
    1571             :             }
    1572       20475 :             poDstFeat->SetGeomFieldDirectly(i, poPoint);
    1573             :         }
    1574             :         else
    1575             :         {
    1576             :             // Add other options here.
    1577             :         }
    1578             : 
    1579             :         // In the non-direct case, we need to check that the geometry
    1580             :         // intersects the source region before an optional clipping.
    1581       41964 :         if (bUseSrcRegion &&
    1582       41529 :             apoGeomFieldProps[i]->eGeometryStyle != VGS_Direct &&
    1583       20535 :             apoGeomFieldProps[i]->poSrcRegion != nullptr)
    1584             :         {
    1585          24 :             OGRGeometry *poGeom = poDstFeat->GetGeomFieldRef(i);
    1586          48 :             if (poGeom != nullptr &&
    1587          24 :                 !poGeom->Intersects(apoGeomFieldProps[i]->poSrcRegion.get()))
    1588             :             {
    1589          14 :                 poDstFeat.reset();
    1590             : 
    1591             :                 // Fetch next source feature and retry translating it.
    1592          14 :                 poSrcFeat.reset(poSrcLayer->GetNextFeature());
    1593          14 :                 if (poSrcFeat == nullptr)
    1594           4 :                     return nullptr;
    1595             : 
    1596          10 :                 goto retry;
    1597             :             }
    1598             :         }
    1599             :     }
    1600             : 
    1601       20916 :     ClipAndAssignSRS(poDstFeat.get());
    1602             : 
    1603             :     // Copy fields.
    1604       83957 :     for (int iVRTField = 0; iVRTField < poFeatureDefn->GetFieldCount();
    1605             :          iVRTField++)
    1606             :     {
    1607       63041 :         if (anSrcField[iVRTField] == -1)
    1608           8 :             continue;
    1609             : 
    1610       63033 :         OGRFieldDefn *poDstDefn = poFeatureDefn->GetFieldDefn(iVRTField);
    1611             :         OGRFieldDefn *poSrcDefn =
    1612       63033 :             poSrcLayer->GetLayerDefn()->GetFieldDefn(anSrcField[iVRTField]);
    1613             : 
    1614      126030 :         if (!poSrcFeat->IsFieldSetAndNotNull(anSrcField[iVRTField]) ||
    1615       62997 :             poDstDefn->IsIgnored())
    1616          38 :             continue;
    1617             : 
    1618      125990 :         if (abDirectCopy[iVRTField] &&
    1619       62995 :             poDstDefn->GetType() == poSrcDefn->GetType())
    1620             :         {
    1621      125822 :             poDstFeat->SetField(
    1622       62911 :                 iVRTField, poSrcFeat->GetRawFieldRef(anSrcField[iVRTField]));
    1623             :         }
    1624             :         else
    1625             :         {
    1626             :             // Eventually we need to offer some more sophisticated translation
    1627             :             // options here for more esoteric types.
    1628          84 :             if (poDstDefn->GetType() == OFTReal)
    1629          32 :                 poDstFeat->SetField(iVRTField, poSrcFeat->GetFieldAsDouble(
    1630          16 :                                                    anSrcField[iVRTField]));
    1631             :             else
    1632         136 :                 poDstFeat->SetField(iVRTField, poSrcFeat->GetFieldAsString(
    1633          68 :                                                    anSrcField[iVRTField]));
    1634             :         }
    1635             :     }
    1636             : 
    1637       20916 :     return poDstFeat;
    1638             : }
    1639             : 
    1640             : /************************************************************************/
    1641             : /*                             GetFeature()                             */
    1642             : /************************************************************************/
    1643             : 
    1644          97 : OGRFeature *OGRVRTLayer::GetFeature(GIntBig nFeatureId)
    1645             : 
    1646             : {
    1647          97 :     if (!bHasFullInitialized)
    1648           3 :         FullInitialize();
    1649          97 :     if (!poSrcLayer || poDS->GetRecursionDetected())
    1650           1 :         return nullptr;
    1651             : 
    1652          96 :     bNeedReset = true;
    1653             : 
    1654             :     // If the FID is directly mapped, we can do a simple
    1655             :     // GetFeature() to get our target feature.  Otherwise we need
    1656             :     // to setup an appropriate query to get it.
    1657          96 :     std::unique_ptr<OGRFeature> poSrcFeature;
    1658          96 :     std::unique_ptr<OGRFeature> poFeature;
    1659             : 
    1660          96 :     if (iFIDField == -1)
    1661             :     {
    1662          94 :         poSrcFeature.reset(poSrcLayer->GetFeature(nFeatureId));
    1663             :     }
    1664             :     else
    1665             :     {
    1666             :         const char *pszFID =
    1667           2 :             poSrcLayer->GetLayerDefn()->GetFieldDefn(iFIDField)->GetNameRef();
    1668           2 :         char *pszFIDQuery = static_cast<char *>(CPLMalloc(strlen(pszFID) + 64));
    1669             : 
    1670           2 :         poSrcLayer->ResetReading();
    1671           2 :         snprintf(pszFIDQuery, strlen(pszFID) + 64, "%s = " CPL_FRMT_GIB, pszFID,
    1672             :                  nFeatureId);
    1673           2 :         poSrcLayer->SetSpatialFilter(nullptr);
    1674           2 :         poSrcLayer->SetAttributeFilter(pszFIDQuery);
    1675           2 :         CPLFree(pszFIDQuery);
    1676             : 
    1677           2 :         poSrcFeature.reset(poSrcLayer->GetNextFeature());
    1678             :     }
    1679             : 
    1680          96 :     if (poSrcFeature == nullptr)
    1681          24 :         return nullptr;
    1682             : 
    1683             :     // Translate feature and return it.
    1684          72 :     if (poFeatureDefn.get() == GetSrcLayerDefn())
    1685             :     {
    1686          53 :         poFeature = std::move(poSrcFeature);
    1687          53 :         ClipAndAssignSRS(poFeature.get());
    1688             :     }
    1689             :     else
    1690             :     {
    1691          19 :         poFeature = TranslateFeature(poSrcFeature, FALSE);
    1692             :     }
    1693             : 
    1694          72 :     return poFeature.release();
    1695             : }
    1696             : 
    1697             : /************************************************************************/
    1698             : /*                           SetNextByIndex()                           */
    1699             : /************************************************************************/
    1700             : 
    1701          24 : OGRErr OGRVRTLayer::SetNextByIndex(GIntBig nIndex)
    1702             : {
    1703          24 :     if (!bHasFullInitialized)
    1704           2 :         FullInitialize();
    1705          24 :     if (!poSrcLayer || poDS->GetRecursionDetected())
    1706           1 :         return OGRERR_FAILURE;
    1707             : 
    1708          23 :     if (bNeedReset)
    1709             :     {
    1710           1 :         if (!ResetSourceReading())
    1711           0 :             return OGRERR_FAILURE;
    1712             :     }
    1713             : 
    1714          23 :     if (TestCapability(OLCFastSetNextByIndex))
    1715          13 :         return poSrcLayer->SetNextByIndex(nIndex);
    1716             : 
    1717          10 :     return OGRLayer::SetNextByIndex(nIndex);
    1718             : }
    1719             : 
    1720             : /************************************************************************/
    1721             : /*               TranslateVRTFeatureToSrcFeature()                      */
    1722             : /*                                                                      */
    1723             : /*      Translate a VRT feature into a feature for the source layer     */
    1724             : /************************************************************************/
    1725             : 
    1726             : std::unique_ptr<OGRFeature>
    1727           5 : OGRVRTLayer::TranslateVRTFeatureToSrcFeature(const OGRFeature *poVRTFeature)
    1728             : {
    1729           5 :     auto poSrcFeat = std::make_unique<OGRFeature>(poSrcLayer->GetLayerDefn());
    1730             : 
    1731           5 :     poSrcFeat->SetFID(poVRTFeature->GetFID());
    1732             : 
    1733             :     // Handle style string.
    1734           5 :     if (iStyleField != -1)
    1735             :     {
    1736           1 :         if (poVRTFeature->GetStyleString() != nullptr)
    1737           0 :             poSrcFeat->SetField(iStyleField, poVRTFeature->GetStyleString());
    1738             :     }
    1739             :     else
    1740             :     {
    1741           4 :         if (poVRTFeature->GetStyleString() != nullptr)
    1742           0 :             poSrcFeat->SetStyleString(poVRTFeature->GetStyleString());
    1743             :     }
    1744             : 
    1745             :     // Handle the geometry.  Eventually there will be several more
    1746             :     // supported options.
    1747          10 :     for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    1748             :     {
    1749             :         OGRVRTGeometryStyle eGeometryStyle =
    1750           5 :             apoGeomFieldProps[i]->eGeometryStyle;
    1751           5 :         int iGeomField = apoGeomFieldProps[i]->iGeomField;
    1752             : 
    1753           5 :         if (eGeometryStyle == VGS_None)
    1754             :         {
    1755             :             // Do nothing.
    1756             :         }
    1757           5 :         else if (eGeometryStyle == VGS_WKT && iGeomField >= 0)
    1758             :         {
    1759           1 :             const OGRGeometry *poGeom = poVRTFeature->GetGeomFieldRef(i);
    1760           1 :             if (poGeom != nullptr)
    1761             :             {
    1762           1 :                 char *pszWKT = nullptr;
    1763           1 :                 if (poGeom->exportToWkt(&pszWKT) == OGRERR_NONE)
    1764             :                 {
    1765           1 :                     poSrcFeat->SetField(iGeomField, pszWKT);
    1766             :                 }
    1767           1 :                 CPLFree(pszWKT);
    1768           1 :             }
    1769             :         }
    1770           4 :         else if (eGeometryStyle == VGS_WKB && iGeomField >= 0)
    1771             :         {
    1772           1 :             const OGRGeometry *poGeom = poVRTFeature->GetGeomFieldRef(i);
    1773           1 :             if (poGeom != nullptr)
    1774             :             {
    1775           1 :                 const size_t nSize = poGeom->WkbSize();
    1776           1 :                 if (nSize >
    1777           1 :                     static_cast<size_t>(std::numeric_limits<int>::max()))
    1778             :                 {
    1779             :                 }
    1780             :                 else
    1781             :                 {
    1782             :                     GByte *pabyData =
    1783           1 :                         static_cast<GByte *>(VSI_MALLOC_VERBOSE(nSize));
    1784           2 :                     if (pabyData &&
    1785           1 :                         poGeom->exportToWkb(wkbNDR, pabyData) == OGRERR_NONE)
    1786             :                     {
    1787           1 :                         if (poSrcFeat->GetFieldDefnRef(iGeomField)->GetType() ==
    1788             :                             OFTBinary)
    1789             :                         {
    1790           0 :                             poSrcFeat->SetField(
    1791             :                                 iGeomField, static_cast<int>(nSize), pabyData);
    1792             :                         }
    1793             :                         else
    1794             :                         {
    1795           1 :                             char *pszHexWKB = CPLBinaryToHex(
    1796             :                                 static_cast<int>(nSize), pabyData);
    1797           1 :                             poSrcFeat->SetField(iGeomField, pszHexWKB);
    1798           1 :                             CPLFree(pszHexWKB);
    1799             :                         }
    1800             :                     }
    1801           1 :                     CPLFree(pabyData);
    1802             :                 }
    1803           1 :             }
    1804             :         }
    1805           3 :         else if (eGeometryStyle == VGS_Shape)
    1806             :         {
    1807           0 :             CPLDebug("OGR_VRT", "Update of VGS_Shape geometries not supported");
    1808             :         }
    1809           3 :         else if (eGeometryStyle == VGS_Direct && iGeomField >= 0)
    1810             :         {
    1811           2 :             poSrcFeat->SetGeomField(iGeomField,
    1812             :                                     poVRTFeature->GetGeomFieldRef(i));
    1813             :         }
    1814           1 :         else if (eGeometryStyle == VGS_PointFromColumns)
    1815             :         {
    1816           1 :             const OGRGeometry *poGeom = poVRTFeature->GetGeomFieldRef(i);
    1817           1 :             if (poGeom != nullptr)
    1818             :             {
    1819           1 :                 if (wkbFlatten(poGeom->getGeometryType()) != wkbPoint)
    1820             :                 {
    1821           0 :                     CPLError(CE_Warning, CPLE_NotSupported,
    1822             :                              "Cannot set a non ponctual geometry for "
    1823             :                              "PointFromColumns geometry");
    1824             :                 }
    1825             :                 else
    1826             :                 {
    1827           1 :                     auto poPoint = poGeom->toPoint();
    1828           1 :                     poSrcFeat->SetField(apoGeomFieldProps[i]->iGeomXField,
    1829             :                                         poPoint->getX());
    1830           1 :                     poSrcFeat->SetField(apoGeomFieldProps[i]->iGeomYField,
    1831             :                                         poPoint->getY());
    1832           1 :                     if (apoGeomFieldProps[i]->iGeomZField != -1)
    1833             :                     {
    1834           0 :                         poSrcFeat->SetField(apoGeomFieldProps[i]->iGeomZField,
    1835             :                                             poPoint->getZ());
    1836             :                     }
    1837           1 :                     if (apoGeomFieldProps[i]->iGeomMField != -1)
    1838             :                     {
    1839           0 :                         poSrcFeat->SetField(apoGeomFieldProps[i]->iGeomMField,
    1840             :                                             poPoint->getM());
    1841             :                     }
    1842             :                 }
    1843             :             }
    1844             :         }
    1845             :         else
    1846             :         {
    1847             :             // Add other options here.
    1848             :         }
    1849             : 
    1850           5 :         OGRGeometry *poGeom = poSrcFeat->GetGeomFieldRef(i);
    1851           5 :         if (poGeom != nullptr)
    1852           2 :             poGeom->assignSpatialReference(
    1853           2 :                 GetLayerDefn()->GetGeomFieldDefn(i)->GetSpatialRef());
    1854             :     }
    1855             : 
    1856             :     // Copy fields.
    1857          16 :     for (int iVRTField = 0; iVRTField < poFeatureDefn->GetFieldCount();
    1858             :          iVRTField++)
    1859             :     {
    1860          11 :         bool bSkip = false;
    1861          20 :         for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    1862             :         {
    1863             :             // Do not set source geometry columns. Have been set just above.
    1864          11 :             if ((apoGeomFieldProps[i]->eGeometryStyle != VGS_Direct &&
    1865           9 :                  anSrcField[iVRTField] == apoGeomFieldProps[i]->iGeomField) ||
    1866           9 :                 anSrcField[iVRTField] == apoGeomFieldProps[i]->iGeomXField ||
    1867           9 :                 anSrcField[iVRTField] == apoGeomFieldProps[i]->iGeomYField ||
    1868          31 :                 anSrcField[iVRTField] == apoGeomFieldProps[i]->iGeomZField ||
    1869           9 :                 anSrcField[iVRTField] == apoGeomFieldProps[i]->iGeomMField)
    1870             :             {
    1871           2 :                 bSkip = true;
    1872           2 :                 break;
    1873             :             }
    1874             :         }
    1875          11 :         if (bSkip)
    1876           2 :             continue;
    1877             : 
    1878           9 :         OGRFieldDefn *poVRTDefn = poFeatureDefn->GetFieldDefn(iVRTField);
    1879             :         OGRFieldDefn *poSrcDefn =
    1880           9 :             poSrcLayer->GetLayerDefn()->GetFieldDefn(anSrcField[iVRTField]);
    1881             : 
    1882          18 :         if (abDirectCopy[iVRTField] &&
    1883           9 :             poVRTDefn->GetType() == poSrcDefn->GetType())
    1884             :         {
    1885           9 :             poSrcFeat->SetField(anSrcField[iVRTField],
    1886             :                                 poVRTFeature->GetRawFieldRef(iVRTField));
    1887             :         }
    1888             :         else
    1889             :         {
    1890             :             // Eventually we need to offer some more sophisticated translation
    1891             :             // options here for more esoteric types.
    1892           0 :             poSrcFeat->SetField(anSrcField[iVRTField],
    1893             :                                 poVRTFeature->GetFieldAsString(iVRTField));
    1894             :         }
    1895             :     }
    1896             : 
    1897           5 :     return poSrcFeat;
    1898             : }
    1899             : 
    1900             : /************************************************************************/
    1901             : /*                           ICreateFeature()                           */
    1902             : /************************************************************************/
    1903             : 
    1904          11 : OGRErr OGRVRTLayer::ICreateFeature(OGRFeature *poVRTFeature)
    1905             : {
    1906          11 :     if (!bHasFullInitialized)
    1907           0 :         FullInitialize();
    1908          11 :     if (!poSrcLayer || poDS->GetRecursionDetected())
    1909           1 :         return OGRERR_FAILURE;
    1910             : 
    1911          10 :     if (!bUpdate)
    1912             :     {
    1913           2 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1914             :                  "CreateFeature");
    1915           2 :         return OGRERR_FAILURE;
    1916             :     }
    1917             : 
    1918           8 :     if (iFIDField != -1)
    1919             :     {
    1920           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1921             :                  "The CreateFeature() operation is not supported "
    1922             :                  "if the FID option is specified.");
    1923           0 :         return OGRERR_FAILURE;
    1924             :     }
    1925             : 
    1926           8 :     if (GetSrcLayerDefn() == poFeatureDefn.get())
    1927           5 :         return poSrcLayer->CreateFeature(poVRTFeature);
    1928             : 
    1929           3 :     auto poSrcFeature = TranslateVRTFeatureToSrcFeature(poVRTFeature);
    1930           3 :     poSrcFeature->SetFID(OGRNullFID);
    1931           3 :     OGRErr eErr = poSrcLayer->CreateFeature(poSrcFeature.get());
    1932           3 :     if (eErr == OGRERR_NONE)
    1933             :     {
    1934           3 :         poVRTFeature->SetFID(poSrcFeature->GetFID());
    1935             :     }
    1936           3 :     return eErr;
    1937             : }
    1938             : 
    1939             : /************************************************************************/
    1940             : /*                            ISetFeature()                             */
    1941             : /************************************************************************/
    1942             : 
    1943          16 : OGRErr OGRVRTLayer::ISetFeature(OGRFeature *poVRTFeature)
    1944             : {
    1945          16 :     if (!bHasFullInitialized)
    1946           0 :         FullInitialize();
    1947          16 :     if (!poSrcLayer || poDS->GetRecursionDetected())
    1948           1 :         return OGRERR_FAILURE;
    1949             : 
    1950          15 :     if (!bUpdate)
    1951             :     {
    1952          10 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1953             :                  "SetFeature");
    1954          10 :         return OGRERR_FAILURE;
    1955             :     }
    1956             : 
    1957           5 :     if (iFIDField != -1)
    1958             :     {
    1959           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1960             :                  "The SetFeature() operation is not supported "
    1961             :                  "if the FID option is specified.");
    1962           0 :         return OGRERR_FAILURE;
    1963             :     }
    1964             : 
    1965           5 :     if (GetSrcLayerDefn() == poFeatureDefn.get())
    1966           3 :         return poSrcLayer->SetFeature(poVRTFeature);
    1967             : 
    1968           4 :     auto poSrcFeature = TranslateVRTFeatureToSrcFeature(poVRTFeature);
    1969           2 :     return poSrcLayer->SetFeature(poSrcFeature.get());
    1970             : }
    1971             : 
    1972             : /************************************************************************/
    1973             : /*                           DeleteFeature()                            */
    1974             : /************************************************************************/
    1975             : 
    1976          20 : OGRErr OGRVRTLayer::DeleteFeature(GIntBig nFID)
    1977             : 
    1978             : {
    1979          20 :     if (!bHasFullInitialized)
    1980           2 :         FullInitialize();
    1981          20 :     if (!poSrcLayer || poDS->GetRecursionDetected())
    1982           1 :         return OGRERR_FAILURE;
    1983             : 
    1984          19 :     if (!bUpdate)
    1985             :     {
    1986          18 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1987             :                  "DeleteFeature");
    1988          18 :         return OGRERR_FAILURE;
    1989             :     }
    1990             : 
    1991           1 :     if (iFIDField != -1)
    1992             :     {
    1993           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1994             :                  "The DeleteFeature() operation is not supported "
    1995             :                  "if the FID option is specified.");
    1996           0 :         return OGRERR_FAILURE;
    1997             :     }
    1998             : 
    1999           1 :     return poSrcLayer->DeleteFeature(nFID);
    2000             : }
    2001             : 
    2002             : /************************************************************************/
    2003             : /*                         SetAttributeFilter()                         */
    2004             : /************************************************************************/
    2005             : 
    2006        1214 : OGRErr OGRVRTLayer::SetAttributeFilter(const char *pszNewQuery)
    2007             : 
    2008             : {
    2009        1214 :     if (!bHasFullInitialized)
    2010          87 :         FullInitialize();
    2011        1214 :     if (!poSrcLayer || poDS->GetRecursionDetected())
    2012          41 :         return OGRERR_FAILURE;
    2013             : 
    2014        1173 :     if (bAttrFilterPassThrough)
    2015             :     {
    2016        1165 :         CPLFree(pszAttrFilter);
    2017        1165 :         if (pszNewQuery == nullptr || strlen(pszNewQuery) == 0)
    2018         901 :             pszAttrFilter = nullptr;
    2019             :         else
    2020         264 :             pszAttrFilter = CPLStrdup(pszNewQuery);
    2021             : 
    2022        1165 :         ResetReading();
    2023        1165 :         return OGRERR_NONE;
    2024             :     }
    2025             :     else
    2026             :     {
    2027             :         // Setup m_poAttrQuery.
    2028           8 :         return OGRLayer::SetAttributeFilter(pszNewQuery);
    2029             :     }
    2030             : }
    2031             : 
    2032             : /************************************************************************/
    2033             : /*                           TestCapability()                           */
    2034             : /************************************************************************/
    2035             : 
    2036        1499 : int OGRVRTLayer::TestCapability(const char *pszCap) const
    2037             : 
    2038             : {
    2039        1499 :     if (EQUAL(pszCap, OLCFastFeatureCount) && nFeatureCount >= 0 &&
    2040           0 :         m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
    2041           0 :         return TRUE;
    2042             : 
    2043        1518 :     if (EQUAL(pszCap, OLCFastGetExtent) && apoGeomFieldProps.size() == 1 &&
    2044          19 :         apoGeomFieldProps[0]->sStaticEnvelope.IsInit())
    2045           3 :         return TRUE;
    2046             : 
    2047        1496 :     if (!bHasFullInitialized)
    2048          37 :         const_cast<OGRVRTLayer *>(this)->FullInitialize();
    2049        1496 :     if (!poSrcLayer || poDS->GetRecursionDetected())
    2050           9 :         return FALSE;
    2051             : 
    2052        1487 :     if (EQUAL(pszCap, OLCFastFeatureCount) ||
    2053        1081 :         EQUAL(pszCap, OLCFastSetNextByIndex))
    2054             :     {
    2055         429 :         if (m_poAttrQuery == nullptr)
    2056             :         {
    2057         429 :             bool bForward = true;
    2058        1270 :             for (size_t i = 0; i < apoGeomFieldProps.size(); i++)
    2059             :             {
    2060         902 :                 if (!(apoGeomFieldProps[i]->eGeometryStyle == VGS_Direct ||
    2061          39 :                       (apoGeomFieldProps[i]->poSrcRegion == nullptr &&
    2062          31 :                        m_poFilterGeom == nullptr)))
    2063             :                 {
    2064          22 :                     bForward = false;
    2065          22 :                     break;
    2066             :                 }
    2067             :             }
    2068         429 :             if (bForward)
    2069             :             {
    2070         407 :                 return poSrcLayer->TestCapability(pszCap);
    2071             :             }
    2072             :         }
    2073          22 :         return FALSE;
    2074             :     }
    2075             : 
    2076        1058 :     else if (EQUAL(pszCap, OLCFastSpatialFilter))
    2077           9 :         return apoGeomFieldProps.size() == 1 &&
    2078           8 :                apoGeomFieldProps[0]->eGeometryStyle == VGS_Direct &&
    2079          17 :                m_poAttrQuery == nullptr && poSrcLayer->TestCapability(pszCap);
    2080             : 
    2081        1049 :     else if (EQUAL(pszCap, OLCFastGetExtent))
    2082          42 :         return apoGeomFieldProps.size() == 1 &&
    2083          19 :                apoGeomFieldProps[0]->eGeometryStyle == VGS_Direct &&
    2084          38 :                m_poAttrQuery == nullptr &&
    2085          19 :                (apoGeomFieldProps[0]->poSrcRegion == nullptr ||
    2086          61 :                 apoGeomFieldProps[0]->bSrcClip) &&
    2087          61 :                poSrcLayer->TestCapability(pszCap);
    2088             : 
    2089        1007 :     else if (EQUAL(pszCap, OLCRandomRead))
    2090           6 :         return iFIDField == -1 && poSrcLayer->TestCapability(pszCap);
    2091             : 
    2092        1001 :     else if (EQUAL(pszCap, OLCSequentialWrite) ||
    2093         991 :              EQUAL(pszCap, OLCRandomWrite) || EQUAL(pszCap, OLCDeleteFeature))
    2094          28 :         return bUpdate && iFIDField == -1 && poSrcLayer->TestCapability(pszCap);
    2095             : 
    2096         973 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
    2097         146 :         return poSrcLayer->TestCapability(pszCap);
    2098             : 
    2099         827 :     else if (EQUAL(pszCap, OLCTransactions))
    2100           1 :         return bUpdate && poSrcLayer->TestCapability(pszCap);
    2101             : 
    2102         826 :     else if (EQUAL(pszCap, OLCIgnoreFields) ||
    2103         186 :              EQUAL(pszCap, OLCCurveGeometries) ||
    2104         138 :              EQUAL(pszCap, OLCZGeometries) ||
    2105         114 :              EQUAL(pszCap, OLCMeasuredGeometries))
    2106         760 :         return poSrcLayer->TestCapability(pszCap);
    2107             : 
    2108          66 :     return FALSE;
    2109             : }
    2110             : 
    2111             : /************************************************************************/
    2112             : /*                             IGetExtent()                             */
    2113             : /************************************************************************/
    2114             : 
    2115         102 : OGRErr OGRVRTLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
    2116             :                                bool bForce)
    2117             : {
    2118         102 :     if (static_cast<size_t>(iGeomField) >= apoGeomFieldProps.size())
    2119           0 :         return OGRERR_FAILURE;
    2120             : 
    2121         102 :     if (apoGeomFieldProps[iGeomField]->sStaticEnvelope.IsInit())
    2122             :     {
    2123           5 :         *psExtent = apoGeomFieldProps[iGeomField]->sStaticEnvelope;
    2124           5 :         return OGRERR_NONE;
    2125             :     }
    2126             : 
    2127          97 :     if (!bHasFullInitialized)
    2128           0 :         FullInitialize();
    2129          97 :     if (!poSrcLayer || poDS->GetRecursionDetected())
    2130           0 :         return OGRERR_FAILURE;
    2131             : 
    2132          97 :     if (apoGeomFieldProps[iGeomField]->eGeometryStyle == VGS_Direct &&
    2133         191 :         m_poAttrQuery == nullptr &&
    2134          93 :         (apoGeomFieldProps[iGeomField]->poSrcRegion == nullptr ||
    2135           1 :          apoGeomFieldProps[iGeomField]->bSrcClip))
    2136             :     {
    2137          92 :         if (bNeedReset)
    2138          32 :             ResetSourceReading();
    2139             : 
    2140          92 :         OGRErr eErr = poSrcLayer->GetExtent(
    2141          92 :             apoGeomFieldProps[iGeomField]->iGeomField, psExtent, bForce);
    2142         178 :         if (eErr != OGRERR_NONE ||
    2143          86 :             apoGeomFieldProps[iGeomField]->poSrcRegion == nullptr)
    2144          92 :             return eErr;
    2145             : 
    2146           0 :         OGREnvelope sSrcRegionEnvelope;
    2147           0 :         apoGeomFieldProps[iGeomField]->poSrcRegion->getEnvelope(
    2148           0 :             &sSrcRegionEnvelope);
    2149             : 
    2150           0 :         psExtent->Intersect(sSrcRegionEnvelope);
    2151           0 :         return eErr;
    2152             :     }
    2153             : 
    2154           5 :     return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
    2155             : }
    2156             : 
    2157             : /************************************************************************/
    2158             : /*                          GetFeatureCount()                           */
    2159             : /************************************************************************/
    2160             : 
    2161         404 : GIntBig OGRVRTLayer::GetFeatureCount(int bForce)
    2162             : 
    2163             : {
    2164         404 :     if (m_bEmptyResultSet)
    2165           0 :         return 0;
    2166         404 :     if (nFeatureCount >= 0 && m_poFilterGeom == nullptr &&
    2167           2 :         m_poAttrQuery == nullptr)
    2168             :     {
    2169           2 :         return nFeatureCount;
    2170             :     }
    2171             : 
    2172         402 :     if (!bHasFullInitialized)
    2173           5 :         FullInitialize();
    2174         402 :     if (!poSrcLayer || poDS->GetRecursionDetected())
    2175           6 :         return 0;
    2176             : 
    2177         396 :     if (TestCapability(OLCFastFeatureCount))
    2178             :     {
    2179          86 :         if (bNeedReset)
    2180          80 :             ResetSourceReading();
    2181             : 
    2182          86 :         return poSrcLayer->GetFeatureCount(bForce);
    2183             :     }
    2184             : 
    2185         310 :     return OGRLayer::GetFeatureCount(bForce);
    2186             : }
    2187             : 
    2188             : /************************************************************************/
    2189             : /*                         ISetSpatialFilter()                          */
    2190             : /************************************************************************/
    2191             : 
    2192        1147 : OGRErr OGRVRTLayer::ISetSpatialFilter(int iGeomField,
    2193             :                                       const OGRGeometry *poGeomIn)
    2194             : {
    2195        1147 :     if (!bHasFullInitialized)
    2196           2 :         FullInitialize();
    2197        1147 :     if (!poSrcLayer || poDS->GetRecursionDetected())
    2198          41 :         return OGRERR_FAILURE;
    2199             : 
    2200        2159 :     if (iGeomField >= 0 && iGeomField < GetLayerDefn()->GetGeomFieldCount() &&
    2201        1053 :         apoGeomFieldProps[iGeomField]->eGeometryStyle == VGS_Direct)
    2202        1029 :         bNeedReset = true;
    2203             : 
    2204        1106 :     m_iGeomFieldFilter = iGeomField;
    2205        1106 :     if (InstallFilter(poGeomIn))
    2206         392 :         ResetReading();
    2207        1106 :     return OGRERR_NONE;
    2208             : }
    2209             : 
    2210             : /************************************************************************/
    2211             : /*                             SyncToDisk()                             */
    2212             : /************************************************************************/
    2213             : 
    2214           3 : OGRErr OGRVRTLayer::SyncToDisk()
    2215             : {
    2216           3 :     if (!bHasFullInitialized)
    2217           2 :         FullInitialize();
    2218           3 :     if (!poSrcLayer || poDS->GetRecursionDetected())
    2219           1 :         return OGRERR_FAILURE;
    2220             : 
    2221           2 :     return poSrcLayer->SyncToDisk();
    2222             : }
    2223             : 
    2224             : /************************************************************************/
    2225             : /*                            GetLayerDefn()                            */
    2226             : /************************************************************************/
    2227             : 
    2228       67723 : const OGRFeatureDefn *OGRVRTLayer::GetLayerDefn() const
    2229             : {
    2230       67723 :     if (!bHasFullInitialized)
    2231         318 :         const_cast<OGRVRTLayer *>(this)->FullInitialize();
    2232             : 
    2233       67723 :     return poFeatureDefn.get();
    2234             : }
    2235             : 
    2236             : /************************************************************************/
    2237             : /*                            GetGeomType()                             */
    2238             : /************************************************************************/
    2239             : 
    2240          75 : OGRwkbGeometryType OGRVRTLayer::GetGeomType() const
    2241             : {
    2242          90 :     if (CPLGetXMLValue(psLTree, "GeometryType", nullptr) != nullptr ||
    2243          15 :         CPLGetXMLValue(psLTree, "GeometryField.GeometryType", nullptr) !=
    2244             :             nullptr)
    2245             :     {
    2246          62 :         if (apoGeomFieldProps.size() >= 1)
    2247          60 :             return apoGeomFieldProps[0]->eGeomType;
    2248           2 :         return wkbNone;
    2249             :     }
    2250             : 
    2251          13 :     return GetLayerDefn()->GetGeomType();
    2252             : }
    2253             : 
    2254             : /************************************************************************/
    2255             : /*                            GetFIDColumn()                            */
    2256             : /************************************************************************/
    2257             : 
    2258         215 : const char *OGRVRTLayer::GetFIDColumn() const
    2259             : {
    2260         215 :     if (!bHasFullInitialized)
    2261           6 :         const_cast<OGRVRTLayer *>(this)->FullInitialize();
    2262             : 
    2263         215 :     if (!poSrcLayer || poDS->GetRecursionDetected())
    2264          35 :         return "";
    2265             : 
    2266         180 :     return osFIDFieldName;
    2267             : }
    2268             : 
    2269             : /************************************************************************/
    2270             : /*                          StartTransaction()                          */
    2271             : /************************************************************************/
    2272             : 
    2273           3 : OGRErr OGRVRTLayer::StartTransaction()
    2274             : {
    2275           3 :     if (!bHasFullInitialized)
    2276           1 :         FullInitialize();
    2277           3 :     if (!poSrcLayer || !bUpdate || poDS->GetRecursionDetected())
    2278           1 :         return OGRERR_FAILURE;
    2279             : 
    2280           2 :     return poSrcLayer->StartTransaction();
    2281             : }
    2282             : 
    2283             : /************************************************************************/
    2284             : /*                         CommitTransaction()                          */
    2285             : /************************************************************************/
    2286             : 
    2287           2 : OGRErr OGRVRTLayer::CommitTransaction()
    2288             : {
    2289           2 :     if (!bHasFullInitialized)
    2290           1 :         FullInitialize();
    2291           2 :     if (!poSrcLayer || !bUpdate || poDS->GetRecursionDetected())
    2292           1 :         return OGRERR_FAILURE;
    2293             : 
    2294           1 :     return poSrcLayer->CommitTransaction();
    2295             : }
    2296             : 
    2297             : /************************************************************************/
    2298             : /*                        RollbackTransaction()                         */
    2299             : /************************************************************************/
    2300             : 
    2301           2 : OGRErr OGRVRTLayer::RollbackTransaction()
    2302             : {
    2303           2 :     if (!bHasFullInitialized)
    2304           1 :         FullInitialize();
    2305           2 :     if (!poSrcLayer || !bUpdate || poDS->GetRecursionDetected())
    2306           1 :         return OGRERR_FAILURE;
    2307             : 
    2308           1 :     return poSrcLayer->RollbackTransaction();
    2309             : }
    2310             : 
    2311             : /************************************************************************/
    2312             : /*                          SetIgnoredFields()                          */
    2313             : /************************************************************************/
    2314             : 
    2315        1202 : OGRErr OGRVRTLayer::SetIgnoredFields(CSLConstList papszFields)
    2316             : {
    2317        1202 :     if (!bHasFullInitialized)
    2318           1 :         FullInitialize();
    2319        1202 :     if (!poSrcLayer || poDS->GetRecursionDetected())
    2320          98 :         return OGRERR_FAILURE;
    2321             : 
    2322        1104 :     if (!poSrcLayer->TestCapability(OLCIgnoreFields))
    2323           6 :         return OGRERR_FAILURE;
    2324             : 
    2325        1098 :     OGRErr eErr = OGRLayer::SetIgnoredFields(papszFields);
    2326        1098 :     if (eErr != OGRERR_NONE)
    2327           0 :         return eErr;
    2328             : 
    2329        2196 :     CPLStringList aosFieldsSrc;
    2330             : 
    2331             :     // Translate explicitly ignored fields of VRT layers to their equivalent
    2332             :     // source fields.
    2333        2302 :     for (const char *pszFieldName : cpl::Iterate(papszFields))
    2334             :     {
    2335        1204 :         if (EQUAL(pszFieldName, "OGR_GEOMETRY") ||
    2336        1183 :             EQUAL(pszFieldName, "OGR_STYLE"))
    2337             :         {
    2338          21 :             aosFieldsSrc.AddString(pszFieldName);
    2339             :         }
    2340             :         else
    2341             :         {
    2342             :             int iVRTField =
    2343        1183 :                 GetFieldIndexCaseSensitiveFirst(GetLayerDefn(), pszFieldName);
    2344        1183 :             if (iVRTField >= 0)
    2345             :             {
    2346         799 :                 int iSrcField = anSrcField[iVRTField];
    2347         799 :                 if (iSrcField >= 0)
    2348             :                 {
    2349             :                     // If we are asked to ignore x or y for a
    2350             :                     // VGS_PointFromColumns geometry field, we must NOT pass
    2351             :                     // that order to the underlying layer.
    2352         799 :                     bool bOKToIgnore = true;
    2353        3157 :                     for (int iGeomVRTField = 0;
    2354        3157 :                          iGeomVRTField < GetLayerDefn()->GetGeomFieldCount();
    2355             :                          iGeomVRTField++)
    2356             :                     {
    2357        2360 :                         if (iSrcField ==
    2358        2360 :                                 apoGeomFieldProps[iGeomVRTField]->iGeomXField ||
    2359             :                             iSrcField ==
    2360        2359 :                                 apoGeomFieldProps[iGeomVRTField]->iGeomYField ||
    2361             :                             iSrcField ==
    2362        7077 :                                 apoGeomFieldProps[iGeomVRTField]->iGeomZField ||
    2363             :                             iSrcField ==
    2364        2358 :                                 apoGeomFieldProps[iGeomVRTField]->iGeomMField)
    2365             :                         {
    2366           2 :                             bOKToIgnore = false;
    2367           2 :                             break;
    2368             :                         }
    2369             :                     }
    2370         799 :                     if (bOKToIgnore)
    2371             :                     {
    2372             :                         const OGRFieldDefn *poSrcDefn =
    2373         797 :                             GetSrcLayerDefn()->GetFieldDefn(iSrcField);
    2374         797 :                         aosFieldsSrc.AddString(poSrcDefn->GetNameRef());
    2375             :                     }
    2376             :                 }
    2377             :             }
    2378             :             else
    2379             :             {
    2380         384 :                 iVRTField = GetLayerDefn()->GetGeomFieldIndex(pszFieldName);
    2381         768 :                 if (iVRTField >= 0 &&
    2382         384 :                     apoGeomFieldProps[iVRTField]->eGeometryStyle == VGS_Direct)
    2383             :                 {
    2384         383 :                     int iSrcField = apoGeomFieldProps[iVRTField]->iGeomField;
    2385         383 :                     if (iSrcField >= 0)
    2386             :                     {
    2387             :                         const OGRGeomFieldDefn *poSrcDefn =
    2388         383 :                             GetSrcLayerDefn()->GetGeomFieldDefn(iSrcField);
    2389         383 :                         aosFieldsSrc.AddString(poSrcDefn->GetNameRef());
    2390             :                     }
    2391             :                 }
    2392             :             }
    2393             :         }
    2394             :     }
    2395             : 
    2396             :     // Add source fields that are not referenced by VRT layer.
    2397        2196 :     std::vector<bool> abSrcFieldUsed(GetSrcLayerDefn()->GetFieldCount());
    2398        5003 :     for (int iVRTField = 0; iVRTField < GetLayerDefn()->GetFieldCount();
    2399             :          iVRTField++)
    2400             :     {
    2401        3905 :         const int iSrcField = anSrcField[iVRTField];
    2402        3905 :         if (iSrcField >= 0)
    2403        3902 :             abSrcFieldUsed[iSrcField] = true;
    2404             :     }
    2405        3073 :     for (int iVRTField = 0; iVRTField < GetLayerDefn()->GetGeomFieldCount();
    2406             :          iVRTField++)
    2407             :     {
    2408             :         OGRVRTGeometryStyle eGeometryStyle =
    2409        1975 :             apoGeomFieldProps[iVRTField]->eGeometryStyle;
    2410             :         // For a VGS_PointFromColumns geometry field, we must not ignore
    2411             :         // the fields that help building it.
    2412        1975 :         if (eGeometryStyle == VGS_PointFromColumns)
    2413             :         {
    2414          75 :             int iSrcField = apoGeomFieldProps[iVRTField]->iGeomXField;
    2415          75 :             if (iSrcField >= 0)
    2416          75 :                 abSrcFieldUsed[iSrcField] = true;
    2417          75 :             iSrcField = apoGeomFieldProps[iVRTField]->iGeomYField;
    2418          75 :             if (iSrcField >= 0)
    2419          75 :                 abSrcFieldUsed[iSrcField] = true;
    2420          75 :             iSrcField = apoGeomFieldProps[iVRTField]->iGeomZField;
    2421          75 :             if (iSrcField >= 0)
    2422          54 :                 abSrcFieldUsed[iSrcField] = true;
    2423          75 :             iSrcField = apoGeomFieldProps[iVRTField]->iGeomMField;
    2424          75 :             if (iSrcField >= 0)
    2425           1 :                 abSrcFieldUsed[iSrcField] = true;
    2426             :         }
    2427             :         // Similarly for other kinds of geometry fields.
    2428        1900 :         else if (eGeometryStyle == VGS_WKT || eGeometryStyle == VGS_WKB ||
    2429             :                  eGeometryStyle == VGS_Shape)
    2430             :         {
    2431          58 :             int iSrcField = apoGeomFieldProps[iVRTField]->iGeomField;
    2432          58 :             if (iSrcField >= 0)
    2433          58 :                 abSrcFieldUsed[iSrcField] = true;
    2434             :         }
    2435             :     }
    2436        1098 :     if (iStyleField >= 0)
    2437           3 :         abSrcFieldUsed[iStyleField] = true;
    2438        1098 :     if (iFIDField >= 0)
    2439          28 :         abSrcFieldUsed[iFIDField] = true;
    2440        5096 :     for (int iSrcField = 0; iSrcField < GetSrcLayerDefn()->GetFieldCount();
    2441             :          iSrcField++)
    2442             :     {
    2443        3998 :         if (!abSrcFieldUsed[iSrcField])
    2444             :         {
    2445             :             const OGRFieldDefn *poSrcDefn =
    2446          39 :                 GetSrcLayerDefn()->GetFieldDefn(iSrcField);
    2447          39 :             aosFieldsSrc.AddString(poSrcDefn->GetNameRef());
    2448             :         }
    2449             :     }
    2450             : 
    2451             :     // Add source geometry fields that are not referenced by VRT layer.
    2452        1098 :     abSrcFieldUsed.clear();
    2453        1098 :     abSrcFieldUsed.resize(GetSrcLayerDefn()->GetGeomFieldCount());
    2454        3073 :     for (int iVRTField = 0; iVRTField < GetLayerDefn()->GetGeomFieldCount();
    2455             :          iVRTField++)
    2456             :     {
    2457        1975 :         if (apoGeomFieldProps[iVRTField]->eGeometryStyle == VGS_Direct)
    2458             :         {
    2459        1842 :             const int iSrcField = apoGeomFieldProps[iVRTField]->iGeomField;
    2460        1842 :             if (iSrcField >= 0)
    2461        1842 :                 abSrcFieldUsed[iSrcField] = true;
    2462             :         }
    2463             :     }
    2464        3098 :     for (int iSrcField = 0; iSrcField < GetSrcLayerDefn()->GetGeomFieldCount();
    2465             :          iSrcField++)
    2466             :     {
    2467        2000 :         if (!abSrcFieldUsed[iSrcField])
    2468             :         {
    2469             :             const OGRGeomFieldDefn *poSrcDefn =
    2470         158 :                 GetSrcLayerDefn()->GetGeomFieldDefn(iSrcField);
    2471         158 :             aosFieldsSrc.AddString(poSrcDefn->GetNameRef());
    2472             :         }
    2473             :     }
    2474             : 
    2475        1098 :     return poSrcLayer->SetIgnoredFields(aosFieldsSrc.List());
    2476             : }
    2477             : 
    2478             : /************************************************************************/
    2479             : /*                           GetSrcDataset()                            */
    2480             : /************************************************************************/
    2481             : 
    2482           7 : GDALDataset *OGRVRTLayer::GetSrcDataset()
    2483             : {
    2484           7 :     if (!bHasFullInitialized)
    2485           0 :         FullInitialize();
    2486           7 :     if (!poSrcLayer || poDS->GetRecursionDetected())
    2487           0 :         return nullptr;
    2488           7 :     return poSrcDS.get();
    2489             : }

Generated by: LCOV version 1.14