Coverage Report

Created: 2025-11-15 08:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/flatgeobuf/ogrflatgeobufeditablelayer.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  FlatGeobuf driver
4
 * Purpose:  Implements OGRFlatGeobufEditableLayer class.
5
 * Author:   Björn Harrtell <bjorn at wololo dot org>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2020, Björn Harrtell <bjorn at wololo dot org>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "ogrsf_frmts.h"
14
#include "cpl_vsi_virtual.h"
15
#include "cpl_conv.h"
16
#include "cpl_json.h"
17
#include "cpl_http.h"
18
#include "ogr_p.h"
19
20
#include "ogr_flatgeobuf.h"
21
#include "geometryreader.h"
22
#include "geometrywriter.h"
23
24
#include <algorithm>
25
#include <stdexcept>
26
27
using namespace flatbuffers;
28
using namespace FlatGeobuf;
29
using namespace ogr_flatgeobuf;
30
31
class OGRFlatGeobufEditableLayerSynchronizer final
32
    : public IOGREditableLayerSynchronizer
33
{
34
    OGRFlatGeobufLayer *m_poFlatGeobufLayer;
35
    char **m_papszOpenOptions;
36
37
  public:
38
    OGRFlatGeobufEditableLayerSynchronizer(
39
        OGRFlatGeobufLayer *poFlatGeobufLayer, char **papszOpenOptions)
40
0
        : m_poFlatGeobufLayer(poFlatGeobufLayer),
41
0
          m_papszOpenOptions(CSLDuplicate(papszOpenOptions))
42
0
    {
43
0
    }
44
45
    ~OGRFlatGeobufEditableLayerSynchronizer() override;
46
47
    virtual OGRErr EditableSyncToDisk(OGRLayer *poEditableLayer,
48
                                      OGRLayer **ppoDecoratedLayer) override;
49
};
50
51
OGRFlatGeobufEditableLayerSynchronizer::
52
    ~OGRFlatGeobufEditableLayerSynchronizer()
53
0
{
54
0
    CSLDestroy(m_papszOpenOptions);
55
0
}
56
57
OGRErr OGRFlatGeobufEditableLayerSynchronizer::EditableSyncToDisk(
58
    OGRLayer *poEditableLayer, OGRLayer **ppoDecoratedLayer)
59
0
{
60
0
    CPLDebugOnly("FlatGeobuf", "EditableSyncToDisk called");
61
62
0
    CPLAssert(m_poFlatGeobufLayer == *ppoDecoratedLayer);
63
64
0
    const CPLString osLayerName(m_poFlatGeobufLayer->GetName());
65
0
    const CPLString osFilename(m_poFlatGeobufLayer->GetFilename());
66
0
    VSIStatBufL sStatBuf;
67
0
    CPLString osTmpFilename(osFilename);
68
0
    if (VSIStatL(osFilename, &sStatBuf) == 0)
69
0
    {
70
0
        osTmpFilename += "_ogr_tmp.fgb";
71
0
    }
72
0
    const OGRSpatialReference *spatialRef =
73
0
        m_poFlatGeobufLayer->GetSpatialRef();
74
0
    auto gType = m_poFlatGeobufLayer->getOGRwkbGeometryType();
75
0
    auto createIndex = m_poFlatGeobufLayer->GetIndexNodeSize() > 0;
76
77
0
    OGRFlatGeobufLayer *poFlatGeobufTmpLayer = OGRFlatGeobufLayer::Create(
78
0
        m_poFlatGeobufLayer->GetDataset(), osLayerName.c_str(),
79
0
        osTmpFilename.c_str(), spatialRef, gType, createIndex,
80
0
        m_papszOpenOptions);
81
0
    if (poFlatGeobufTmpLayer == nullptr)
82
0
        return OGRERR_FAILURE;
83
84
0
    OGRErr eErr = OGRERR_NONE;
85
0
    OGRFeatureDefn *poEditableFDefn = poEditableLayer->GetLayerDefn();
86
0
    for (int i = 0; eErr == OGRERR_NONE && i < poEditableFDefn->GetFieldCount();
87
0
         i++)
88
0
    {
89
0
        OGRFieldDefn oFieldDefn(poEditableFDefn->GetFieldDefn(i));
90
0
        eErr = poFlatGeobufTmpLayer->CreateField(&oFieldDefn);
91
0
    }
92
93
0
    poEditableLayer->ResetReading();
94
95
    // Disable all filters.
96
0
    const char *pszQueryStringConst = poEditableLayer->GetAttrQueryString();
97
0
    char *pszQueryStringBak =
98
0
        pszQueryStringConst ? CPLStrdup(pszQueryStringConst) : nullptr;
99
0
    poEditableLayer->SetAttributeFilter(nullptr);
100
101
0
    const int iFilterGeomIndexBak = poEditableLayer->GetGeomFieldFilter();
102
0
    OGRGeometry *poFilterGeomBak = poEditableLayer->GetSpatialFilter();
103
0
    if (poFilterGeomBak)
104
0
        poFilterGeomBak = poFilterGeomBak->clone();
105
0
    poEditableLayer->SetSpatialFilter(nullptr);
106
107
0
    auto aoMapSrcToTargetIdx =
108
0
        poFlatGeobufTmpLayer->GetLayerDefn()->ComputeMapForSetFrom(
109
0
            poEditableLayer->GetLayerDefn(), true);
110
0
    aoMapSrcToTargetIdx.push_back(
111
0
        -1);  // add dummy entry to be sure that .data() is valid
112
113
0
    for (auto &&poFeature : poEditableLayer)
114
0
    {
115
0
        if (eErr != OGRERR_NONE)
116
0
            break;
117
0
        OGRFeature *poNewFeature =
118
0
            new OGRFeature(poFlatGeobufTmpLayer->GetLayerDefn());
119
0
        poNewFeature->SetFrom(poFeature.get(), aoMapSrcToTargetIdx.data(),
120
0
                              true);
121
0
        eErr = poFlatGeobufTmpLayer->CreateFeature(poNewFeature);
122
0
        delete poNewFeature;
123
0
    }
124
0
    delete poFlatGeobufTmpLayer;
125
126
    // Restore filters.
127
0
    poEditableLayer->SetAttributeFilter(pszQueryStringBak);
128
0
    CPLFree(pszQueryStringBak);
129
0
    poEditableLayer->SetSpatialFilter(iFilterGeomIndexBak, poFilterGeomBak);
130
0
    delete poFilterGeomBak;
131
132
0
    if (eErr != OGRERR_NONE)
133
0
    {
134
0
        CPLError(CE_Failure, CPLE_AppDefined, "Error while creating %s",
135
0
                 osTmpFilename.c_str());
136
0
        VSIUnlink(osTmpFilename);
137
0
        return eErr;
138
0
    }
139
140
0
    delete m_poFlatGeobufLayer;
141
0
    *ppoDecoratedLayer = nullptr;
142
0
    m_poFlatGeobufLayer = nullptr;
143
144
0
    if (osFilename != osTmpFilename)
145
0
    {
146
0
        const CPLString osTmpOriFilename(osFilename + ".ogr_bak");
147
0
        if (VSIRename(osFilename, osTmpOriFilename) != 0 ||
148
0
            VSIRename(osTmpFilename, osFilename) != 0)
149
0
        {
150
0
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot rename files");
151
0
            return OGRERR_FAILURE;
152
0
        }
153
0
        VSIUnlink(osTmpOriFilename);
154
0
    }
155
156
0
    VSILFILE *fp = VSIFOpenL(osFilename, "rb+");
157
0
    if (fp == nullptr)
158
0
    {
159
0
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot reopen updated %s",
160
0
                 osFilename.c_str());
161
0
        return OGRERR_FAILURE;
162
0
    }
