LCOV - code coverage report
Current view: top level - ogr - ogr_schema_override.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 168 195 86.2 %
Date: 2025-10-21 22:35:35 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * Project:  OGR_SCHEMA open options handling
       3             :  * Purpose:  Class for representing a layer schema override.
       4             :  * Author:   Alessandro Pasotti, elpaso@itopen.it
       5             :  *
       6             :  ******************************************************************************
       7             :  * Copyright (c) 2024, Alessandro Pasotti <elpaso at itopen dot it>
       8             :  *
       9             :  * SPDX-License-Identifier: MIT
      10             :  ****************************************************************************/
      11             : 
      12             : //! @cond Doxygen_Suppress
      13             : 
      14             : #include "ogr_schema_override.h"
      15             : #include "ogrsf_frmts.h"
      16             : 
      17          53 : bool OGRSchemaOverride::LoadFromJSON(const std::string &osJSON)
      18             : {
      19         106 :     std::string osFieldsSchemaOverride;
      20          53 :     bool bFieldsSchemaOverrideIsFilePath{false};
      21             : 
      22             :     // Try to load the content of the file
      23          53 :     GByte *pabyRet = nullptr;
      24          53 :     if (VSIIngestFile(nullptr, osJSON.c_str(), &pabyRet, nullptr, -1) == TRUE)
      25             :     {
      26          14 :         bFieldsSchemaOverrideIsFilePath = true;
      27          14 :         osFieldsSchemaOverride = std::string(reinterpret_cast<char *>(pabyRet));
      28          14 :         VSIFree(pabyRet);
      29             :     }
      30             : 
      31          53 :     if (!bFieldsSchemaOverrideIsFilePath)
      32             :     {
      33          39 :         osFieldsSchemaOverride = osJSON;
      34             :     }
      35             : 
      36         106 :     CPLJSONDocument oSchemaDoc;
      37          53 :     if (oSchemaDoc.LoadMemory(osFieldsSchemaOverride))
      38             :     {
      39         106 :         const CPLJSONObject oRoot = oSchemaDoc.GetRoot();
      40          53 :         if (oRoot.IsValid())
      41             :         {
      42         159 :             const auto aoLayers = oRoot.GetArray("layers");
      43             :             // Loop through layer names and get the field details for each field.
      44         104 :             for (const auto &oLayer : aoLayers)
      45             :             {
      46          56 :                 if (oLayer.IsValid())
      47             :                 {
      48         112 :                     const auto oLayerFields = oLayer.GetArray("fields");
      49             :                     // Parse fields
      50         112 :                     const auto osLayerName = oLayer.GetString("name");
      51         112 :                     const auto osSchemaType = oLayer.GetString("schemaType");
      52             :                     // Default schemaType is "Patch"
      53             :                     const auto bSchemaFullOverride =
      54          56 :                         CPLString(osSchemaType).tolower() == "full";
      55          56 :                     OGRLayerSchemaOverride oLayerOverride;
      56          56 :                     oLayerOverride.SetLayerName(osLayerName);
      57          56 :                     oLayerOverride.SetFullOverride(bSchemaFullOverride);
      58             : 
      59          56 :                     if (oLayerFields.Size() > 0 && !osLayerName.empty())
      60             :                     {
      61         119 :                         for (const auto &oField : oLayerFields)
      62             :                         {
      63         136 :                             const auto osFieldName = oField.GetString("name");
      64          68 :                             OGRFieldDefnOverride oFieldOverride;
      65             : 
      66             :                             const CPLString oSrcType(
      67         136 :                                 CPLString(oField.GetString("srcType"))
      68          68 :                                     .tolower());
      69             :                             const CPLString oSrcSubType(
      70         136 :                                 CPLString(oField.GetString("srcSubType"))
      71          68 :                                     .tolower());
      72             :                             const CPLString oType(
      73         136 :                                 CPLString(oField.GetString("type")).tolower());
      74             :                             const CPLString oSubType(
      75         136 :                                 CPLString(oField.GetString("subType"))
      76          68 :                                     .tolower());
      77             :                             const CPLString osNewName(
      78         136 :                                 CPLString(oField.GetString("newName"))
      79          68 :                                     .tolower());
      80          68 :                             const auto nWidth = oField.GetInteger("width", 0);
      81             :                             const auto nPrecision =
      82          68 :                                 oField.GetInteger("precision", 0);
      83             : 
      84          68 :                             if (!osNewName.empty())
      85             :                             {
      86           9 :                                 oFieldOverride.SetFieldName(osNewName);
      87             :                             }
      88             : 
      89          68 :                             if (!oSrcType.empty())
      90             :                             {
      91           2 :                                 if (bSchemaFullOverride)
      92             :                                 {
      93           0 :                                     CPLError(CE_Failure, CPLE_AppDefined,
      94             :                                              "Non-patch OGR_SCHEMA definition "
      95             :                                              "is not allowed with specifying "
      96             :                                              "source field type");
      97           0 :                                     return false;
      98             :                                 }
      99           2 :                                 if (!osFieldName.empty() || !osNewName.empty())
     100             :                                 {
     101           0 :                                     CPLError(CE_Warning, CPLE_AppDefined,
     102             :                                              "Field name and source field type "
     103             :                                              "are mutually exclusive");
     104           0 :                                     return false;
     105             :                                 }
     106             :                                 const OGRFieldType eType =
     107           2 :                                     OGRFieldDefn::GetFieldTypeByName(
     108             :                                         oSrcType.c_str());
     109             :                                 // Check if the field type is valid
     110           2 :                                 if (eType == OFTString && oSrcType != "string")
     111             :                                 {
     112           0 :                                     CPLError(CE_Failure, CPLE_AppDefined,
     113             :                                              "Unsupported source field type: "
     114             :                                              "%s",
     115             :                                              oSrcType.c_str());
     116           0 :                                     return false;
     117             :                                 }
     118           2 :                                 oFieldOverride.SetSrcFieldType(eType);
     119             :                             }
     120             : 
     121          68 :                             if (!oSrcSubType.empty())
     122             :                             {
     123           1 :                                 if (bSchemaFullOverride)
     124             :                                 {
     125           0 :                                     CPLError(CE_Failure, CPLE_AppDefined,
     126             :                                              "Non-patch OGR_SCHEMA definition "
     127             :                                              "is not allowed with specifying "
     128             :                                              "source field subtype");
     129           0 :                                     return false;
     130             :                                 }
     131           1 :                                 if (!osFieldName.empty() || !osNewName.empty())
     132             :                                 {
     133           0 :                                     CPLError(CE_Warning, CPLE_AppDefined,
     134             :                                              "Field name and source field "
     135             :                                              "subtype are mutually exclusive");
     136           0 :                                     return false;
     137             :                                 }
     138             :                                 const OGRFieldSubType eSubType =
     139           1 :                                     OGRFieldDefn::GetFieldSubTypeByName(
     140             :                                         oSubType.c_str());
     141             :                                 // Check if the field subType is valid
     142           2 :                                 if (eSubType == OFSTNone &&
     143           1 :                                     oSrcSubType != "none")
     144             :                                 {
     145           0 :                                     CPLError(CE_Failure, CPLE_AppDefined,
     146             :                                              "Unsupported source field subType:"
     147             :                                              " %s",
     148             :                                              oSubType.c_str());
     149           0 :                                     return false;
     150             :                                 }
     151           1 :                                 oFieldOverride.SetSrcFieldSubType(eSubType);
     152             :                             }
     153             : 
     154         134 :                             if (oSrcType.empty() && oSrcSubType.empty() &&
     155          66 :                                 osFieldName.empty())
     156             :                             {
     157           0 :                                 CPLError(CE_Warning, CPLE_AppDefined,
     158             :                                          "Field name is missing");
     159           0 :                                 return false;
     160             :                             }
     161             : 
     162          68 :                             if (!oType.empty())
     163             :                             {
     164             :                                 const OGRFieldType eType =
     165          44 :                                     OGRFieldDefn::GetFieldTypeByName(
     166             :                                         oType.c_str());
     167             :                                 // Check if the field type is valid
     168          44 :                                 if (eType == OFTString && oType != "string")
     169             :                                 {
     170           5 :                                     CPLError(CE_Failure, CPLE_AppDefined,
     171             :                                              "Unsupported field type: %s "
     172             :                                              "for field %s",
     173             :                                              oType.c_str(),
     174             :                                              osFieldName.c_str());
     175           5 :                                     return false;
     176             :                                 }
     177          39 :                                 oFieldOverride.SetFieldType(eType);
     178             :                             }
     179             : 
     180          63 :                             if (!oSubType.empty())
     181             :                             {
     182             :                                 const OGRFieldSubType eSubType =
     183          21 :                                     OGRFieldDefn::GetFieldSubTypeByName(
     184             :                                         oSubType.c_str());
     185             :                                 // Check if the field subType is valid
     186          21 :                                 if (eSubType == OFSTNone && oSubType != "none")
     187             :                                 {
     188           0 :                                     CPLError(CE_Failure, CPLE_AppDefined,
     189             :                                              "Unsupported field subType: "
     190             :                                              "%s for field %s",
     191             :                                              oSubType.c_str(),
     192             :                                              osFieldName.c_str());
     193           0 :                                     return false;
     194             :                                 }
     195          21 :                                 oFieldOverride.SetFieldSubType(eSubType);
     196             :                             }
     197             : 
     198          63 :                             if (nWidth != 0)
     199             :                             {
     200           5 :                                 oFieldOverride.SetFieldWidth(nWidth);
     201             :                             }
     202             : 
     203          63 :                             if (nPrecision != 0)
     204             :                             {
     205           5 :                                 oFieldOverride.SetFieldPrecision(nPrecision);
     206             :                             }
     207             : 
     208          63 :                             if (bSchemaFullOverride || oFieldOverride.IsValid())
     209             :                             {
     210          63 :                                 if (osFieldName.empty())
     211             :                                 {
     212           2 :                                     oLayerOverride.AddUnnamedFieldOverride(
     213             :                                         oFieldOverride);
     214             :                                 }
     215             :                                 else
     216             :                                 {
     217          61 :                                     oLayerOverride.AddNamedFieldOverride(
     218             :                                         osFieldName, oFieldOverride);
     219             :                                 }
     220             :                             }
     221             :                             else
     222             :                             {
     223           0 :                                 CPLError(CE_Failure, CPLE_AppDefined,
     224             :                                          "Field %s has no valid overrides "
     225             :                                          "and schemaType is not \"Full\"",
     226             :                                          osFieldName.c_str());
     227           0 :                                 return false;
     228             :                             }
     229             :                         }
     230             :                     }
     231             : 
     232          51 :                     if (oLayerOverride.IsValid())
     233             :                     {
     234          51 :                         AddLayerOverride(oLayerOverride);
     235             :                     }
     236             :                     else
     237             :                     {
     238           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     239             :                                  "Layer %s has no valid overrides",
     240             :                                  osLayerName.c_str());
     241           0 :                         return false;
     242             :                     }
     243             :                 }
     244             :                 else
     245             :                 {
     246           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     247             :                              "SCHEMA info is invalid JSON");
     248           0 :                     return false;
     249             :                 }
     250             :             }
     251          48 :             return true;
     252             :         }
     253             :         else
     254             :         {
     255           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     256             :                      "SCHEMA info is invalid JSON");
     257           0 :             return false;
     258             :         }
     259             :     }
     260             :     else
     261             :     {
     262           0 :         CPLError(CE_Failure, CPLE_AppDefined, "SCHEMA info is invalid JSON");
     263           0 :         return false;
     264             :     }
     265             : }
     266             : 
     267          48 : bool OGRSchemaOverride::IsValid() const
     268             : {
     269          48 :     bool isValid = !m_aoLayerOverrides.empty();
     270          99 :     for (const auto &oLayerOverride : m_aoLayerOverrides)
     271             :     {
     272          51 :         isValid &= oLayerOverride.IsValid();
     273             :     }
     274          48 :     return isValid;
     275             : }
     276             : 
     277          24 : bool OGRSchemaOverride::DefaultApply(
     278             :     GDALDataset *poDS, const char *pszDebugKey,
     279             :     std::function<void(OGRLayer *, int)> callbackWhenRemovingField) const
     280             : {
     281          24 :     const auto &oLayerOverrides = GetLayerOverrides();
     282          44 :     for (const auto &oLayerFieldOverride : oLayerOverrides)
     283             :     {
     284          25 :         const auto &osLayerName = oLayerFieldOverride.GetLayerName();
     285          25 :         const bool bIsFullOverride{oLayerFieldOverride.IsFullOverride()};
     286             :         auto oNamedFieldOverrides =
     287          25 :             oLayerFieldOverride.GetNamedFieldOverrides();
     288             :         const auto &oUnnamedFieldOverrides =
     289          25 :             oLayerFieldOverride.GetUnnamedFieldOverrides();
     290             : 
     291             :         const auto ProcessLayer =
     292          23 :             [&callbackWhenRemovingField, &osLayerName, &oNamedFieldOverrides,
     293         425 :              &oUnnamedFieldOverrides, bIsFullOverride](OGRLayer *poLayer)
     294             :         {
     295          46 :             std::vector<OGRFieldDefn *> aoFields;
     296             :             // Patch field definitions
     297          23 :             auto poLayerDefn = poLayer->GetLayerDefn();
     298         174 :             for (int i = 0; i < poLayerDefn->GetFieldCount(); i++)
     299             :             {
     300         151 :                 auto poFieldDefn = poLayerDefn->GetFieldDefn(i);
     301             : 
     302             :                 const auto PatchFieldDefn =
     303          72 :                     [poFieldDefn](const OGRFieldDefnOverride &oFieldOverride)
     304             :                 {
     305          32 :                     if (oFieldOverride.GetFieldType().has_value())
     306          36 :                         whileUnsealing(poFieldDefn)
     307          18 :                             ->SetType(oFieldOverride.GetFieldType().value());
     308          32 :                     if (oFieldOverride.GetFieldWidth().has_value())
     309           6 :                         whileUnsealing(poFieldDefn)
     310           3 :                             ->SetWidth(oFieldOverride.GetFieldWidth().value());
     311          32 :                     if (oFieldOverride.GetFieldPrecision().has_value())
     312           6 :                         whileUnsealing(poFieldDefn)
     313           3 :                             ->SetPrecision(
     314           3 :                                 oFieldOverride.GetFieldPrecision().value());
     315          32 :                     if (oFieldOverride.GetFieldSubType().has_value())
     316          26 :                         whileUnsealing(poFieldDefn)
     317          13 :                             ->SetSubType(
     318          13 :                                 oFieldOverride.GetFieldSubType().value());
     319          32 :                     if (oFieldOverride.GetFieldName().has_value())
     320           6 :                         whileUnsealing(poFieldDefn)
     321           6 :                             ->SetName(
     322           6 :                                 oFieldOverride.GetFieldName().value().c_str());
     323          32 :                 };
     324             : 
     325             :                 auto oFieldOverrideIter =
     326         151 :                     oNamedFieldOverrides.find(poFieldDefn->GetNameRef());
     327         151 :                 if (oFieldOverrideIter != oNamedFieldOverrides.cend())
     328             :                 {
     329          26 :                     const auto &oFieldOverride = oFieldOverrideIter->second;
     330          26 :                     PatchFieldDefn(oFieldOverride);
     331             : 
     332          26 :                     if (bIsFullOverride)
     333             :                     {
     334           8 :                         aoFields.push_back(poFieldDefn);
     335             :                     }
     336          26 :                     oNamedFieldOverrides.erase(oFieldOverrideIter);
     337             :                 }
     338             :                 else
     339             :                 {
     340         130 :                     for (const auto &oFieldOverride : oUnnamedFieldOverrides)
     341             :                     {
     342          11 :                         if ((!oFieldOverride.GetSrcFieldType().has_value() ||
     343          11 :                              oFieldOverride.GetSrcFieldType().value() ==
     344          28 :                                  poFieldDefn->GetType()) &&
     345           6 :                             (!oFieldOverride.GetSrcFieldSubType().has_value() ||
     346          12 :                              oFieldOverride.GetSrcFieldSubType().value() ==
     347           1 :                                  poFieldDefn->GetSubType()))
     348             :                         {
     349           6 :                             PatchFieldDefn(oFieldOverride);
     350           6 :                             break;
     351             :                         }
     352             :                     }
     353             :                 }
     354             :             }
     355             : 
     356             :             // Error if any field override is not found
     357          23 :             if (!oNamedFieldOverrides.empty())
     358             :             {
     359           6 :                 CPLError(CE_Failure, CPLE_AppDefined,
     360             :                          "Field %s not found in layer %s",
     361           3 :                          oNamedFieldOverrides.cbegin()->first.c_str(),
     362             :                          osLayerName.c_str());
     363           3 :                 return false;
     364             :             }
     365             : 
     366             :             // Remove fields not in the override
     367          20 :             if (bIsFullOverride)
     368             :             {
     369          35 :                 for (int i = poLayerDefn->GetFieldCount() - 1; i >= 0; i--)
     370             :                 {
     371          30 :                     auto poFieldDefn = poLayerDefn->GetFieldDefn(i);
     372          30 :                     if (std::find(aoFields.begin(), aoFields.end(),
     373          30 :                                   poFieldDefn) == aoFields.end())
     374             :                     {
     375          22 :                         callbackWhenRemovingField(poLayer, i);
     376          22 :                         whileUnsealing(poLayerDefn)->DeleteFieldDefn(i);
     377             :                     }
     378             :                 }
     379             :             }
     380             : 
     381          20 :             return true;
     382          25 :         };
     383             : 
     384          25 :         CPLDebug(pszDebugKey, "Applying schema override for layer %s",
     385             :                  osLayerName.c_str());
     386             : 
     387          25 :         if (osLayerName == "*")
     388             :         {
     389           6 :             for (auto *poLayer : poDS->GetLayers())
     390             :             {
     391           3 :                 if (!ProcessLayer(poLayer))
     392           0 :                     return false;
     393             :             }
     394             :         }
     395             :         else
     396             :         {
     397             :             // Fail if the layer name does not exist
     398          22 :             auto poLayer = poDS->GetLayerByName(osLayerName.c_str());
     399          22 :             if (poLayer == nullptr)
     400             :             {
     401           2 :                 CPLError(CE_Failure, CPLE_AppDefined, "Layer %s not found",
     402             :                          osLayerName.c_str());
     403           2 :                 return false;
     404             :             }
     405          20 :             if (!ProcessLayer(poLayer))
     406           3 :                 return false;
     407             :         }
     408             :     }
     409             : 
     410          19 :     return true;
     411             : }
     412             : 
     413         102 : bool OGRLayerSchemaOverride::IsValid() const
     414             : {
     415             :     bool isValid =
     416         204 :         !m_osLayerName.empty() &&
     417         102 :         (!m_oNamedFieldOverrides.empty() || !m_aoUnnamedFieldOverrides.empty());
     418         224 :     for (const auto &oFieldOverrideIter : m_oNamedFieldOverrides)
     419             :     {
     420         122 :         isValid &= !oFieldOverrideIter.first.empty();
     421             :         // When schemaType is "full" override we don't need to check if the field
     422             :         // overrides are valid: a list of fields to keep is enough.
     423         122 :         if (!m_bIsFullOverride)
     424             :         {
     425          98 :             isValid &= oFieldOverrideIter.second.IsValid();
     426             :         }
     427             :     }
     428         102 :     return isValid;
     429             : }
     430             : 
     431         149 : bool OGRFieldDefnOverride::IsValid() const
     432             : {
     433         322 :     return m_osName.has_value() || m_eType.has_value() ||
     434          66 :            m_eSubType.has_value() || m_eSrcType.has_value() ||
     435         301 :            m_eSrcSubType.has_value() || m_nWidth.has_value() ||
     436         149 :            m_nPrecision.has_value();
     437             : }
     438             : 
     439             : //! @endcond

Generated by: LCOV version 1.14