Coverage Report

Created: 2026-02-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/gnm/gnm_frmts/db/gnmdbnetwork.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL/OGR Geography Network support (Geographic Network Model)
4
 * Purpose:  GNM db based generic driver.
5
 * Authors:  Mikhail Gusev (gusevmihs at gmail dot com)
6
 *           Dmitry Baryshnikov, polimax@mail.ru
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 2014, Mikhail Gusev
10
 * Copyright (c) 2014-2015, NextGIS <info@nextgis.com>
11
 *
12
 * Permission is hereby granted, free of charge, to any person obtaining a
13
 * copy of this software and associated documentation files (the "Software"),
14
 * to deal in the Software without restriction, including without limitation
15
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16
 * and/or sell copies of the Software, and to permit persons to whom the
17
 * Software is furnished to do so, subject to the following conditions:
18
 *
19
 * The above copyright notice and this permission notice shall be included
20
 * in all copies or substantial portions of the Software.
21
 *
22
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28
 * DEALINGS IN THE SOFTWARE.
29
 ****************************************************************************/
30
31
#include "gnmdb.h"
32
#include "gnm_priv.h"
33
34
0
GNMDatabaseNetwork::GNMDatabaseNetwork() : GNMGenericNetwork()
35
0
{
36
0
    m_poDS = nullptr;
37
0
}
38
39
GNMDatabaseNetwork::~GNMDatabaseNetwork()
40
0
{
41
0
    FlushCache(true);
42
43
0
    GDALClose(m_poDS);
44
0
}
45
46
CPLErr GNMDatabaseNetwork::Open(GDALOpenInfo *poOpenInfo)
47
0
{
48
0
    FormName(poOpenInfo->pszFilename, poOpenInfo->papszOpenOptions);
49
50
0
    if (CSLFindName(poOpenInfo->papszOpenOptions, "LIST_ALL_TABLES") == -1)
51
0
        poOpenInfo->papszOpenOptions = CSLAddNameValue(
52
0
            poOpenInfo->papszOpenOptions, "LIST_ALL_TABLES", "YES");
53
54
0
    m_poDS = (GDALDataset *)GDALOpenEx(m_soNetworkFullName,
55
0
                                       GDAL_OF_VECTOR | GDAL_OF_UPDATE, nullptr,
56
0
                                       nullptr, poOpenInfo->papszOpenOptions);
57
58
0
    if (nullptr == m_poDS)
59
0
    {
60
0
        CPLError(CE_Failure, CPLE_OpenFailed, "Open '%s' failed",
61
0
                 m_soNetworkFullName.c_str());
62
0
        return CE_Failure;
63
0
    }
64
65
    // There should be only one schema so no schema name can be in table name
66
0
    if (LoadMetadataLayer(m_poDS) != CE_None)
67
0
    {
68
0
        return CE_Failure;
69
0
    }
70
71
0
    if (LoadGraphLayer(m_poDS) != CE_None)
72
0
    {
73
0
        return CE_Failure;
74
0
    }
75
76
0
    if (LoadFeaturesLayer(m_poDS) != CE_None)
77
0
    {
78
0
        return CE_Failure;
79
0
    }
80
81
0
    return CE_None;
82
0
}
83
84
CPLErr GNMDatabaseNetwork::Create(const char *pszFilename,
85
                                  CSLConstList papszOptions)