163
164
0
    m_poFlatGeobufLayer =
165
0
        OGRFlatGeobufLayer::Open(osFilename.c_str(), fp, false);
166
0
    *ppoDecoratedLayer = m_poFlatGeobufLayer;
167
168
0
    return OGRERR_NONE;
169
0
}
170
171
OGRFlatGeobufEditableLayer::OGRFlatGeobufEditableLayer(
172
    OGRFlatGeobufLayer *poFlatGeobufLayer, char **papszOpenOptions)
173
0
    : OGREditableLayer(poFlatGeobufLayer, true,
174
0
                       new OGRFlatGeobufEditableLayerSynchronizer(
175
0
                           poFlatGeobufLayer, papszOpenOptions),
176
0
                       true)
177
0
{
178
0
}
Unexecuted instantiation: OGRFlatGeobufEditableLayer::OGRFlatGeobufEditableLayer(OGRFlatGeobufLayer*, char**)
Unexecuted instantiation: OGRFlatGeobufEditableLayer::OGRFlatGeobufEditableLayer(OGRFlatGeobufLayer*, char**)
179
180
GIntBig OGRFlatGeobufEditableLayer::GetFeatureCount(int bForce)
181
0
{
182
0
    const GIntBig nRet = OGREditableLayer::GetFeatureCount(bForce);
183
0
    if (m_poDecoratedLayer != nullptr && m_nNextFID <= 0)
184
0
    {
185
0
        const GIntBig nTotalFeatureCount =
186
0
            static_cast<OGRFlatGeobufLayer *>(m_poDecoratedLayer)
187
0
                ->GetFeatureCount(false);
188
0
        if (nTotalFeatureCount >= 0)
189
0
            SetNextFID(nTotalFeatureCount + 1);
190
0
    }
191
0
    return nRet;
192
0
}
193
194
/************************************************************************/
195
/*                            TestCapability()                          */
196
/************************************************************************/
197
198
int OGRFlatGeobufEditableLayer::TestCapability(const char *pszCap) const
199
0
{
200
0
    if (EQUAL(pszCap, OLCSequentialWrite) || EQUAL(pszCap, OLCRandomWrite) ||
201
0
        EQUAL(pszCap, OLCCreateField) || EQUAL(pszCap, OLCDeleteField) ||
202
0
        EQUAL(pszCap, OLCReorderFields) || EQUAL(pszCap, OLCAlterFieldDefn) ||
203
0
        EQUAL(pszCap, OLCDeleteFeature))
204
0
    {
205
0
        return TRUE;
206
0
    }
207
208
0
    return OGREditableLayer::TestCapability(pszCap);
209
0
}