LCOV - code coverage report
Current view: top level - ogr - ograpispy.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 741 843 87.9 %
Date: 2024-05-04 12:52:34 Functions: 89 92 96.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  OGR C API "Spy"
       5             :  * Author:   Even Rouault, even.rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
       9             :  *
      10             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include "cpl_port.h"
      30             : #include "cpl_multiproc.h"
      31             : #include "ograpispy.h"
      32             : 
      33             : #include <cstdio>
      34             : #include <map>
      35             : #include <set>
      36             : 
      37             : #include "cpl_string.h"
      38             : #include "gdal.h"
      39             : #include "ogr_geometry.h"
      40             : #include "ogr_feature.h"
      41             : #include "ogr_spatialref.h"
      42             : #include "ogrsf_frmts.h"
      43             : 
      44             : #ifdef OGRAPISPY_ENABLED
      45             : 
      46             : int bOGRAPISpyEnabled = FALSE;
      47             : static CPLMutex *hOGRAPISpyMutex = nullptr;
      48             : static CPLString osSnapshotPath;
      49             : static CPLString osSpyFile;
      50             : static FILE *fpSpyFile = nullptr;
      51             : 
      52             : // Keep in sync with cpl_conv.cpp
      53             : void OGRAPISPYCPLSetConfigOption(const char *, const char *);
      54             : void OGRAPISPYCPLSetThreadLocalConfigOption(const char *, const char *);
      55             : 
      56             : namespace
      57             : {
      58             : 
      59             : class LayerDescription
      60             : {
      61             :   public:
      62             :     int iLayer = -1;
      63             : 
      64          13 :     LayerDescription() = default;
      65             : 
      66          13 :     explicit LayerDescription(int iLayerIn) : iLayer(iLayerIn)
      67             :     {
      68          13 :     }
      69             : };
      70             : 
      71           9 : class DatasetDescription
      72             : {
      73             :   public:
      74             :     int iDS = -1;
      75             :     std::map<OGRLayerH, LayerDescription> oMapLayer{};
      76             : 
      77           9 :     DatasetDescription() = default;
      78             : 
      79           9 :     explicit DatasetDescription(int iDSIn) : iDS(iDSIn)
      80             :     {
      81           9 :     }
      82             : 
      83             :     DatasetDescription &operator=(DatasetDescription &&) = default;
      84             :     ~DatasetDescription();
      85             : };
      86             : 
      87           7 : class FeatureDefnDescription
      88             : {
      89             :   public:
      90             :     OGRFeatureDefnH hFDefn = nullptr;
      91             :     int iUniqueNumber = -1;
      92             :     std::map<OGRFieldDefnH, int> oMapFieldDefn{};
      93             :     std::map<OGRGeomFieldDefnH, int> oMapGeomFieldDefn{};
      94             : 
      95           7 :     FeatureDefnDescription() = default;
      96             : 
      97           7 :     FeatureDefnDescription(OGRFeatureDefnH hFDefnIn, int iUniqueNumberIn)
      98           7 :         : hFDefn(hFDefnIn), iUniqueNumber(iUniqueNumberIn)
      99             :     {
     100           7 :     }
     101             : 
     102             :     FeatureDefnDescription(const FeatureDefnDescription &) = default;
     103             :     FeatureDefnDescription &operator=(const FeatureDefnDescription &) = default;
     104             : 
     105             :     void Free();
     106             : };
     107             : 
     108             : }  // namespace
     109             : 
     110             : static std::map<GDALDatasetH, DatasetDescription> oMapDS;
     111             : static std::set<int> oSetDSIndex;
     112             : static std::map<OGRLayerH, CPLString> oGlobalMapLayer;
     113             : static OGRLayerH hLayerGetNextFeature = nullptr;
     114             : static OGRLayerH hLayerGetLayerDefn = nullptr;
     115             : static bool bDeferGetFieldCount = false;
     116             : static int nGetNextFeatureCalls = 0;
     117             : static std::set<CPLString> aoSetCreatedDS;
     118             : static std::map<OGRFeatureDefnH, FeatureDefnDescription> oMapFDefn;
     119             : static std::map<OGRGeomFieldDefnH, CPLString> oGlobalMapGeomFieldDefn;
     120             : static std::map<OGRFieldDefnH, CPLString> oGlobalMapFieldDefn;
     121             : 
     122           1 : void FeatureDefnDescription::Free()
     123             : {
     124             :     {
     125             :         std::map<OGRGeomFieldDefnH, int>::iterator oIter =
     126           1 :             oMapGeomFieldDefn.begin();
     127           1 :         for (; oIter != oMapGeomFieldDefn.end(); ++oIter)
     128           0 :             oGlobalMapGeomFieldDefn.erase(oIter->first);
     129             :     }
     130             :     {
     131           1 :         std::map<OGRFieldDefnH, int>::iterator oIter = oMapFieldDefn.begin();
     132           1 :         for (; oIter != oMapFieldDefn.end(); ++oIter)
     133           0 :             oGlobalMapFieldDefn.erase(oIter->first);
     134             :     }
     135           1 : }
     136             : 
     137          18 : DatasetDescription::~DatasetDescription()
     138             : {
     139             :     {
     140             :         std::map<OGRLayerH, LayerDescription>::iterator oIter =
     141          18 :             oMapLayer.begin();
     142          27 :         for (; oIter != oMapLayer.end(); ++oIter)
     143           9 :             oGlobalMapLayer.erase(oIter->first);
     144             :     }
     145          18 : }
     146             : 
     147         852 : void OGRAPISpyDestroyMutex()
     148             : {
     149         852 :     if (hOGRAPISpyMutex)
     150             :     {
     151           0 :         CPLDestroyMutex(hOGRAPISpyMutex);
     152           0 :         hOGRAPISpyMutex = nullptr;
     153             : 
     154           0 :         aoSetCreatedDS.clear();
     155           0 :         oMapFDefn.clear();
     156           0 :         oGlobalMapGeomFieldDefn.clear();
     157           0 :         oGlobalMapFieldDefn.clear();
     158             :     }
     159         852 : }
     160             : 
     161         196 : static void OGRAPISpyFileReopen()
     162             : {
     163         196 :     if (fpSpyFile == nullptr)
     164             :     {
     165         182 :         fpSpyFile = fopen(osSpyFile, "ab");
     166         182 :         if (fpSpyFile == nullptr)
     167           0 :             fpSpyFile = stderr;
     168             :     }
     169         196 : }
     170             : 
     171         185 : static void OGRAPISpyFileClose()
     172             : {
     173         185 :     if (fpSpyFile != stdout && fpSpyFile != stderr)
     174             :     {
     175         185 :         fclose(fpSpyFile);
     176         185 :         fpSpyFile = nullptr;
     177             :     }
     178         185 : }
     179             : 
     180       81537 : static bool OGRAPISpyEnabled()
     181             : {
     182       81537 :     if (bOGRAPISpyEnabled < 0)
     183           8 :         return false;
     184             : 
     185       81529 :     const char *pszSpyFile = CPLGetConfigOption("OGR_API_SPY_FILE", nullptr);
     186       81577 :     bOGRAPISpyEnabled = pszSpyFile != nullptr;
     187       81577 :     if (!bOGRAPISpyEnabled)
     188             :     {
     189       81529 :         osSpyFile.resize(0);
     190       81527 :         aoSetCreatedDS.clear();
     191       81427 :         return false;
     192             :     }
     193          48 :     if (!osSpyFile.empty())
     194          43 :         return true;
     195             : 
     196           6 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     197           3 :     if (!osSpyFile.empty())
     198           0 :         return true;
     199             : 
     200           3 :     osSpyFile = pszSpyFile;
     201             : 
     202             :     const char *pszSnapshotPath =
     203           3 :         CPLGetConfigOption("OGR_API_SPY_SNAPSHOT_PATH", ".");
     204           3 :     if (EQUAL(pszSnapshotPath, "NO"))
     205           0 :         osSnapshotPath = "";
     206             :     else
     207           3 :         osSnapshotPath = pszSnapshotPath;
     208             : 
     209           3 :     if (EQUAL(pszSpyFile, "stdout"))
     210           0 :         fpSpyFile = stdout;
     211           3 :     else if (EQUAL(pszSpyFile, "stderr"))
     212           0 :         fpSpyFile = stderr;
     213             :     else
     214           3 :         fpSpyFile = fopen(pszSpyFile, "wb");
     215           3 :     if (fpSpyFile == nullptr)
     216           0 :         fpSpyFile = stderr;
     217             : 
     218           3 :     fprintf(fpSpyFile,
     219             :             "# This file is generated by the OGR_API_SPY mechanism.\n");
     220           3 :     fprintf(fpSpyFile, "import os\n");
     221           3 :     fprintf(fpSpyFile, "import shutil\n");
     222           3 :     fprintf(fpSpyFile, "from osgeo import gdal\n");
     223           3 :     fprintf(fpSpyFile, "from osgeo import ogr\n");
     224           3 :     fprintf(fpSpyFile, "from osgeo import osr\n");
     225             :     // To make pyflakes happy in case it is unused later.
     226           3 :     fprintf(fpSpyFile, "os.access\n");
     227           3 :     fprintf(fpSpyFile, "shutil.copy\n");  // Same here.
     228           3 :     fprintf(fpSpyFile, "\n");
     229             : 
     230           3 :     return true;
     231             : }
     232             : 
     233          16 : static CPLString OGRAPISpyGetOptions(char **papszOptions)
     234             : {
     235          16 :     if (papszOptions == nullptr)
     236             :     {
     237          10 :         return "[]";
     238             :     }
     239             : 
     240          12 :     CPLString options = "[";
     241          14 :     for (char **papszIter = papszOptions; *papszIter != nullptr; papszIter++)
     242             :     {
     243           8 :         if (papszIter != papszOptions)
     244           2 :             options += ", ";
     245           8 :         options += "'";
     246           8 :         options += *papszIter;
     247           8 :         options += "'";
     248             :     }
     249           6 :     options += "]";
     250             : 
     251           6 :     return options;
     252             : }
     253             : 
     254          73 : static CPLString OGRAPISpyGetString(const char *pszStr)
     255             : {
     256          73 :     if (pszStr == nullptr)
     257           2 :         return "None";
     258         142 :     CPLString osRet = "'";
     259         719 :     while (*pszStr)
     260             :     {
     261         648 :         if (*pszStr == '\'')
     262           4 :             osRet += "\\'";
     263         644 :         else if (*pszStr == '\\')
     264           0 :             osRet += "\\\\";
     265             :         else
     266         644 :             osRet += *pszStr;
     267         648 :         pszStr++;
     268             :     }
     269          71 :     osRet += "'";
     270          71 :     return osRet;
     271             : }
     272             : 
     273          74 : static CPLString OGRAPISpyGetDSVar(GDALDatasetH hDS)
     274             : {
     275          74 :     if (hDS && oMapDS.find(hDS) == oMapDS.end())
     276             :     {
     277           9 :         int i = 1;
     278           9 :         while (oSetDSIndex.find(i) != oSetDSIndex.end())
     279           0 :             i++;
     280           9 :         oMapDS[hDS] = DatasetDescription(i);
     281           9 :         oSetDSIndex.insert(i);
     282             :     }
     283          74 :     return CPLSPrintf("ds%d", hDS ? oMapDS[hDS].iDS : 0);
     284             : }
     285             : 
     286          98 : static CPLString OGRAPISpyGetLayerVar(OGRLayerH hLayer)
     287             : {
     288          98 :     return oGlobalMapLayer[hLayer];
     289             : }
     290             : 
     291          17 : static CPLString OGRAPISpyGetAndRegisterLayerVar(GDALDatasetH hDS,
     292             :                                                  OGRLayerH hLayer)
     293             : {
     294          17 :     DatasetDescription &dd = oMapDS[hDS];
     295          17 :     if (hLayer && dd.oMapLayer.find(hLayer) == dd.oMapLayer.end())
     296             :     {
     297          13 :         const int i = static_cast<int>(dd.oMapLayer.size()) + 1;
     298          13 :         dd.oMapLayer[hLayer] = LayerDescription(i);
     299          13 :         oGlobalMapLayer[hLayer] =
     300          26 :             OGRAPISpyGetDSVar(hDS) + "_" + CPLSPrintf("lyr%d", i);
     301             :     }
     302             : 
     303          34 :     return OGRAPISpyGetDSVar(hDS) + "_" +
     304          51 :            CPLSPrintf("lyr%d", hLayer ? dd.oMapLayer[hLayer].iLayer : 0);
     305             : }
     306             : 
     307          10 : static CPLString OGRAPISpyGetSRS(OGRSpatialReferenceH hSpatialRef)
     308             : {
     309          10 :     if (hSpatialRef == nullptr)
     310           6 :         return "None";
     311             : 
     312           4 :     char *pszWKT = nullptr;
     313           4 :     OGRSpatialReference::FromHandle(hSpatialRef)->exportToWkt(&pszWKT);
     314             :     const char *pszRet =
     315           4 :         CPLSPrintf(R"(osr.SpatialReference("""%s"""))", pszWKT);
     316           4 :     CPLFree(pszWKT);
     317           4 :     return pszRet;
     318             : }
     319             : 
     320          12 : static CPLString OGRAPISpyGetGeom(OGRGeometryH hGeom)
     321             : {
     322          12 :     if (hGeom == nullptr)
     323           6 :         return "None";
     324             : 
     325           6 :     char *pszWKT = nullptr;
     326           6 :     OGRGeometry::FromHandle(hGeom)->exportToWkt(&pszWKT);
     327           6 :     const char *pszRet = CPLSPrintf("ogr.CreateGeometryFromWkt('%s')", pszWKT);
     328           6 :     CPLFree(pszWKT);
     329           6 :     return pszRet;
     330             : }
     331             : 
     332             : #define casePrefixOgrDot(x)                                                    \
     333             :     case x:                                                                    \
     334             :         return "ogr." #x;
     335             : 
     336          10 : static CPLString OGRAPISpyGetGeomType(OGRwkbGeometryType eType)
     337             : {
     338          10 :     switch (eType)
     339             :     {
     340          10 :         casePrefixOgrDot(wkbUnknown) casePrefixOgrDot(wkbPoint) casePrefixOgrDot(wkbLineString) casePrefixOgrDot(
     341           0 :             wkbPolygon) casePrefixOgrDot(wkbMultiPoint) casePrefixOgrDot(wkbMultiLineString)
     342           0 :             casePrefixOgrDot(wkbMultiPolygon) casePrefixOgrDot(wkbGeometryCollection) casePrefixOgrDot(
     343           0 :                 wkbCircularString) casePrefixOgrDot(wkbCompoundCurve) casePrefixOgrDot(wkbCurvePolygon)
     344           0 :                 casePrefixOgrDot(wkbMultiCurve) casePrefixOgrDot(wkbMultiSurface) casePrefixOgrDot(
     345           0 :                     wkbCurve) casePrefixOgrDot(wkbSurface) casePrefixOgrDot(wkbNone) casePrefixOgrDot(wkbLinearRing)
     346           0 :                     casePrefixOgrDot(wkbCircularStringZ) casePrefixOgrDot(wkbCompoundCurveZ) casePrefixOgrDot(
     347           0 :                         wkbCurvePolygonZ) casePrefixOgrDot(wkbMultiCurveZ) casePrefixOgrDot(wkbMultiSurfaceZ)
     348           0 :                         casePrefixOgrDot(wkbCurveZ) casePrefixOgrDot(wkbSurfaceZ) casePrefixOgrDot(
     349           0 :                             wkbPoint25D) casePrefixOgrDot(wkbLineString25D) casePrefixOgrDot(wkbPolygon25D)
     350           0 :                             casePrefixOgrDot(wkbMultiPoint25D) casePrefixOgrDot(wkbMultiLineString25D) casePrefixOgrDot(
     351           0 :                                 wkbMultiPolygon25D) casePrefixOgrDot(wkbGeometryCollection25D)
     352           0 :                                 casePrefixOgrDot(wkbPolyhedralSurface) casePrefixOgrDot(
     353           0 :                                     wkbTIN) casePrefixOgrDot(wkbTriangle) casePrefixOgrDot(wkbPolyhedralSurfaceZ)
     354           0 :                                     casePrefixOgrDot(wkbTINZ) casePrefixOgrDot(wkbTriangleZ) casePrefixOgrDot(
     355           0 :                                         wkbPointM) casePrefixOgrDot(wkbLineStringM) casePrefixOgrDot(wkbPolygonM)
     356           0 :                                         casePrefixOgrDot(wkbMultiPointM) casePrefixOgrDot(
     357           0 :                                             wkbMultiLineStringM) casePrefixOgrDot(wkbMultiPolygonM)
     358           0 :                                             casePrefixOgrDot(wkbGeometryCollectionM) casePrefixOgrDot(
     359           0 :                                                 wkbCircularStringM) casePrefixOgrDot(wkbCompoundCurveM)
     360           0 :                                                 casePrefixOgrDot(wkbCurvePolygonM) casePrefixOgrDot(
     361           0 :                                                     wkbMultiCurveM) casePrefixOgrDot(wkbMultiSurfaceM)
     362           0 :                                                     casePrefixOgrDot(wkbCurveM) casePrefixOgrDot(
     363           0 :                                                         wkbSurfaceM) casePrefixOgrDot(wkbPolyhedralSurfaceM)
     364           0 :                                                         casePrefixOgrDot(wkbTINM) casePrefixOgrDot(
     365           0 :                                                             wkbTriangleM) casePrefixOgrDot(wkbPointZM)
     366           0 :                                                             casePrefixOgrDot(wkbLineStringZM) casePrefixOgrDot(
     367           0 :                                                                 wkbPolygonZM) casePrefixOgrDot(wkbMultiPointZM)
     368           0 :                                                                 casePrefixOgrDot(wkbMultiLineStringZM) casePrefixOgrDot(
     369           0 :                                                                     wkbMultiPolygonZM) casePrefixOgrDot(wkbGeometryCollectionZM)
     370           0 :                                                                     casePrefixOgrDot(wkbCircularStringZM) casePrefixOgrDot(
     371             :                                                                         wkbCompoundCurveZM)
     372           0 :                                                                         casePrefixOgrDot(wkbCurvePolygonZM) casePrefixOgrDot(
     373             :                                                                             wkbMultiCurveZM)
     374           0 :                                                                             casePrefixOgrDot(
     375             :                                                                                 wkbMultiSurfaceZM)
     376           0 :                                                                                 casePrefixOgrDot(
     377             :                                                                                     wkbCurveZM)
     378           0 :                                                                                     casePrefixOgrDot(
     379             :                                                                                         wkbSurfaceZM)
     380           0 :                                                                                         casePrefixOgrDot(
     381             :                                                                                             wkbPolyhedralSurfaceZM)
     382           0 :                                                                                             casePrefixOgrDot(
     383             :                                                                                                 wkbTriangleZM)
     384           0 :                                                                                                 casePrefixOgrDot(
     385             :                                                                                                     wkbTINZM)
     386             :     }
     387           0 :     return "error";
     388             : }
     389             : 
     390           8 : static CPLString OGRAPISpyGetFieldType(OGRFieldType eType)
     391             : {
     392           8 :     switch (eType)
     393             :     {
     394           2 :         casePrefixOgrDot(OFTInteger) casePrefixOgrDot(OFTInteger64)
     395           0 :             casePrefixOgrDot(OFTIntegerList) casePrefixOgrDot(OFTInteger64List)
     396           2 :                 casePrefixOgrDot(OFTReal) casePrefixOgrDot(OFTRealList)
     397           4 :                     casePrefixOgrDot(OFTString) casePrefixOgrDot(OFTStringList)
     398           0 :                         casePrefixOgrDot(OFTWideString)
     399           0 :                             casePrefixOgrDot(OFTWideStringList)
     400           0 :                                 casePrefixOgrDot(OFTBinary)
     401           0 :                                     casePrefixOgrDot(OFTDate)
     402           0 :                                         casePrefixOgrDot(OFTTime)
     403           0 :                                             casePrefixOgrDot(OFTDateTime)
     404             :     }
     405           0 :     return "error";
     406             : }
     407             : 
     408             : #undef casePrefixOgrDot
     409             : 
     410          44 : static CPLString OGRAPISpyGetFeatureDefnVar(OGRFeatureDefnH hFDefn)
     411             : {
     412             :     std::map<OGRFeatureDefnH, FeatureDefnDescription>::iterator oIter =
     413          44 :         oMapFDefn.find(hFDefn);
     414          44 :     int i = 0;
     415          44 :     if (oIter == oMapFDefn.end())
     416             :     {
     417           7 :         i = static_cast<int>(oMapFDefn.size()) + 1;
     418           7 :         oMapFDefn[hFDefn] = FeatureDefnDescription(hFDefn, i);
     419             : 
     420             :         // So that we can check when they are no longer used.
     421           7 :         OGRFeatureDefn::FromHandle(hFDefn)->Reference();
     422             :     }
     423             :     else
     424             :     {
     425          37 :         i = oIter->second.iUniqueNumber;
     426             :     }
     427          88 :     return CPLSPrintf("fdefn%d", i);
     428             : }
     429             : 
     430         188 : static void OGRAPISpyFlushDefered()
     431             : {
     432         188 :     OGRAPISpyFileReopen();
     433         188 :     if (hLayerGetLayerDefn != nullptr)
     434             :     {
     435          13 :         OGRFeatureDefnH hDefn = OGRFeatureDefn::ToHandle(
     436          13 :             OGRLayer::FromHandle(hLayerGetLayerDefn)->GetLayerDefn());
     437          26 :         fprintf(fpSpyFile, "%s = %s.GetLayerDefn()\n",
     438          26 :                 OGRAPISpyGetFeatureDefnVar(hDefn).c_str(),
     439          26 :                 OGRAPISpyGetLayerVar(hLayerGetLayerDefn).c_str());
     440             : 
     441          13 :         if (bDeferGetFieldCount)
     442             :         {
     443           2 :             fprintf(fpSpyFile, "%s.GetFieldCount()\n",
     444           4 :                     OGRAPISpyGetFeatureDefnVar(hDefn).c_str());
     445           2 :             bDeferGetFieldCount = false;
     446             :         }
     447             : 
     448          13 :         hLayerGetLayerDefn = nullptr;
     449             :     }
     450             : 
     451         188 :     if (nGetNextFeatureCalls == 1)
     452             :     {
     453           2 :         fprintf(fpSpyFile, "%s.GetNextFeature()\n",
     454           4 :                 OGRAPISpyGetLayerVar(hLayerGetNextFeature).c_str());
     455           2 :         hLayerGetNextFeature = nullptr;
     456           2 :         nGetNextFeatureCalls = 0;
     457             :     }
     458         186 :     else if (nGetNextFeatureCalls > 0)
     459             :     {
     460           2 :         fprintf(fpSpyFile, "for i in range(%d):\n", nGetNextFeatureCalls);
     461           2 :         fprintf(fpSpyFile, "    %s.GetNextFeature()\n",
     462           4 :                 OGRAPISpyGetLayerVar(hLayerGetNextFeature).c_str());
     463           2 :         hLayerGetNextFeature = nullptr;
     464           2 :         nGetNextFeatureCalls = 0;
     465             :     }
     466         188 : }
     467             : 
     468       10907 : int OGRAPISpyOpenTakeSnapshot(const char *pszName, int bUpdate)
     469             : {
     470       10912 :     if (!OGRAPISpyEnabled() || !bUpdate || osSnapshotPath.empty() ||
     471       10912 :         aoSetCreatedDS.find(pszName) != aoSetCreatedDS.end())
     472       10904 :         return -1;
     473           3 :     OGRAPISpyFlushDefered();
     474             : 
     475             :     VSIStatBufL sStat;
     476           3 :     if (VSIStatL(pszName, &sStat) == 0)
     477             :     {
     478           1 :         bOGRAPISpyEnabled = -1;
     479             :         GDALDatasetH hDS =
     480           1 :             GDALOpenEx(pszName, GDAL_OF_VECTOR, nullptr, nullptr, nullptr);
     481             :         char **papszFileList =
     482           1 :             hDS ? GDALDataset::FromHandle(hDS)->GetFileList() : nullptr;
     483           1 :         GDALClose(hDS);
     484           1 :         bOGRAPISpyEnabled = true;
     485           1 :         if (papszFileList)
     486             :         {
     487           1 :             int i = 1;
     488           2 :             CPLString osBaseDir;
     489           2 :             CPLString osSrcDir;
     490           1 :             CPLString osWorkingDir;
     491             :             while (true)
     492             :             {
     493           0 :                 osBaseDir = CPLFormFilename(
     494           1 :                     osSnapshotPath, CPLSPrintf("snapshot_%d", i), nullptr);
     495           1 :                 if (VSIStatL(osBaseDir, &sStat) != 0)
     496           1 :                     break;
     497           0 :                 i++;
     498             :             }
     499           1 :             VSIMkdir(osSnapshotPath, 0777);
     500           1 :             VSIMkdir(osBaseDir, 0777);
     501           1 :             osSrcDir = CPLFormFilename(osBaseDir, "source", nullptr);
     502           1 :             VSIMkdir(osSrcDir, 0777);
     503           1 :             osWorkingDir = CPLFormFilename(osBaseDir, "working", nullptr);
     504           1 :             VSIMkdir(osWorkingDir, 0777);
     505             : 
     506           1 :             OGRAPISpyFileReopen();
     507           1 :             fprintf(fpSpyFile, "# Take snapshot of %s\n", pszName);
     508           1 :             fprintf(fpSpyFile, "try:\n");
     509           1 :             fprintf(fpSpyFile, "    shutil.rmtree('%s')\n",
     510             :                     osWorkingDir.c_str());
     511           1 :             fprintf(fpSpyFile, "except:\n");
     512           1 :             fprintf(fpSpyFile, "    pass\n");
     513           1 :             fprintf(fpSpyFile, "os.mkdir('%s')\n", osWorkingDir.c_str());
     514           4 :             for (char **papszIter = papszFileList; *papszIter; papszIter++)
     515             :             {
     516             :                 CPLString osSnapshotSrcFile = CPLFormFilename(
     517           6 :                     osSrcDir, CPLGetFilename(*papszIter), nullptr);
     518             :                 CPLString osSnapshotWorkingFile = CPLFormFilename(
     519           6 :                     osWorkingDir, CPLGetFilename(*papszIter), nullptr);
     520           3 :                 CPL_IGNORE_RET_VAL(CPLCopyFile(osSnapshotSrcFile, *papszIter));
     521           3 :                 CPL_IGNORE_RET_VAL(
     522           3 :                     CPLCopyFile(osSnapshotWorkingFile, *papszIter));
     523           3 :                 fprintf(fpSpyFile, "shutil.copy('%s', '%s')\n",
     524             :                         osSnapshotSrcFile.c_str(),
     525             :                         osSnapshotWorkingFile.c_str());
     526             :             }
     527           1 :             CSLDestroy(papszFileList);
     528           1 :             return i;
     529             :         }
     530             :     }
     531           2 :     return -1;
     532             : }
     533             : 
     534       10907 : void OGRAPISpyOpen(const char *pszName, int bUpdate, int iSnapshot,
     535             :                    GDALDatasetH *phDS)
     536             : {
     537       10907 :     if (!OGRAPISpyEnabled())
     538       10900 :         return;
     539          14 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     540           7 :     OGRAPISpyFlushDefered();
     541             : 
     542          14 :     CPLString osName;
     543           7 :     if (iSnapshot > 0)
     544             :     {
     545             :         CPLString osBaseDir = CPLFormFilename(
     546           2 :             osSnapshotPath, CPLSPrintf("snapshot_%d", iSnapshot), nullptr);
     547           2 :         CPLString osWorkingDir = CPLFormFilename(osBaseDir, "working", nullptr);
     548             :         osName =
     549           1 :             CPLFormFilename(osWorkingDir, CPLGetFilename(pszName), nullptr);
     550           1 :         pszName = osName.c_str();
     551             : 
     552           1 :         if (*phDS != nullptr)
     553             :         {
     554           1 :             bOGRAPISpyEnabled = -1;
     555           1 :             GDALClose(GDALDataset::FromHandle(*phDS));
     556           1 :             *phDS = GDALOpenEx(pszName, GDAL_OF_VECTOR | GDAL_OF_UPDATE,
     557             :                                nullptr, nullptr, nullptr);
     558           1 :             bOGRAPISpyEnabled = true;
     559             :         }
     560             :     }
     561             : 
     562           7 :     OGRAPISpyFileReopen();
     563           7 :     if (*phDS != nullptr)
     564           3 :         fprintf(fpSpyFile, "%s = ", OGRAPISpyGetDSVar(*phDS).c_str());
     565           7 :     if (bUpdate)
     566           5 :         fprintf(fpSpyFile, "gdal.OpenEx(%s, gdal.OF_VECTOR | gdal.OF_UPDATE)\n",
     567          10 :                 OGRAPISpyGetString(pszName).c_str());
     568             :     else
     569           2 :         fprintf(fpSpyFile, "gdal.OpenEx(%s, gdal.OF_VECTOR)\n",
     570           4 :                 OGRAPISpyGetString(pszName).c_str());
     571           7 :     OGRAPISpyFileClose();
     572             : }
     573             : 
     574          11 : void OGRAPISpyPreClose(GDALDatasetH hDS)
     575             : {
     576          11 :     if (!OGRAPISpyEnabled())
     577           2 :         return;
     578          18 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     579           9 :     OGRAPISpyFlushDefered();
     580           9 :     fprintf(fpSpyFile, "ds%d = None\n", oMapDS[hDS].iDS);
     581           9 :     oSetDSIndex.erase(oMapDS[hDS].iDS);
     582           9 :     oMapDS.erase(hDS);
     583           9 :     OGRAPISpyFileClose();
     584             : }
     585             : 
     586          11 : void OGRAPISpyPostClose()
     587             : {
     588             :     {
     589          11 :         if (!OGRAPISpyEnabled())
     590           2 :             return;
     591          18 :         CPLMutexHolderD(&hOGRAPISpyMutex);
     592             :         std::map<OGRFeatureDefnH, FeatureDefnDescription>::iterator oIter =
     593           9 :             oMapFDefn.begin();
     594          18 :         std::vector<OGRFeatureDefnH> oArray;
     595          20 :         for (; oIter != oMapFDefn.end(); ++oIter)
     596             :         {
     597          11 :             FeatureDefnDescription &featureDefnDescription = oIter->second;
     598          11 :             if (OGRFeatureDefn::FromHandle(featureDefnDescription.hFDefn)
     599          11 :                     ->GetReferenceCount() == 1)
     600             :             {
     601           1 :                 oArray.push_back(featureDefnDescription.hFDefn);
     602             :             }
     603             :         }
     604          10 :         for (auto &hFDefn : oArray)
     605             :         {
     606           1 :             FeatureDefnDescription &featureDefnDescription = oMapFDefn[hFDefn];
     607             :             OGRFeatureDefn::FromHandle(featureDefnDescription.hFDefn)
     608           1 :                 ->Release();
     609           1 :             featureDefnDescription.Free();
     610           1 :             oMapFDefn.erase(hFDefn);
     611             :         }
     612             :     }
     613             : }
     614             : 
     615        2713 : void OGRAPISpyCreateDataSource(GDALDriverH hDriver, const char *pszName,
     616             :                                char **papszOptions, GDALDatasetH hDS)
     617             : {
     618        2713 :     if (!OGRAPISpyEnabled())
     619        2707 :         return;
     620          12 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     621           6 :     OGRAPISpyFlushDefered();
     622           6 :     if (hDS != nullptr)
     623           6 :         fprintf(fpSpyFile, "%s = ", OGRAPISpyGetDSVar(hDS).c_str());
     624          12 :     fprintf(fpSpyFile,
     625             :             "ogr.GetDriverByName('%s').CreateDataSource(%s, options=%s)\n",
     626             :             GDALGetDriverShortName(hDriver),
     627          12 :             OGRAPISpyGetString(pszName).c_str(),
     628          12 :             OGRAPISpyGetOptions(papszOptions).c_str());
     629           6 :     if (hDS != nullptr)
     630             :     {
     631           6 :         aoSetCreatedDS.insert(pszName);
     632             :     }
     633           6 :     OGRAPISpyFileClose();
     634             : }
     635             : 
     636         538 : void OGRAPISpyDeleteDataSource(GDALDriverH hDriver, const char *pszName)
     637             : {
     638         538 :     if (!OGRAPISpyEnabled())
     639         532 :         return;
     640          12 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     641           6 :     OGRAPISpyFlushDefered();
     642           6 :     fprintf(fpSpyFile, "ogr.GetDriverByName('%s').DeleteDataSource(%s)\n",
     643             :             GDALGetDriverShortName(hDriver),
     644          12 :             OGRAPISpyGetString(pszName).c_str());
     645           6 :     aoSetCreatedDS.erase(pszName);
     646           6 :     OGRAPISpyFileClose();
     647             : }
     648             : 
     649           3 : void OGRAPISpy_DS_GetLayer(GDALDatasetH hDS, int iLayer, OGRLayerH hLayer)
     650             : {
     651           6 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     652           3 :     OGRAPISpyFlushDefered();
     653           3 :     if (hLayer != nullptr)
     654           3 :         fprintf(fpSpyFile,
     655           6 :                 "%s = ", OGRAPISpyGetAndRegisterLayerVar(hDS, hLayer).c_str());
     656           3 :     fprintf(fpSpyFile, "%s.GetLayer(%d)\n", OGRAPISpyGetDSVar(hDS).c_str(),
     657             :             iLayer);
     658           3 :     OGRAPISpyFileClose();
     659           3 : }
     660             : 
     661           2 : void OGRAPISpy_DS_GetLayerCount(GDALDatasetH hDS)
     662             : {
     663           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     664           2 :     OGRAPISpyFlushDefered();
     665           2 :     fprintf(fpSpyFile, "%s.GetLayerCount()\n", OGRAPISpyGetDSVar(hDS).c_str());
     666           2 :     OGRAPISpyFileClose();
     667           2 : }
     668             : 
     669           4 : void OGRAPISpy_DS_GetLayerByName(GDALDatasetH hDS, const char *pszLayerName,
     670             :                                  OGRLayerH hLayer)
     671             : {
     672           8 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     673           4 :     OGRAPISpyFlushDefered();
     674           4 :     if (hLayer != nullptr)
     675           2 :         fprintf(fpSpyFile,
     676           4 :                 "%s = ", OGRAPISpyGetAndRegisterLayerVar(hDS, hLayer).c_str());
     677           8 :     fprintf(fpSpyFile, "%s.GetLayerByName(%s)\n",
     678           8 :             OGRAPISpyGetDSVar(hDS).c_str(),
     679           8 :             OGRAPISpyGetString(pszLayerName).c_str());
     680           4 :     OGRAPISpyFileClose();
     681           4 : }
     682             : 
     683           4 : void OGRAPISpy_DS_ExecuteSQL(GDALDatasetH hDS, const char *pszStatement,
     684             :                              OGRGeometryH hSpatialFilter,
     685             :                              const char *pszDialect, OGRLayerH hLayer)
     686             : {
     687           8 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     688           4 :     OGRAPISpyFlushDefered();
     689           4 :     if (hLayer != nullptr)
     690           4 :         fprintf(fpSpyFile,
     691           8 :                 "%s = ", OGRAPISpyGetAndRegisterLayerVar(hDS, hLayer).c_str());
     692          16 :     fprintf(fpSpyFile, "%s.ExecuteSQL(%s, %s, %s)\n",
     693           8 :             OGRAPISpyGetDSVar(hDS).c_str(),
     694           8 :             OGRAPISpyGetString(pszStatement).c_str(),
     695           8 :             OGRAPISpyGetGeom(hSpatialFilter).c_str(),
     696           8 :             OGRAPISpyGetString(pszDialect).c_str());
     697           4 :     OGRAPISpyFileClose();
     698           4 : }
     699             : 
     700           6 : void OGRAPISpy_DS_ReleaseResultSet(GDALDatasetH hDS, OGRLayerH hLayer)
     701             : {
     702          12 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     703           6 :     OGRAPISpyFlushDefered();
     704           8 :     fprintf(fpSpyFile, "%s.ReleaseResultSet(%s)\n",
     705          12 :             OGRAPISpyGetDSVar(hDS).c_str(),
     706          12 :             (hLayer) ? OGRAPISpyGetLayerVar(hLayer).c_str() : "None");
     707             : 
     708           6 :     DatasetDescription &dd = oMapDS[hDS];
     709           6 :     dd.oMapLayer.erase(hLayer);
     710           6 :     oGlobalMapLayer.erase(hLayer);
     711             : 
     712           6 :     OGRAPISpyFileClose();
     713           6 : }
     714             : 
     715           8 : void OGRAPISpy_DS_CreateLayer(GDALDatasetH hDS, const char *pszName,
     716             :                               OGRSpatialReferenceH hSpatialRef,
     717             :                               OGRwkbGeometryType eType, char **papszOptions,
     718             :                               OGRLayerH hLayer)
     719             : {
     720          16 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     721           8 :     OGRAPISpyFlushDefered();
     722           8 :     if (hLayer != nullptr)
     723           8 :         fprintf(fpSpyFile,
     724          16 :                 "%s = ", OGRAPISpyGetAndRegisterLayerVar(hDS, hLayer).c_str());
     725          40 :     fprintf(fpSpyFile, "%s.CreateLayer(%s, srs=%s, geom_type=%s, options=%s)\n",
     726          24 :             OGRAPISpyGetDSVar(hDS).c_str(), OGRAPISpyGetString(pszName).c_str(),
     727          16 :             OGRAPISpyGetSRS(hSpatialRef).c_str(),
     728          16 :             OGRAPISpyGetGeomType(eType).c_str(),
     729          16 :             OGRAPISpyGetOptions(papszOptions).c_str());
     730           8 :     OGRAPISpyFileClose();
     731           8 : }
     732             : 
     733           2 : void OGRAPISpy_DS_DeleteLayer(GDALDatasetH hDS, int iLayer)
     734             : {
     735           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     736           2 :     OGRAPISpyFlushDefered();
     737           2 :     fprintf(fpSpyFile, "%s.DeleteLayer(%d)\n", OGRAPISpyGetDSVar(hDS).c_str(),
     738             :             iLayer);
     739             :     // Should perhaps remove from the maps.
     740           2 :     OGRAPISpyFileClose();
     741           2 : }
     742             : 
     743           2 : void OGRAPISpy_Dataset_StartTransaction(GDALDatasetH hDS, int bForce)
     744             : {
     745           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     746           2 :     OGRAPISpyFlushDefered();
     747           2 :     fprintf(fpSpyFile, "%s.StartTransaction(%d)\n",
     748           4 :             OGRAPISpyGetDSVar(hDS).c_str(), bForce);
     749           2 :     OGRAPISpyFileClose();
     750           2 : }
     751             : 
     752           2 : void OGRAPISpy_Dataset_CommitTransaction(GDALDatasetH hDS)
     753             : {
     754           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     755           2 :     OGRAPISpyFlushDefered();
     756           2 :     fprintf(fpSpyFile, "%s.CommitTransaction()\n",
     757           4 :             OGRAPISpyGetDSVar(hDS).c_str());
     758           2 :     OGRAPISpyFileClose();
     759           2 : }
     760             : 
     761           2 : void OGRAPISpy_Dataset_RollbackTransaction(GDALDatasetH hDS)
     762             : {
     763           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     764           2 :     OGRAPISpyFlushDefered();
     765           2 :     fprintf(fpSpyFile, "%s.RollbackTransaction()\n",
     766           4 :             OGRAPISpyGetDSVar(hDS).c_str());
     767           2 :     OGRAPISpyFileClose();
     768           2 : }
     769             : 
     770           2 : void OGRAPISpy_L_GetFeatureCount(OGRLayerH hLayer, int bForce)
     771             : {
     772           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     773           2 :     OGRAPISpyFlushDefered();
     774           2 :     fprintf(fpSpyFile, "%s.GetFeatureCount(force=%d)\n",
     775           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(), bForce);
     776           2 :     OGRAPISpyFileClose();
     777           2 : }
     778             : 
     779           0 : void OGRAPISpy_L_GetExtent(OGRLayerH hLayer, int bForce)
     780             : {
     781           0 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     782           0 :     OGRAPISpyFlushDefered();
     783           0 :     fprintf(fpSpyFile, "%s.GetExtent(force=%d)\n",
     784           0 :             OGRAPISpyGetLayerVar(hLayer).c_str(), bForce);
     785           0 :     OGRAPISpyFileClose();
     786           0 : }
     787             : 
     788           4 : void OGRAPISpy_L_GetExtentEx(OGRLayerH hLayer, int iGeomField, int bForce)
     789             : {
     790           8 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     791           4 :     OGRAPISpyFlushDefered();
     792           4 :     fprintf(fpSpyFile, "%s.GetExtent(geom_field=%d, force=%d)\n",
     793           8 :             OGRAPISpyGetLayerVar(hLayer).c_str(), iGeomField, bForce);
     794           4 :     OGRAPISpyFileClose();
     795           4 : }
     796             : 
     797           0 : void OGRAPISpy_L_GetExtent3D(OGRLayerH hLayer, int iGeomField, int bForce)
     798             : {
     799           0 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     800           0 :     OGRAPISpyFlushDefered();
     801           0 :     fprintf(fpSpyFile, "%s.GetExtent3D(geom_field=%d, force=%d)\n",
     802           0 :             OGRAPISpyGetLayerVar(hLayer).c_str(), iGeomField, bForce);
     803           0 :     OGRAPISpyFileClose();
     804           0 : }
     805             : 
     806           4 : void OGRAPISpy_L_SetAttributeFilter(OGRLayerH hLayer, const char *pszFilter)
     807             : {
     808           8 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     809           4 :     OGRAPISpyFlushDefered();
     810           8 :     fprintf(fpSpyFile, "%s.SetAttributeFilter(%s)\n",
     811           8 :             OGRAPISpyGetLayerVar(hLayer).c_str(),
     812           8 :             OGRAPISpyGetString(pszFilter).c_str());
     813           4 :     OGRAPISpyFileClose();
     814           4 : }
     815             : 
     816           2 : void OGRAPISpy_L_GetFeature(OGRLayerH hLayer, GIntBig nFeatureId)
     817             : {
     818           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     819           2 :     OGRAPISpyFlushDefered();
     820           2 :     fprintf(fpSpyFile, "%s.GetFeature(" CPL_FRMT_GIB ")\n",
     821           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(), nFeatureId);
     822           2 :     OGRAPISpyFileClose();
     823           2 : }
     824             : 
     825           2 : void OGRAPISpy_L_SetNextByIndex(OGRLayerH hLayer, GIntBig nIndex)
     826             : {
     827           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     828           2 :     OGRAPISpyFlushDefered();
     829           2 :     fprintf(fpSpyFile, "%s.SetNextByIndex(" CPL_FRMT_GIB ")\n",
     830           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(), nIndex);
     831           2 :     OGRAPISpyFileClose();
     832           2 : }
     833             : 
     834           8 : void OGRAPISpy_L_GetNextFeature(OGRLayerH hLayer)
     835             : {
     836           8 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     837           8 :     if (hLayerGetNextFeature != hLayer)
     838             :     {
     839           4 :         OGRAPISpyFlushDefered();
     840           4 :         OGRAPISpyFileClose();
     841             :     }
     842           8 :     hLayerGetNextFeature = hLayer;
     843           8 :     nGetNextFeatureCalls++;
     844           8 : }
     845             : 
     846           7 : static void OGRAPISpyDumpFeature(OGRFeatureH hFeat)
     847             : {
     848           7 :     OGRFeature *poFeature = OGRFeature::FromHandle(hFeat);
     849             : 
     850           7 :     fprintf(fpSpyFile, "f = ogr.Feature(%s)\n",
     851          14 :             OGRAPISpyGetFeatureDefnVar(
     852             :                 OGRFeatureDefn::ToHandle(poFeature->GetDefnRef()))
     853             :                 .c_str());
     854           7 :     if (poFeature->GetFID() != -1)
     855           2 :         fprintf(fpSpyFile, "f.SetFID(" CPL_FRMT_GIB ")\n", poFeature->GetFID());
     856          20 :     for (int i = 0; i < poFeature->GetFieldCount(); i++)
     857             :     {
     858          13 :         if (poFeature->IsFieldNull(i))
     859             :         {
     860           0 :             fprintf(fpSpyFile, "f.SetFieldNull(%d)\n", i);
     861             :         }
     862          13 :         else if (poFeature->IsFieldSet(i))
     863             :         {
     864          10 :             switch (poFeature->GetFieldDefnRef(i)->GetType())
     865             :             {
     866           4 :                 case OFTInteger:
     867           4 :                     fprintf(fpSpyFile, "f.SetField(%d, %d)\n", i,
     868             :                             poFeature->GetFieldAsInteger(i));
     869           4 :                     break;
     870           2 :                 case OFTReal:
     871           2 :                     fprintf(fpSpyFile, "%s",
     872             :                             CPLSPrintf("f.SetField(%d, %.16g)\n", i,
     873             :                                        poFeature->GetFieldAsDouble(i)));
     874           2 :                     break;
     875           4 :                 case OFTString:
     876           4 :                     fprintf(fpSpyFile, "f.SetField(%d, %s)\n", i,
     877           8 :                             OGRAPISpyGetString(poFeature->GetFieldAsString(i))
     878             :                                 .c_str());
     879           4 :                     break;
     880           0 :                 default:
     881           0 :                     fprintf(fpSpyFile, "f.SetField(%d, %s) #FIXME\n", i,
     882           0 :                             OGRAPISpyGetString(poFeature->GetFieldAsString(i))
     883             :                                 .c_str());
     884           0 :                     break;
     885             :             }
     886             :         }
     887             :     }
     888          14 :     for (int i = 0; i < poFeature->GetGeomFieldCount(); i++)
     889             :     {
     890           7 :         OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
     891           7 :         if (poGeom != nullptr)
     892             :         {
     893           2 :             fprintf(fpSpyFile, "f.SetGeomField(%d, %s)\n", i,
     894           4 :                     OGRAPISpyGetGeom(OGRGeometry::ToHandle(poGeom)).c_str());
     895             :         }
     896             :     }
     897           7 :     const char *pszStyleString = poFeature->GetStyleString();
     898           7 :     if (pszStyleString != nullptr)
     899           2 :         fprintf(fpSpyFile, "f.SetStyleString(%s)\n",
     900           4 :                 OGRAPISpyGetString(pszStyleString).c_str());
     901           7 : }
     902             : 
     903           2 : void OGRAPISpy_L_SetFeature(OGRLayerH hLayer, OGRFeatureH hFeat)
     904             : {
     905           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     906           2 :     OGRAPISpyFlushDefered();
     907           2 :     OGRAPISpyDumpFeature(hFeat);
     908           2 :     fprintf(fpSpyFile, "%s.SetFeature(f)\n",
     909           4 :             OGRAPISpyGetLayerVar(hLayer).c_str());
     910             :     // In case layer defn is changed afterwards.
     911           2 :     fprintf(fpSpyFile, "f = None\n");
     912           2 :     OGRAPISpyFileClose();
     913           2 : }
     914             : 
     915           5 : void OGRAPISpy_L_CreateFeature(OGRLayerH hLayer, OGRFeatureH hFeat)
     916             : {
     917          10 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     918           5 :     OGRAPISpyFlushDefered();
     919           5 :     OGRAPISpyDumpFeature(hFeat);
     920           5 :     fprintf(fpSpyFile, "%s.CreateFeature(f)\n",
     921          10 :             OGRAPISpyGetLayerVar(hLayer).c_str());
     922             :     // In case layer defn is changed afterwards.
     923           5 :     fprintf(fpSpyFile, "f = None\n");
     924           5 :     OGRAPISpyFileClose();
     925           5 : }
     926             : 
     927           0 : void OGRAPISpy_L_UpsertFeature(OGRLayerH hLayer, OGRFeatureH hFeat)
     928             : {
     929           0 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     930           0 :     OGRAPISpyFlushDefered();
     931           0 :     OGRAPISpyDumpFeature(hFeat);
     932           0 :     fprintf(fpSpyFile, "%s.UpsertFeature(f)\n",
     933           0 :             OGRAPISpyGetLayerVar(hLayer).c_str());
     934             :     // In case layer defn is changed afterwards.
     935           0 :     fprintf(fpSpyFile, "f = None\n");
     936           0 :     OGRAPISpyFileClose();
     937           0 : }
     938             : 
     939           8 : static void OGRAPISpyDumpFieldDefn(OGRFieldDefn *poFieldDefn)
     940             : {
     941          16 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     942          16 :     fprintf(fpSpyFile, "fd = ogr.FieldDefn(%s, %s)\n",
     943          16 :             OGRAPISpyGetString(poFieldDefn->GetNameRef()).c_str(),
     944          16 :             OGRAPISpyGetFieldType(poFieldDefn->GetType()).c_str());
     945           8 :     if (poFieldDefn->GetWidth() > 0)
     946           2 :         fprintf(fpSpyFile, "fd.SetWidth(%d)\n", poFieldDefn->GetWidth());
     947           8 :     if (poFieldDefn->GetPrecision() > 0)
     948           2 :         fprintf(fpSpyFile, "fd.SetPrecision(%d)\n",
     949             :                 poFieldDefn->GetPrecision());
     950           8 :     if (!poFieldDefn->IsNullable())
     951           2 :         fprintf(fpSpyFile, "fd.SetNullable(0)\n");
     952           8 :     if (poFieldDefn->GetDefault() != nullptr)
     953           2 :         fprintf(fpSpyFile, "fd.SetDefault(%s)\n",
     954           4 :                 OGRAPISpyGetString(poFieldDefn->GetDefault()).c_str());
     955           8 : }
     956             : 
     957           6 : void OGRAPISpy_L_CreateField(OGRLayerH hLayer, OGRFieldDefnH hField,
     958             :                              int bApproxOK)
     959             : {
     960          12 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     961           6 :     OGRAPISpyFlushDefered();
     962           6 :     OGRFieldDefn *poFieldDefn = OGRFieldDefn::FromHandle(hField);
     963           6 :     OGRAPISpyDumpFieldDefn(poFieldDefn);
     964           6 :     fprintf(fpSpyFile, "%s.CreateField(fd, approx_ok=%d)\n",
     965          12 :             OGRAPISpyGetLayerVar(hLayer).c_str(), bApproxOK);
     966           6 :     OGRAPISpyFileClose();
     967           6 : }
     968             : 
     969           2 : void OGRAPISpy_L_DeleteField(OGRLayerH hLayer, int iField)
     970             : {
     971           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     972           2 :     OGRAPISpyFlushDefered();
     973           2 :     fprintf(fpSpyFile, "%s.DeleteField(%d)\n",
     974           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(), iField);
     975           2 :     OGRAPISpyFileClose();
     976           2 : }
     977             : 
     978           2 : void OGRAPISpy_L_ReorderFields(OGRLayerH hLayer, int *panMap)
     979             : {
     980           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     981           2 :     OGRAPISpyFlushDefered();
     982           2 :     OGRLayer *poLayer = OGRLayer::FromHandle(hLayer);
     983           2 :     fprintf(fpSpyFile, "%s.ReorderFields([",
     984           4 :             OGRAPISpyGetLayerVar(hLayer).c_str());
     985           8 :     for (int i = 0; i < poLayer->GetLayerDefn()->GetFieldCount(); i++)
     986             :     {
     987           6 :         if (i > 0)
     988           4 :             fprintf(fpSpyFile, ", ");
     989           6 :         fprintf(fpSpyFile, "%d", panMap[i]);
     990             :     }
     991           2 :     fprintf(fpSpyFile, "])\n");
     992           2 :     OGRAPISpyFileClose();
     993           2 : }
     994             : 
     995           2 : void OGRAPISpy_L_ReorderField(OGRLayerH hLayer, int iOldFieldPos,
     996             :                               int iNewFieldPos)
     997             : {
     998           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     999           2 :     OGRAPISpyFlushDefered();
    1000           2 :     fprintf(fpSpyFile, "%s.ReorderField(%d, %d)\n",
    1001           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(), iOldFieldPos, iNewFieldPos);
    1002           2 :     OGRAPISpyFileClose();
    1003           2 : }
    1004             : 
    1005           2 : void OGRAPISpy_L_AlterFieldDefn(OGRLayerH hLayer, int iField,
    1006             :                                 OGRFieldDefnH hNewFieldDefn, int nFlags)
    1007             : {
    1008           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1009           2 :     OGRAPISpyFlushDefered();
    1010           2 :     OGRFieldDefn *poFieldDefn = OGRFieldDefn::FromHandle(hNewFieldDefn);
    1011           2 :     OGRAPISpyDumpFieldDefn(poFieldDefn);
    1012           2 :     fprintf(fpSpyFile, "%s.AlterFieldDefn(%d, fd, %d)\n",
    1013           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(), iField, nFlags);
    1014           2 :     OGRAPISpyFileClose();
    1015           2 : }
    1016             : 
    1017           2 : void OGRAPISpy_L_CreateGeomField(OGRLayerH hLayer, OGRGeomFieldDefnH hField,
    1018             :                                  int bApproxOK)
    1019             : {
    1020           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1021           2 :     OGRAPISpyFlushDefered();
    1022           2 :     OGRGeomFieldDefn *poGeomFieldDefn = OGRGeomFieldDefn::FromHandle(hField);
    1023             : 
    1024           4 :     fprintf(fpSpyFile, "geom_fd = ogr.GeomFieldDefn(%s, %s)\n",
    1025           4 :             OGRAPISpyGetString(poGeomFieldDefn->GetNameRef()).c_str(),
    1026           4 :             OGRAPISpyGetGeomType(poGeomFieldDefn->GetType()).c_str());
    1027           2 :     if (poGeomFieldDefn->GetSpatialRef() != nullptr)
    1028           2 :         fprintf(fpSpyFile, "geom_fd.SetSpatialRef(%s)\n",
    1029           4 :                 OGRAPISpyGetSRS(OGRSpatialReference::ToHandle(
    1030             :                                     const_cast<OGRSpatialReference *>(
    1031           2 :                                         poGeomFieldDefn->GetSpatialRef())))
    1032             :                     .c_str());
    1033           2 :     if (!poGeomFieldDefn->IsNullable())
    1034           2 :         fprintf(fpSpyFile, "geom_fd.SetNullable(0)\n");
    1035           2 :     fprintf(fpSpyFile, "%s.CreateGeomField(geom_fd, approx_ok=%d)\n",
    1036           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(), bApproxOK);
    1037           2 :     OGRAPISpyFileClose();
    1038           2 : }
    1039             : 
    1040          22 : static void OGRAPISpy_L_Op(OGRLayerH hLayer, const char *pszMethod)
    1041             : {
    1042          44 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1043          22 :     OGRAPISpyFlushDefered();
    1044          22 :     fprintf(fpSpyFile, "%s.%s()\n", OGRAPISpyGetLayerVar(hLayer).c_str(),
    1045             :             pszMethod);
    1046          22 :     OGRAPISpyFileClose();
    1047          22 : }
    1048             : 
    1049           2 : void OGRAPISpy_L_StartTransaction(OGRLayerH hLayer)
    1050             : {
    1051           2 :     OGRAPISpy_L_Op(hLayer, "StartTransaction");
    1052           2 : }
    1053             : 
    1054           2 : void OGRAPISpy_L_CommitTransaction(OGRLayerH hLayer)
    1055             : {
    1056           2 :     OGRAPISpy_L_Op(hLayer, "CommitTransaction");
    1057           2 : }
    1058             : 
    1059           2 : void OGRAPISpy_L_RollbackTransaction(OGRLayerH hLayer)
    1060             : {
    1061           2 :     OGRAPISpy_L_Op(hLayer, "RollbackTransaction");
    1062           2 : }
    1063             : 
    1064          15 : void OGRAPISpy_L_GetLayerDefn(OGRLayerH hLayer)
    1065             : {
    1066          15 :     if (hLayer != hLayerGetLayerDefn)
    1067             :     {
    1068          13 :         OGRAPISpyFlushDefered();
    1069          13 :         hLayerGetLayerDefn = hLayer;
    1070          13 :         OGRAPISpyFileClose();
    1071             :     }
    1072          15 : }
    1073             : 
    1074           2 : void OGRAPISpy_L_GetSpatialRef(OGRLayerH hLayer)
    1075             : {
    1076           2 :     OGRAPISpy_L_Op(hLayer, "GetSpatialRef");
    1077           2 : }
    1078             : 
    1079           2 : void OGRAPISpy_L_GetSpatialFilter(OGRLayerH hLayer)
    1080             : {
    1081           2 :     OGRAPISpy_L_Op(hLayer, "GetSpatialFilter");
    1082           2 : }
    1083             : 
    1084           2 : void OGRAPISpy_L_ResetReading(OGRLayerH hLayer)
    1085             : {
    1086           2 :     OGRAPISpy_L_Op(hLayer, "ResetReading");
    1087           2 : }
    1088             : 
    1089           2 : void OGRAPISpy_L_SyncToDisk(OGRLayerH hLayer)
    1090             : {
    1091           2 :     OGRAPISpy_L_Op(hLayer, "SyncToDisk");
    1092           2 : }
    1093             : 
    1094           2 : void OGRAPISpy_L_GetFIDColumn(OGRLayerH hLayer)
    1095             : {
    1096           2 :     OGRAPISpy_L_Op(hLayer, "GetFIDColumn");
    1097           2 : }
    1098             : 
    1099           2 : void OGRAPISpy_L_GetGeometryColumn(OGRLayerH hLayer)
    1100             : {
    1101           2 :     OGRAPISpy_L_Op(hLayer, "GetGeometryColumn");
    1102           2 : }
    1103             : 
    1104           2 : void OGRAPISpy_L_GetName(OGRLayerH hLayer)
    1105             : {
    1106           2 :     OGRAPISpy_L_Op(hLayer, "GetName");
    1107           2 : }
    1108             : 
    1109           2 : void OGRAPISpy_L_GetGeomType(OGRLayerH hLayer)
    1110             : {
    1111           2 :     OGRAPISpy_L_Op(hLayer, "GetGeomType");
    1112           2 : }
    1113             : 
    1114           2 : void OGRAPISpy_L_FindFieldIndex(OGRLayerH hLayer, const char *pszFieldName,
    1115             :                                 int bExactMatch)
    1116             : {
    1117           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1118           2 :     OGRAPISpyFlushDefered();
    1119           4 :     fprintf(fpSpyFile, "%s.FindFieldIndex(%s, %d)\n",
    1120           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(),
    1121           4 :             OGRAPISpyGetString(pszFieldName).c_str(), bExactMatch);
    1122           2 :     OGRAPISpyFileClose();
    1123           2 : }
    1124             : 
    1125           2 : void OGRAPISpy_L_TestCapability(OGRLayerH hLayer, const char *pszCap)
    1126             : {
    1127           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1128           2 :     OGRAPISpyFlushDefered();
    1129           4 :     fprintf(fpSpyFile, "%s.TestCapability(%s)\n",
    1130           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(),
    1131           4 :             OGRAPISpyGetString(pszCap).c_str());
    1132           2 :     OGRAPISpyFileClose();
    1133           2 : }
    1134             : 
    1135           4 : void OGRAPISpy_L_SetSpatialFilter(OGRLayerH hLayer, OGRGeometryH hGeom)
    1136             : {
    1137           8 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1138           4 :     OGRAPISpyFlushDefered();
    1139           8 :     fprintf(fpSpyFile, "%s.SetSpatialFilter(%s)\n",
    1140           8 :             OGRAPISpyGetLayerVar(hLayer).c_str(),
    1141           8 :             OGRAPISpyGetGeom(hGeom).c_str());
    1142           4 :     OGRAPISpyFileClose();
    1143           4 : }
    1144             : 
    1145           2 : void OGRAPISpy_L_SetSpatialFilterEx(OGRLayerH hLayer, int iGeomField,
    1146             :                                     OGRGeometryH hGeom)
    1147             : {
    1148           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1149           2 :     OGRAPISpyFlushDefered();
    1150           4 :     fprintf(fpSpyFile, "%s.SetSpatialFilter(%d, %s)\n",
    1151           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(), iGeomField,
    1152           4 :             OGRAPISpyGetGeom(hGeom).c_str());
    1153           2 :     OGRAPISpyFileClose();
    1154           2 : }
    1155             : 
    1156           2 : void OGRAPISpy_L_SetSpatialFilterRect(OGRLayerH hLayer, double dfMinX,
    1157             :                                       double dfMinY, double dfMaxX,
    1158             :                                       double dfMaxY)
    1159             : {
    1160           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1161           2 :     OGRAPISpyFlushDefered();
    1162           2 :     fprintf(fpSpyFile, "%s",
    1163             :             CPLSPrintf("%s.SetSpatialFilterRect(%.16g, %.16g, %.16g, %.16g)\n",
    1164           4 :                        OGRAPISpyGetLayerVar(hLayer).c_str(), dfMinX, dfMinY,
    1165             :                        dfMaxX, dfMaxY));
    1166           2 :     OGRAPISpyFileClose();
    1167           2 : }
    1168             : 
    1169           2 : void OGRAPISpy_L_SetSpatialFilterRectEx(OGRLayerH hLayer, int iGeomField,
    1170             :                                         double dfMinX, double dfMinY,
    1171             :                                         double dfMaxX, double dfMaxY)
    1172             : {
    1173           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1174           2 :     OGRAPISpyFlushDefered();
    1175           2 :     fprintf(fpSpyFile, "%s",
    1176             :             CPLSPrintf("%s.SetSpatialFilterRect(%d, "
    1177             :                        "%.16g, %.16g, %.16g, %.16g)\n",
    1178           4 :                        OGRAPISpyGetLayerVar(hLayer).c_str(), iGeomField, dfMinX,
    1179             :                        dfMinY, dfMaxX, dfMaxY));
    1180           2 :     OGRAPISpyFileClose();
    1181           2 : }
    1182             : 
    1183           2 : void OGRAPISpy_L_DeleteFeature(OGRLayerH hLayer, GIntBig nFID)
    1184             : {
    1185           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1186           2 :     OGRAPISpyFlushDefered();
    1187           2 :     fprintf(fpSpyFile, "%s.DeleteFeature(" CPL_FRMT_GIB ")\n",
    1188           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(), nFID);
    1189           2 :     OGRAPISpyFileClose();
    1190           2 : }
    1191             : 
    1192           2 : void OGRAPISpy_L_SetIgnoredFields(OGRLayerH hLayer,
    1193             :                                   const char **papszIgnoredFields)
    1194             : {
    1195           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1196           2 :     OGRAPISpyFlushDefered();
    1197           4 :     fprintf(
    1198             :         fpSpyFile, "%s.SetIgnoredFields(%s)\n",
    1199           4 :         OGRAPISpyGetLayerVar(hLayer).c_str(),
    1200           4 :         OGRAPISpyGetOptions(const_cast<char **>(papszIgnoredFields)).c_str());
    1201           2 :     OGRAPISpyFileClose();
    1202           2 : }
    1203             : 
    1204           2 : void OGRAPISpy_FD_GetGeomType(OGRFeatureDefnH hDefn)
    1205             : {
    1206           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1207           2 :     OGRAPISpyFlushDefered();
    1208           2 :     fprintf(fpSpyFile, "%s.GetGeomType()\n",
    1209           4 :             OGRAPISpyGetFeatureDefnVar(hDefn).c_str());
    1210           2 :     OGRAPISpyFileClose();
    1211           2 : }
    1212             : 
    1213           6 : void OGRAPISpy_FD_GetFieldCount(OGRFeatureDefnH hDefn)
    1214             : {
    1215          12 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1216          10 :     if (hLayerGetLayerDefn != nullptr &&
    1217           4 :         OGRFeatureDefn::ToHandle(
    1218           4 :             OGRLayer::FromHandle(hLayerGetLayerDefn)->GetLayerDefn()) == hDefn)
    1219             :     {
    1220           4 :         bDeferGetFieldCount = true;
    1221             :     }
    1222             :     else
    1223             :     {
    1224           2 :         OGRAPISpyFlushDefered();
    1225           2 :         fprintf(fpSpyFile, "%s.GetFieldCount()\n",
    1226           4 :                 OGRAPISpyGetFeatureDefnVar(hDefn).c_str());
    1227           2 :         OGRAPISpyFileClose();
    1228             :     }
    1229           6 : }
    1230             : 
    1231           2 : void OGRAPISpy_FD_GetFieldDefn(OGRFeatureDefnH hDefn, int iField,
    1232             :                                OGRFieldDefnH hField)
    1233             : {
    1234           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1235           2 :     OGRAPISpyFlushDefered();
    1236           4 :     fprintf(fpSpyFile, "%s_fielddefn%d = %s.GetFieldDefn(%d)\n",
    1237           4 :             OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), iField,
    1238           4 :             OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), iField);
    1239             : 
    1240             :     std::map<OGRFieldDefnH, CPLString>::iterator oIter =
    1241           2 :         oGlobalMapFieldDefn.find(hField);
    1242           2 :     if (oIter == oGlobalMapFieldDefn.end())
    1243             :     {
    1244           2 :         oMapFDefn[hDefn].oMapFieldDefn[hField] = iField;
    1245           2 :         oGlobalMapFieldDefn[hField] =
    1246             :             CPLSPrintf("%s_fielddefn%d",
    1247           4 :                        OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), iField);
    1248             :     }
    1249             : 
    1250           2 :     OGRAPISpyFileClose();
    1251           2 : }
    1252             : 
    1253           2 : void OGRAPISpy_FD_GetFieldIndex(OGRFeatureDefnH hDefn, const char *pszFieldName)
    1254             : {
    1255           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1256           2 :     OGRAPISpyFlushDefered();
    1257           4 :     fprintf(fpSpyFile, "%s.GetFieldIndex(%s)\n",
    1258           4 :             OGRAPISpyGetFeatureDefnVar(hDefn).c_str(),
    1259           4 :             OGRAPISpyGetString(pszFieldName).c_str());
    1260           2 :     OGRAPISpyFileClose();
    1261           2 : }
    1262             : 
    1263           6 : void OGRAPISpy_Fld_GetXXXX(OGRFieldDefnH hField, const char *pszOp)
    1264             : {
    1265          12 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1266           6 :     OGRAPISpyFlushDefered();
    1267           6 :     fprintf(fpSpyFile, "%s.%s()\n", oGlobalMapFieldDefn[hField].c_str(), pszOp);
    1268           6 :     OGRAPISpyFileClose();
    1269           6 : }
    1270             : 
    1271           2 : void OGRAPISpy_FD_GetGeomFieldCount(OGRFeatureDefnH hDefn)
    1272             : {
    1273           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1274           2 :     OGRAPISpyFlushDefered();
    1275           2 :     fprintf(fpSpyFile, "%s.GetGeomFieldCount()\n",
    1276           4 :             OGRAPISpyGetFeatureDefnVar(hDefn).c_str());
    1277           2 :     OGRAPISpyFileClose();
    1278           2 : }
    1279             : 
    1280           2 : void OGRAPISpy_FD_GetGeomFieldDefn(OGRFeatureDefnH hDefn, int iGeomField,
    1281             :                                    OGRGeomFieldDefnH hGeomField)
    1282             : {
    1283           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1284           2 :     OGRAPISpyFlushDefered();
    1285           4 :     fprintf(fpSpyFile, "%s_geomfielddefn%d = %s.GetGeomFieldDefn(%d)\n",
    1286           4 :             OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), iGeomField,
    1287           4 :             OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), iGeomField);
    1288             : 
    1289             :     std::map<OGRGeomFieldDefnH, CPLString>::iterator oIter =
    1290           2 :         oGlobalMapGeomFieldDefn.find(hGeomField);
    1291           2 :     if (oIter == oGlobalMapGeomFieldDefn.end())
    1292             :     {
    1293           2 :         oMapFDefn[hDefn].oMapGeomFieldDefn[hGeomField] = iGeomField;
    1294           2 :         oGlobalMapGeomFieldDefn[hGeomField] =
    1295             :             CPLSPrintf("%s_geomfielddefn%d",
    1296           4 :                        OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), iGeomField);
    1297             :     }
    1298             : 
    1299           2 :     OGRAPISpyFileClose();
    1300           2 : }
    1301             : 
    1302           2 : void OGRAPISpy_FD_GetGeomFieldIndex(OGRFeatureDefnH hDefn,
    1303             :                                     const char *pszFieldName)
    1304             : {
    1305           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1306           2 :     OGRAPISpyFlushDefered();
    1307           4 :     fprintf(fpSpyFile, "%s.GetGeomFieldIndex(%s)\n",
    1308           4 :             OGRAPISpyGetFeatureDefnVar(hDefn).c_str(),
    1309           4 :             OGRAPISpyGetString(pszFieldName).c_str());
    1310           2 :     OGRAPISpyFileClose();
    1311           2 : }
    1312             : 
    1313           6 : void OGRAPISpy_GFld_GetXXXX(OGRGeomFieldDefnH hGeomField, const char *pszOp)
    1314             : {
    1315          12 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1316           6 :     OGRAPISpyFlushDefered();
    1317           6 :     fprintf(fpSpyFile, "%s.%s()\n", oGlobalMapGeomFieldDefn[hGeomField].c_str(),
    1318             :             pszOp);
    1319           6 :     OGRAPISpyFileClose();
    1320           6 : }
    1321             : 
    1322        3911 : void OGRAPISPYCPLSetConfigOption(const char *pszKey, const char *pszValue)
    1323             : {
    1324        3911 :     if (STARTS_WITH(pszKey, "OGR_API_SPY_") || STARTS_WITH(pszKey, "__"))
    1325           0 :         return;
    1326        3911 :     if (!OGRAPISpyEnabled())
    1327        3909 :         return;
    1328           2 :     OGRAPISpyFlushDefered();
    1329           2 :     if (pszValue)
    1330             :     {
    1331           4 :         fprintf(fpSpyFile, "gdal.SetConfigOption(%s, %s)\n",
    1332           4 :                 OGRAPISpyGetString(pszKey).c_str(),
    1333           4 :                 OGRAPISpyGetString(pszValue).c_str());
    1334             :     }
    1335             :     else
    1336             :     {
    1337           0 :         fprintf(fpSpyFile, "gdal.SetConfigOption(%s, None)\n",
    1338           0 :                 OGRAPISpyGetString(pszKey).c_str());
    1339             :     }
    1340           2 :     OGRAPISpyFileClose();
    1341             : }
    1342             : 
    1343       52573 : void OGRAPISPYCPLSetThreadLocalConfigOption(const char *pszKey,
    1344             :                                             const char *pszValue)
    1345             : {
    1346       52573 :     if (STARTS_WITH(pszKey, "OGR_API_SPY_") || STARTS_WITH(pszKey, "__"))
    1347          21 :         return;
    1348       52552 :     if (!OGRAPISpyEnabled())
    1349       52475 :         return;
    1350          14 :     OGRAPISpyFlushDefered();
    1351           0 :     if (pszValue)
    1352             :     {
    1353           0 :         fprintf(fpSpyFile,
    1354             :                 "gdal.SetConfigOption(%s, %s) # SetThreadLocalConfigOption "
    1355             :                 "actually\n",
    1356           0 :                 OGRAPISpyGetString(pszKey).c_str(),
    1357           0 :                 OGRAPISpyGetString(pszValue).c_str());
    1358             :     }
    1359             :     else
    1360             :     {
    1361           0 :         fprintf(fpSpyFile,
    1362             :                 "gdal.SetConfigOption(%s, None) # SetThreadLocalConfigOption "
    1363             :                 "actually\n",
    1364           0 :                 OGRAPISpyGetString(pszKey).c_str());
    1365             :     }
    1366           0 :     OGRAPISpyFileClose();
    1367             : }
    1368             : 
    1369             : #endif  // OGRAPISPY_ENABLED

Generated by: LCOV version 1.14