86
0
{
87
0
    FormName(pszFilename, papszOptions);
88
89
0
    if (m_soName.empty() || m_soNetworkFullName.empty())
90
0
    {
91
0
        CPLError(CE_Failure, CPLE_IllegalArg,
92
0
                 "The network name should be present");
93
0
        return CE_Failure;
94
0
    }
95
96
0
    if (nullptr == m_poDS)
97
0
    {
98
0
        m_poDS = (GDALDataset *)GDALOpenEx(m_soNetworkFullName,
99
0
                                           GDAL_OF_VECTOR | GDAL_OF_UPDATE,
100
0
                                           nullptr, nullptr, papszOptions);
101
0
    }
102
103
0
    if (nullptr == m_poDS)
104
0
    {
105
0
        CPLError(CE_Failure, CPLE_OpenFailed, "Open '%s' failed",
106
0
                 m_soNetworkFullName.c_str());
107
0
        return CE_Failure;
108
0
    }
109
110
0
    GDALDriver *l_poDriver = m_poDS->GetDriver();
111
0
    if (nullptr == l_poDriver)
112
0
    {
113
0
        CPLError(CE_Failure, CPLE_OpenFailed, "Get dataset driver failed");
114
0
        return CE_Failure;
115
0
    }
116
117
0
    if (!CheckStorageDriverSupport(l_poDriver->GetDescription()))
118
0
    {
119
0
        return CE_Failure;
120
0
    }
121
122
    // check required options
123
124
0
    const char *pszNetworkDescription =
125
0
        CSLFetchNameValue(papszOptions, GNM_MD_DESCR);
126
0
    if (nullptr != pszNetworkDescription)
127
0
        sDescription = pszNetworkDescription;
128
129
    // check Spatial reference
130
0
    const char *pszSRS = CSLFetchNameValue(papszOptions, GNM_MD_SRS);
131
0
    if (nullptr == pszSRS)
132
0
    {
133
0
        CPLError(CE_Failure, CPLE_IllegalArg,
134
0
                 "The network spatial reference should be present");
135
0
        return CE_Failure;
136
0
    }
137
0
    else
138
0
    {
139
0
        OGRSpatialReference spatialRef;
140
0
        spatialRef.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
141
0
        if (spatialRef.SetFromUserInput(pszSRS) != OGRERR_NONE)
142
0
        {
143
0
            CPLError(CE_Failure, CPLE_IllegalArg,
144
0
                     "The network spatial reference should be present");
145
0
            return CE_Failure;
146
0
        }
147
148
0
        m_oSRS = std::move(spatialRef);
149
0
    }
150
151
0
    int nResult = CheckNetworkExist(pszFilename, papszOptions);
152
153
0
    if (TRUE == nResult)
154
0
    {
155
0
        CPLError(CE_Failure, CPLE_IllegalArg, "The network already exist");
156
0
        return CE_Failure;
157
0
    }
158
159
    // Create the necessary system layers and fields
160
161
    // Create meta layer
162
163
0
    CPLErr eResult = CreateMetadataLayer(m_poDS, GNM_VERSION_NUM);
164
165
0
    if (CE_None != eResult)
166
0
    {
167
        // an error message should come from function
168
0
        return CE_Failure;
169
0
    }
170
171
    // Create graph layer
172
173
0
    eResult = CreateGraphLayer(m_poDS);
174
175
0
    if (CE_None != eResult)
176
0
    {
177
0
        DeleteMetadataLayer();
178
0
        return CE_Failure;
179
0
    }
180
181
    // Create features layer
182
183
0
    eResult = CreateFeaturesLayer(m_poDS);
184
185
0
    if (CE_None != eResult)
186
0
    {
187
0
        DeleteMetadataLayer();
188
0
        DeleteGraphLayer();
189
0
        return CE_Failure;
190
0
    }
191
192
0
    return CE_None;
193
0
}
194
195
int GNMDatabaseNetwork::CheckNetworkExist(const char *pszFilename,
196
                                          CSLConstList papszOptions)
