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

Generated by: LCOV version 1.14