197
0
{
198
    // check if path exist
199
    // if path exist check if network already present and OVERWRITE option
200
    // else create the path
201
202
0
    if (FormName(pszFilename, papszOptions) != CE_None)
203
0
    {
204
0
        return TRUE;
205
0
    }
206
207
0
    if (nullptr == m_poDS)
208
0
    {
209
0
        m_poDS = (GDALDataset *)GDALOpenEx(m_soNetworkFullName,
210
0
                                           GDAL_OF_VECTOR | GDAL_OF_UPDATE,
211
0
                                           nullptr, nullptr, papszOptions);
212
0
    }
213
214
0
    const bool bOverwrite = CPLFetchBool(papszOptions, "OVERWRITE", false);
215
216
0
    std::vector<int> anDeleteLayers;
217
0
    int i;
218
0
    for (i = 0; i < m_poDS->GetLayerCount(); ++i)
219
0
    {
220
0
        OGRLayer *poLayer = m_poDS->GetLayer(i);
221
0
        if (nullptr == poLayer)
222
0
            continue;
223
224
0
        if (EQUAL(poLayer->GetName(), GNM_SYSLAYER_META) ||
225
0
            EQUAL(poLayer->GetName(), GNM_SYSLAYER_GRAPH) ||
226
0
            EQUAL(poLayer->GetName(), GNM_SYSLAYER_FEATURES))
227
0
        {
228
0
            anDeleteLayers.push_back(i);
229
0
        }
230
0
    }
231
232
0
    if (anDeleteLayers.empty())
233
0
        return FALSE;
234
235
0
    if (bOverwrite)
236
0
    {
237
0
        for (i = (int)anDeleteLayers.size(); i > 0; i--)
238
0
        {
239
0
            CPLDebug("GNM", "Delete layer: %d", i);
240
0
            if (m_poDS->DeleteLayer(anDeleteLayers[i - 1]) != OGRERR_NONE)
241
0
                return TRUE;
242
0
        }
243
0
        return FALSE;
244
0
    }
245
0
    else
246
0
    {
247
0
        return TRUE;
248
0
    }
249
0
}
250
251
CPLErr GNMDatabaseNetwork::DeleteMetadataLayer()
252
0
{
253
0
    return DeleteLayerByName(GNM_SYSLAYER_META);
254
0
}
255
256
CPLErr GNMDatabaseNetwork::DeleteGraphLayer()
257
0
{
258
0
    return DeleteLayerByName(GNM_SYSLAYER_GRAPH);
259
0
}
260
261
CPLErr GNMDatabaseNetwork::DeleteFeaturesLayer()
262
0
{
263
0
    return DeleteLayerByName(GNM_SYSLAYER_FEATURES);
264
0
}
265
266
CPLErr GNMDatabaseNetwork::DeleteLayerByName(const char *pszLayerName)
267
0
{
268
0
    if (nullptr == m_poDS)
269
0
        return CE_Failure;
270
271
0
    for (int i = 0; i < m_poDS->GetLayerCount(); ++i)
272
0
    {
273
0
        OGRLayer *poLayer = m_poDS->GetLayer(i);
274
0
        if (nullptr == poLayer)
275
0
            continue;
276
277
0
        if (EQUAL(poLayer->GetName(), pszLayerName))
278
0
            return m_poDS->DeleteLayer(i) == OGRERR_NONE ? CE_None : CE_Failure;
279
0
    }
280
281
0
    CPLError(CE_Failure, CPLE_IllegalArg, "The layer %s not exist",
282
0
             pszLayerName);
283
0
    return CE_Failure;
284
0
}
285
286
CPLErr GNMDatabaseNetwork::DeleteNetworkLayers()
287
0
{
288
0
    while (GetLayerCount() > 0)
289
0
    {
290
0
        OGRErr eErr = DeleteLayer(0);
291
0
        if (eErr != OGRERR_NONE)
292
0
            return CE_Failure;
293
0
    }
294
0
    return CE_None;
295
0
}
296
297
CPLErr GNMDatabaseNetwork::LoadNetworkLayer(const char *pszLayername)
298
0
{
299
    // check if not loaded
300
0
    for (size_t i = 0; i < m_apoLayers.size(); ++i)
301
0
    {
302
0
        if (EQUAL(m_apoLayers[i]->GetName(), pszLayername))
303
0
            return CE_None;
304
0
    }
305
306
0
    OGRLayer *poLayer = m_poDS->GetLayerByName(pszLayername);
307
0
    if (nullptr == poLayer)
308
0
    {
309
0
        CPLError(CE_Failure, CPLE_OpenFailed, "Layer '%s' is not exist",
310
0
                 pszLayername);
311
0
        return CE_Failure;
312
0
    }
313
314
0
    CPLDebug("GNM", "Layer '%s' loaded", poLayer->GetName());
315
316
0
    GNMGenericLayer *pGNMLayer = new GNMGenericLayer(poLayer, this);
317
0
    m_apoLayers.push_back(pGNMLayer);
318
319
0
    return CE_None;
320
0
}
321
322
bool GNMDatabaseNetwork::CheckStorageDriverSupport(const char *pszDriverName)
323
0
{
324
0
    if (EQUAL(pszDriverName, "PostgreSQL"))
325
0
        return true;
326
    // TODO: expand this list with supported OGR direvers
327
0
    return false;
328
0
}
329
330
CPLErr GNMDatabaseNetwork::FormName(const char *pszFilename,
331
                                    CSLConstList papszOptions)
332
0
{
333
0
    if (m_soNetworkFullName.empty())
334
0
        m_soNetworkFullName = pszFilename;
335
336
0
    if (m_soName.empty())
337
0
    {
338
0
        const char *pszNetworkName =
339
0
            CSLFetchNameValue(papszOptions, GNM_MD_NAME);
340
0
        if (nullptr != pszNetworkName)
341
0
        {
342
0
            m_soName = pszNetworkName;
343
0
        }
344
345
0
        char *pszActiveSchemaStart;
346
0
        pszActiveSchemaStart = (char *)strstr(pszFilename, "active_schema=");
347
0
        if (pszActiveSchemaStart == nullptr)
348
0
            pszActiveSchemaStart =
349
0
                (char *)strstr(pszFilename, "ACTIVE_SCHEMA=");
350
0
        if (pszActiveSchemaStart != nullptr)
351
0
        {
352
0
            char *pszActiveSchema;
353
354
0
            pszActiveSchema =
355
0
                CPLStrdup(pszActiveSchemaStart + strlen("active_schema="));
356
357
0
            const char *pszEnd = strchr(pszActiveSchemaStart, ' ');
358
0
            if (pszEnd == nullptr)
359
0
                pszEnd = pszFilename + strlen(pszFilename);
360
361
0
            pszActiveSchema[pszEnd - pszActiveSchemaStart -
362
0
                            strlen("active_schema=")] = '\0';
363
364
0
            m_soName = pszActiveSchema;
365
0
            CPLFree(pszActiveSchema);
366
0
        }
367
0
        else
368
0
        {
369
0
            if (!m_soName.empty())
370
0
            {
371
                // add active schema
372
0
                m_soNetworkFullName += "ACTIVE_SCHEMA=" + m_soName;
373
0
            }
374
0
            else
375
0
            {
376
0
                m_soName = "public";
377
0
            }
378
0
        }
379
380
0
        CPLDebug("GNM", "Network name: %s", m_soName.c_str());
381
0
    }
382
0
    return CE_None;
383
0
}
384
385
OGRErr GNMDatabaseNetwork::DeleteLayer(int nIndex)
386
0
{
387
0
    if (nullptr == m_poDS)
388
0
    {
389
0
        CPLError(CE_Failure, CPLE_FileIO, "Network not opened.");
390
0
        return OGRERR_FAILURE;
391
0
    }
392
393
0
    OGRLayer *poNetworkLayer = GetLayer(nIndex);
394
395
0
    CPLDebug("GNM", "Delete network layer '%s'", poNetworkLayer->GetName());
396
397
0
    int nDeleteIndex = -1;
398
0
    for (int i = 0; i < m_poDS->GetLayerCount(); ++i)
399
0
    {
400
0
        OGRLayer *poLayer = m_poDS->GetLayer(i);
401
0
        if (EQUAL(poNetworkLayer->GetName(), poLayer->GetName()))
402
0
        {
403
0
            nDeleteIndex = i;
404
0
            break;
405
0
        }
406
0
    }
407
408
0
    if (m_poDS->DeleteLayer(nDeleteIndex) != OGRERR_NONE)
409
0
    {
410
0
        return OGRERR_FAILURE;
411
0
    }
412
413
0
    return GNMGenericNetwork::DeleteLayer(nIndex);
414
0
}
415
416
OGRLayer *
417
GNMDatabaseNetwork::ICreateLayer(const char *pszName,
418
                                 const OGRGeomFieldDefn *poGeomFieldDefn,
419
                                 CSLConstList papszOptions)
420
0
{
421
    // check if layer with such name exist
422
0
    for (int i = 0; i < GetLayerCount(); ++i)
423
0
    {
424
0
        OGRLayer *pLayer = GetLayer(i);
425
0
        if (nullptr == pLayer)
426
0
            continue;
427
0
        if (EQUAL(pLayer->GetName(), pszName))
428
0
        {
429
0
            CPLError(CE_Failure, CPLE_IllegalArg,
430
0
                     "The network layer '%s' already exist.", pszName);
431
0
            return nullptr;
432
0
        }
433
0
    }
434
435
0
    OGRSpatialReference oSpaRef(m_oSRS);
436
437
0
    const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
438
0
    OGRLayer *poLayer =
439
0
        m_poDS->CreateLayer(pszName, &oSpaRef, eGType, papszOptions);
440
0
    if (poLayer == nullptr)
441
0
    {
442
0
        CPLError(CE_Failure, CPLE_FileIO, "Layer creation failed.");
443
0
        return nullptr;
444
0
    }
445
446
0
    OGRFieldDefn oField(GNM_SYSFIELD_GFID, GNMGFIDInt);
447
0
    if (poLayer->CreateField(&oField) != OGRERR_NONE)
448
0
    {
449
0
        CPLError(CE_Failure, CPLE_FileIO,
450
0
                 "Creating global identificator field failed.");
451
0
        return nullptr;
452
0
    }
453
454
0
    OGRFieldDefn oFieldBlock(GNM_SYSFIELD_BLOCKED, OFTInteger);
455
0
    if (poLayer->CreateField(&oFieldBlock) != OGRERR_NONE)
456
0
    {
457
0
        CPLError(CE_Failure, CPLE_FileIO, "Creating is blocking field failed.");
458
0
        return nullptr;
459
0
    }
460
461
0
    GNMGenericLayer *pGNMLayer = new GNMGenericLayer(poLayer, this);
462
0
    m_apoLayers.push_back(pGNMLayer);
463
0
    return pGNMLayer;
464
0
}