Coverage Report

Created: 2026-03-30 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/vfk/vfkdatablocksqlite.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  VFK Reader - Data block definition (SQLite)
4
 * Purpose:  Implements VFKDataBlockSQLite
5
 * Author:   Martin Landa, landa.martin gmail.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2012-2014, Martin Landa <landa.martin gmail.com>
9
 * Copyright (c) 2012-2014, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include <algorithm>
15
#include <limits>
16
#include <map>
17
#include <utility>
18
19
#include "vfkreader.h"
20
#include "vfkreaderp.h"
21
22
#include "cpl_conv.h"
23
#include "cpl_error.h"
24
25
/*!
26
  \brief VFKDataBlockSQLite constructor
27
*/
28
VFKDataBlockSQLite::VFKDataBlockSQLite(const char *pszName,
29
                                       const IVFKReader *poReader)
30
9.99k
    : IVFKDataBlock(pszName, poReader), m_hStmt(nullptr)
31
9.99k
{
32
9.99k
}
33
34
/*!
35
  \brief Load geometry (point layers)
36
37
  \return number of invalid features
38
*/
39
int VFKDataBlockSQLite::LoadGeometryPoint()
40
427
{
41
427
    if (LoadGeometryFromDB()) /* try to load geometry from DB */
42
66
        return 0;
43
44
361
    const bool bSkipInvalid = EQUAL(m_pszName, "OB") ||
45
308
                              EQUAL(m_pszName, "OP") ||
46
262
                              EQUAL(m_pszName, "OBBP");
47
48
361
    CPLString osSQL;
49
361
    osSQL.Printf("SELECT SOURADNICE_Y,SOURADNICE_X,%s,rowid FROM %s",
50
361
                 FID_COLUMN, m_pszName);
51
52
361
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
53
361
    sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
54
55
361
    if (poReader->IsSpatial())
56
361
        poReader->ExecuteSQL("BEGIN");
57
58
361
    int nGeometries = 0;
59
361
    int nInvalid = 0;
60
5.01k
    while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
61
4.65k
    {
62
        /* read values */
63
4.65k
        const double x =
64
4.65k
            -1.0 * sqlite3_column_double(
65
4.65k
                       hStmt, 0); /* S-JTSK coordinate system expected */
66
4.65k
        const double y = -1.0 * sqlite3_column_double(hStmt, 1);
67
4.65k
        const GIntBig iFID = sqlite3_column_int64(hStmt, 2);
68
4.65k
        const int rowId = sqlite3_column_int(hStmt, 3);
69
70
4.65k
        VFKFeatureSQLite *poFeature =
71
4.65k
            dynamic_cast<VFKFeatureSQLite *>(GetFeatureByIndex(rowId - 1));
72
4.65k
        if (poFeature == nullptr || poFeature->GetFID() != iFID)
73
10
        {
74
10
            continue;
75
10
        }
76
77
        /* create geometry */
78
4.64k
        OGRPoint pt(x, y);
79
4.64k
        if (!poFeature->SetGeometry(&pt))
80
504
        {
81
504
            nInvalid++;
82
504
            continue;
83
504
        }
84
85
        /* store also geometry in DB */
86
4.13k
        if (poReader->IsSpatial() &&
87
4.13k
            SaveGeometryToDB(&pt, rowId) != OGRERR_FAILURE)
88
4.13k
            nGeometries++;
89
4.13k
    }
90
91
    /* update number of geometries in VFK_DB_TABLE table */
92
361
    UpdateVfkBlocks(nGeometries);
93
94
361
    if (poReader->IsSpatial())
95
361
        poReader->ExecuteSQL("COMMIT");
96
97
361
    return bSkipInvalid ? 0 : nInvalid;
98
427
}
99
100
/*!
101
  \brief Set geometry for linestrings
102
103
  \param poLine VFK feature
104
  \param oOGRLine line geometry
105
  \param[in,out] bValid true when feature's geometry is valid
106
  \param ftype geometry VFK type
107
  \param[in,out] rowIdFeat list of row ids which forms linestring
108
  \param[in,out] nGeometries number of features with valid geometry
109
*/
110
bool VFKDataBlockSQLite::SetGeometryLineString(VFKFeatureSQLite *poLine,
111
                                               OGRLineString *oOGRLine,
112
                                               bool &bValid, const char *ftype,
113
                                               std::vector<int> &rowIdFeat,
114
                                               int &nGeometries)
115
6.00k
{
116
6.00k
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
117
118
6.00k
    oOGRLine->setCoordinateDimension(2); /* force 2D */
119
120
    /* check also VFK validity */
121
6.00k
    if (bValid)
122
3.11k
    {
123
        /* Feature types
124
125
           - '3'    - line       (2 points)
126
           - '4'    - linestring (at least 2 points)
127
           - '11'   - curve      (at least 2 points)
128
           - '15'   - circle     (3 points)
129
           - '15 r' - circle     (center point & radius)
130
           - '16'   - arc        (3 points)
131
        */
132
133
3.11k
        const int npoints = oOGRLine->getNumPoints();
134
3.11k
        if (EQUAL(ftype, "3") && npoints > 2)
135
0
        {
136
            /* be less pedantic, just inform user about data
137
             * inconsistency
138
139
               bValid = false;
140
            */
141
0
            CPLDebug("OGR-VFK",
142
0
                     "Line (fid=" CPL_FRMT_GIB
143
0
                     ") defined by more than two vertices",
144
0
                     poLine->GetFID());
145
0
        }
146
3.11k
        else if (EQUAL(ftype, "11") && npoints < 2)
147
0
        {
148
0
            bValid = false;
149
0
            CPLError(CE_Warning, CPLE_AppDefined,
150
0
                     "Curve (fid=" CPL_FRMT_GIB
151
0
                     ") defined by less than two vertices",
152
0
                     poLine->GetFID());
153
0
        }
154
3.11k
        else if (EQUAL(ftype, "15") && npoints != 3)
155
0
        {
156
0
            bValid = false;
157
0
            CPLError(CE_Warning, CPLE_AppDefined,
158
0
                     "Circle (fid=" CPL_FRMT_GIB
159
0
                     ") defined by invalid number of vertices (%d)",
160
0
                     poLine->GetFID(), oOGRLine->getNumPoints());
161
0
        }
162
3.11k
        else if (strlen(ftype) > 2 && STARTS_WITH_CI(ftype, "15") &&
163
0
                 npoints != 1)
164
0
        {
165
0
            bValid = false;
166
0
            CPLError(CE_Warning, CPLE_AppDefined,
167
0
                     "Circle (fid=" CPL_FRMT_GIB
168
0
                     ") defined by invalid number of vertices (%d)",
169
0
                     poLine->GetFID(), oOGRLine->getNumPoints());
170
0
        }
171
3.11k
        else if (EQUAL(ftype, "16") && npoints != 3)
172
66
        {
173
66
            bValid = false;
174
66
            CPLError(CE_Warning, CPLE_AppDefined,
175
66
                     "Arc (fid=" CPL_FRMT_GIB
176
66
                     ") defined by invalid number of vertices (%d)",
177
66
                     poLine->GetFID(), oOGRLine->getNumPoints());
178
66
        }
179
3.11k
    }
180
181
    /* set geometry (NULL for invalid features) */
182
6.00k
    if (bValid)
183
3.05k
    {
184
3.05k
        if (!poLine->SetGeometry(oOGRLine, ftype))
185
2.92k
        {
186
2.92k
            bValid = false;
187
2.92k
        }
188
3.05k
    }
189
2.94k
    else
190
2.94k
    {
191
2.94k
        poLine->SetGeometry(nullptr);
192
2.94k
    }
193
194
    /* update fid column */
195
6.00k
    UpdateFID(poLine->GetFID(), rowIdFeat);
196
197
    /* store also geometry in DB */
198
6.00k
    CPLAssert(!rowIdFeat.empty());
199
6.00k
    if (bValid && poReader->IsSpatial() &&
200
128
        SaveGeometryToDB(poLine->GetGeometry(), rowIdFeat[0]) != OGRERR_FAILURE)
201
128
    {
202
128
        nGeometries++;
203
128
    }
204
205
6.00k
    rowIdFeat.clear();
206
6.00k
    oOGRLine->empty(); /* restore line */
207
208
6.00k
    return bValid;
209
6.00k
}
210
211
/*!
212
  \brief Load geometry (linestring SBP layer)
213
214
  \return number of invalid features
215
*/
216
int VFKDataBlockSQLite::LoadGeometryLineStringSBP()
217
263
{
218
263
    int nInvalid = 0;
219
220
263
    VFKDataBlockSQLite *poDataBlockPoints =
221
263
        cpl::down_cast<VFKDataBlockSQLite *>(m_poReader->GetDataBlock("SOBR"));
222
263
    if (nullptr == poDataBlockPoints)
223
91
    {
224
91
        CPLError(CE_Failure, CPLE_FileIO, "Data block %s not found.\n",
225
91
                 m_pszName);
226
91
        return nInvalid;
227
91
    }
228
229
172
    int nGeometries = 0;
230
172
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
231
232
172
    poDataBlockPoints->LoadGeometry();
233
234
172
    if (LoadGeometryFromDB()) /* try to load geometry from DB */
235
20
        return 0;
236
237
152
    CPLString osSQL;
238
152
    osSQL.Printf("UPDATE %s SET %s = -1", m_pszName, FID_COLUMN);
239
152
    poReader->ExecuteSQL(osSQL.c_str());
240
152
    bool bValid = true;
241
152
    int iIdx = 0;
242
243
152
    VFKFeatureSQLite *poLine = nullptr;
244
245
456
    for (int i = 0; i < 2; i++)
246
304
    {
247
        /* first collect linestrings related to HP, OB, DPM and ZVB
248
           then collect rest of linestrings */
249
304
        if (i == 0)
250
152
            osSQL.Printf(
251
152
                "SELECT BP_ID,PORADOVE_CISLO_BODU,PARAMETRY_SPOJENI,_rowid_ "
252
152
                "FROM '%s' WHERE "
253
152
                "HP_ID IS NOT NULL OR OB_ID IS NOT NULL OR DPM_ID IS NOT NULL "
254
152
                "OR ZVB_ID IS NOT NULL "
255
152
                "ORDER BY HP_ID,OB_ID,DPM_ID,ZVB_ID,PORADOVE_CISLO_BODU",
256
152
                m_pszName);
257
152
        else
258
152
            osSQL.Printf(
259
152
                "SELECT BP_ID,PORADOVE_CISLO_BODU,PARAMETRY_SPOJENI,_rowid_ "
260
152
                "FROM '%s' WHERE "
261
152
                "OB_ID IS NULL AND HP_ID IS NULL AND DPM_ID IS NULL AND ZVB_ID "
262
152
                "IS NULL "
263
152
                "ORDER BY ID,PORADOVE_CISLO_BODU",
264
152
                m_pszName);
265
266
304
        sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
267
268
304
        if (poReader->IsSpatial())
269
304
            poReader->ExecuteSQL("BEGIN");
270
271
304
        std::vector<int> rowIdFeat;
272
304
        CPLString osFType;
273
304
        OGRLineString oOGRLine;
274
275
11.7k
        while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
276
11.4k
        {
277
            // read values
278
11.4k
            const GUIntBig id = sqlite3_column_int64(hStmt, 0);
279
11.4k
            const GUIntBig ipcb = sqlite3_column_int64(hStmt, 1);
280
11.4k
            const char *pszFType =
281
11.4k
                reinterpret_cast<const char *>(sqlite3_column_text(hStmt, 2));
282
11.4k
            int rowId = sqlite3_column_int(hStmt, 3);
283
284
11.4k
            if (ipcb == 1)
285
6.01k
            {
286
6.01k
                VFKFeatureSQLite *poFeature =
287
6.01k
                    cpl::down_cast<VFKFeatureSQLite *>(GetFeatureByIndex(iIdx));
288
6.01k
                if (poFeature == nullptr)
289
15
                {
290
15
                    CPLError(CE_Failure, CPLE_AppDefined,
291
15
                             "Cannot retrieve feature %d", iIdx);
292
15
                    sqlite3_finalize(hStmt);
293
15
                    break;
294
15
                }
295
6.00k
                poFeature->SetRowId(rowId);
296
297
                /* set geometry & reset */
298
6.00k
                if (poLine && !SetGeometryLineString(poLine, &oOGRLine, bValid,
299
5.91k
                                                     osFType.c_str(), rowIdFeat,
300
5.91k
                                                     nGeometries))
301
5.80k
                {
302
5.80k
                    nInvalid++;
303
5.80k
                }
304
305
6.00k
                bValid = true;
306
6.00k
                poLine = poFeature;
307
6.00k
                osFType = pszFType ? pszFType : "";
308
6.00k
                iIdx++;
309
6.00k
            }
310
311
11.3k
            VFKFeatureSQLite *poPoint = cpl::down_cast<VFKFeatureSQLite *>(
312
11.3k
                poDataBlockPoints->GetFeature("ID", id));
313
11.3k
            if (poPoint)
314
5.62k
            {
315
5.62k
                const OGRGeometry *pt = poPoint->GetGeometry();
316
5.62k
                if (pt)
317
5.19k
                {
318
5.19k
                    oOGRLine.addPoint(pt->toPoint());
319
5.19k
                }
320
425
                else
321
425
                {
322
425
                    CPLDebug("OGR-VFK",
323
425
                             "Geometry (point ID = " CPL_FRMT_GUIB
324
425
                             ") not valid",
325
425
                             id);
326
425
                    bValid = false;
327
425
                }
328
5.62k
            }
329
5.77k
            else
330
5.77k
            {
331
5.77k
                CPLDebug("OGR-VFK",
332
5.77k
                         "Point ID = " CPL_FRMT_GUIB " not found (rowid = %d)",
333
5.77k
                         id, rowId);
334
5.77k
                bValid = false;
335
5.77k
            }
336
337
            /* add vertex to the linestring */
338
11.3k
            rowIdFeat.push_back(rowId);
339
11.3k
        }
340
341
        /* add last line */
342
304
        if (poLine &&
343
85
            !SetGeometryLineString(poLine, &oOGRLine, bValid, osFType.c_str(),
344
85
                                   rowIdFeat, nGeometries))
345
71
        {
346
71
            nInvalid++;
347
71
        }
348
304
        poLine = nullptr;
349
350
304
        if (poReader->IsSpatial())
351
304
            poReader->ExecuteSQL("COMMIT");
352
304
    }
353
354
    /* update number of geometries in VFK_DB_TABLE table */
355
152
    UpdateVfkBlocks(nGeometries);
356
357
152
    return nInvalid;
358
172
}
359
360
/*!
361
  \brief Load geometry (linestring HP/DPM/ZVB layer)
362
363
  \return number of invalid features
364
*/
365
int VFKDataBlockSQLite::LoadGeometryLineStringHP()
366
477
{
367
477
    int nInvalid = 0;
368
477
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
369
370
477
    VFKDataBlockSQLite *poDataBlockLines =
371
477
        cpl::down_cast<VFKDataBlockSQLite *>(m_poReader->GetDataBlock("SBP"));
372
477
    if (nullptr == poDataBlockLines)
373
301
    {
374
301
        CPLError(CE_Failure, CPLE_FileIO, "Data block %s not found.",
375
301
                 m_pszName);
376
301
        return nInvalid;
377
301
    }
378
379
176
    poDataBlockLines->LoadGeometry();
380
381
176
    if (LoadGeometryFromDB()) /* try to load geometry from DB */
382
5
        return 0;
383
384
171
    CPLString osColumn;
385
171
    osColumn.Printf("%s_ID", m_pszName);
386
171
    const char *vrColumn[2] = {osColumn.c_str(), "PORADOVE_CISLO_BODU"};
387
388
171
    GUIntBig vrValue[2] = {0, 1};  // Reduce to first segment.
389
390
171
    CPLString osSQL;
391
171
    osSQL.Printf("SELECT ID,%s,rowid FROM %s", FID_COLUMN, m_pszName);
392
    /* TODO: handle points in DPM */
393
171
    if (EQUAL(m_pszName, "DPM"))
394
41
        osSQL += " WHERE SOURADNICE_X IS NULL";
395
171
    sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
396
397
171
    if (poReader->IsSpatial())
398
171
        poReader->ExecuteSQL("BEGIN");
399
400
171
    int nGeometries = 0;
401
402
8.72k
    while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
403
8.55k
    {
404
        /* read values */
405
8.55k
        vrValue[0] = sqlite3_column_int64(hStmt, 0);
406
8.55k
        const GIntBig iFID = sqlite3_column_int64(hStmt, 1);
407
8.55k
        const int rowId = sqlite3_column_int(hStmt, 2);
408
409
8.55k
        VFKFeatureSQLite *poFeature =
410
8.55k
            cpl::down_cast<VFKFeatureSQLite *>(GetFeatureByIndex(rowId - 1));
411
8.55k
        if (poFeature == nullptr || poFeature->GetFID() != iFID)
412
2
        {
413
2
            continue;
414
2
        }
415
416
8.55k
        VFKFeatureSQLite *poLine =
417
8.55k
            poDataBlockLines->GetFeature(vrColumn, vrValue, 2, TRUE);
418
419
8.55k
        const OGRGeometry *poOgrGeometry =
420
8.55k
            poLine ? poLine->GetGeometry() : nullptr;
421
8.55k
        if (!poOgrGeometry || !poFeature->SetGeometry(poOgrGeometry))
422
4.56k
        {
423
4.56k
            CPLDebug("OGR-VFK",
424
4.56k
                     "VFKDataBlockSQLite::LoadGeometryLineStringHP(): name=%s "
425
4.56k
                     "fid=" CPL_FRMT_GIB " "
426
4.56k
                     "id=" CPL_FRMT_GUIB " -> %s geometry",
427
4.56k
                     m_pszName, iFID, vrValue[0],
428
4.56k
                     poOgrGeometry ? "invalid" : "empty");
429
4.56k
            nInvalid++;
430
4.56k
            continue;
431
4.56k
        }
432
433
        /* store also geometry in DB */
434
3.98k
        if (poReader->IsSpatial() &&
435
3.98k
            SaveGeometryToDB(poOgrGeometry, rowId) != OGRERR_FAILURE)
436
3.98k
            nGeometries++;
437
3.98k
    }
438
439
    /* update number of geometries in VFK_DB_TABLE table */
440
171
    UpdateVfkBlocks(nGeometries);
441
442
171
    if (poReader->IsSpatial())
443
171
        poReader->ExecuteSQL("COMMIT");
444
445
171
    return nInvalid;
446
176
}
447
448
/*!
449
  \brief Load geometry (polygon BUD/PAR layers)
450
451
  \return number of invalid features
452
*/
453
int VFKDataBlockSQLite::LoadGeometryPolygon()
454
152
{
455
152
#ifndef HAVE_GEOS
456
457
152
    CPLError(CE_Warning, CPLE_NotSupported,
458
152
             "GEOS support not enabled. Unable to build geometry for %s.",
459
152
             m_pszName);
460
152
    return -1;
461
462
#else
463
464
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
465
466
    VFKDataBlockSQLite *poDataBlockLines1 = nullptr;
467
    VFKDataBlockSQLite *poDataBlockLines2 = nullptr;
468
    bool bIsPar = false;
469
    if (EQUAL(m_pszName, "PAR"))
470
    {
471
        poDataBlockLines1 = cpl::down_cast<VFKDataBlockSQLite *>(
472
            m_poReader->GetDataBlock("HP"));
473
        poDataBlockLines2 = poDataBlockLines1;
474
        bIsPar = true;
475
    }
476
    else
477
    {
478
        poDataBlockLines1 = cpl::down_cast<VFKDataBlockSQLite *>(
479
            m_poReader->GetDataBlock("OB"));
480
        poDataBlockLines2 = cpl::down_cast<VFKDataBlockSQLite *>(
481
            m_poReader->GetDataBlock("SBP"));
482
        bIsPar = false;
483
    }
484
    if (nullptr == poDataBlockLines1)
485
    {
486
        CPLError(CE_Warning, CPLE_FileIO,
487
                 "Data block %s not found. Unable to build geometry for %s.",
488
                 bIsPar ? "HP" : "OB", m_pszName);
489
        return -1;
490
    }
491
    if (nullptr == poDataBlockLines2)
492
    {
493
        CPLError(CE_Warning, CPLE_FileIO,
494
                 "Data block %s not found. Unable to build geometry for %s.",
495
                 "SBP", m_pszName);
496
        return -1;
497
    }
498
499
    poDataBlockLines1->LoadGeometry();
500
    poDataBlockLines2->LoadGeometry();
501
502
    if (LoadGeometryFromDB())  // Try to load geometry from DB.
503
        return 0;
504
505
    const char *vrColumn[2] = {nullptr, nullptr};
506
    GUIntBig vrValue[2] = {0, 0};
507
    if (bIsPar)
508
    {
509
        vrColumn[0] = "PAR_ID_1";
510
        vrColumn[1] = "PAR_ID_2";
511
    }
512
    else
513
    {
514
        vrColumn[0] = "OB_ID";
515
        vrColumn[1] = "PORADOVE_CISLO_BODU";
516
        vrValue[1] = 1;
517
    }
518
519
    CPLString osSQL;
520
    osSQL.Printf("SELECT ID,%s,rowid FROM %s", FID_COLUMN, m_pszName);
521
    sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
522
523
    if (poReader->IsSpatial())
524
        poReader->ExecuteSQL("BEGIN");
525
526
    int nInvalidNoLines = 0;
527
    int nInvalidNoRings = 0;
528
    int nGeometries = 0;
529
530
    while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
531
    {
532
        /* read values */
533
        const GUIntBig id = sqlite3_column_int64(hStmt, 0);
534
        const long iFID = static_cast<long>(sqlite3_column_int64(hStmt, 1));
535
        const int rowId = sqlite3_column_int(hStmt, 2);
536
537
        VFKFeatureSQLite *poFeature =
538
            cpl::down_cast<VFKFeatureSQLite *>(GetFeatureByIndex(rowId - 1));
539
        if (poFeature == nullptr || poFeature->GetFID() != iFID)
540
        {
541
            continue;
542
        }
543
544
        /* collect boundary lines */
545
        VFKFeatureSQLiteList poLineList;
546
        if (bIsPar)
547
        {
548
            vrValue[0] = vrValue[1] = id;
549
            poLineList = poDataBlockLines1->GetFeatures(vrColumn, vrValue, 2);
550
        }
551
        else
552
        {
553
            osSQL.Printf("SELECT ID FROM %s WHERE BUD_ID = " CPL_FRMT_GUIB,
554
                         poDataBlockLines1->GetName(), id);
555
            if (poReader->IsSpatial())
556
            {
557
                CPLString osColumn;
558
559
                osColumn.Printf(" AND %s IS NULL", GEOM_COLUMN);
560
                osSQL += osColumn;
561
            }
562
            sqlite3_stmt *hStmtOb = poReader->PrepareStatement(osSQL.c_str());
563
564
            while (poReader->ExecuteSQL(hStmtOb) == OGRERR_NONE)
565
            {
566
                const GUIntBig idOb = sqlite3_column_int64(hStmtOb, 0);
567
                vrValue[0] = idOb;
568
                VFKFeatureSQLite *poLineSbp =
569
                    poDataBlockLines2->GetFeature(vrColumn, vrValue, 2);
570
                if (poLineSbp)
571
                    poLineList.push_back(poLineSbp);
572
            }
573
        }
574
        if (poLineList.empty())
575
        {
576
            CPLDebug("OGR-VFK", "%s: no lines to polygonize (fid = %ld)",
577
                     m_pszName, iFID);
578
            nInvalidNoLines++;
579
            continue;
580
        }
581
582
        OGRMultiLineString oMultiLine;
583
        for (VFKFeatureSQLite *poLineFeature : poLineList)
584
        {
585
            const OGRGeometry *poLineGeom = poLineFeature->GetGeometry();
586
            if (poLineGeom)
587
            {
588
                oMultiLine.addGeometry(poLineGeom);
589
            }
590
        }
591
592
        /* polygonize using GEOSBuildArea() */
593
        auto poPolygonGeom =
594
            std::unique_ptr<OGRGeometry>(oMultiLine.BuildArea());
595
        if (!poPolygonGeom || poPolygonGeom->IsEmpty())
596
        {
597
            CPLDebug("OGR-VFK", "%s: unable to polygonize (fid = %ld)",
598
                     m_pszName, iFID);
599
            nInvalidNoRings++;
600
            continue;
601
        }
602
603
        /* coerce to MultiPolygon if needed and check the geometry type */
604
        const auto eGeomType = wkbFlatten(poPolygonGeom->getGeometryType());
605
        if ((eGeomType == wkbPolygon) && (m_nGeometryType == wkbMultiPolygon))
606
        {
607
            auto poMP = std::make_unique<OGRMultiPolygon>();
608
            poMP->addGeometryDirectly(poPolygonGeom.release());
609
            poPolygonGeom = std::move(poMP);
610
        }
611
        else if (eGeomType != m_nGeometryType)
612
        {
613
            CPLDebug("OGR-VFK", "%s: invalid geometry type %d (fid = %ld)",
614
                     m_pszName, (int)eGeomType, iFID);
615
            nInvalidNoRings++;
616
            continue;
617
        }
618
619
        /* set geometry */
620
        poPolygonGeom->setCoordinateDimension(2); /* force 2D */
621
        if (!poFeature->SetGeometry(poPolygonGeom.get()))
622
        {
623
            nInvalidNoRings++;
624
            continue;
625
        }
626
627
        /* store also geometry in DB */
628
        if (poReader->IsSpatial() &&
629
            SaveGeometryToDB(poPolygonGeom.get(), rowId) != OGRERR_FAILURE)
630
            nGeometries++;
631
    }
632
633
    CPLDebug("OGR-VFK", "%s: nolines = %d norings = %d", m_pszName,
634
             nInvalidNoLines, nInvalidNoRings);
635
636
    /* update number of geometries in VFK_DB_TABLE table */
637
    UpdateVfkBlocks(nGeometries);
638
639
    if (poReader->IsSpatial())
640
        poReader->ExecuteSQL("COMMIT");
641
642
    return nInvalidNoLines + nInvalidNoRings;
643
#endif  // HAVE_GEOS
644
152
}
645
646
/*!
647
  \brief Get feature by FID
648
649
  Modifies next feature id.
650
651
  \param nFID feature id
652
653
  \return pointer to feature definition or NULL on failure (not found)
654
*/
655
IVFKFeature *VFKDataBlockSQLite::GetFeature(GIntBig nFID)
656
0
{
657
0
    if (m_nFeatureCount < 0)
658
0
    {
659
0
        m_poReader->ReadDataRecords(this);
660
0
    }
661
662
0
    if (nFID < 1 || nFID > m_nFeatureCount)
663
0
        return nullptr;
664
665
0
    if (m_bGeometryPerBlock && !m_bGeometry)
666
0
    {
667
0
        LoadGeometry();
668
0
    }
669
670
0
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
671
672
0
    CPLString osSQL;
673
0
    osSQL.Printf("SELECT rowid FROM %s WHERE %s = " CPL_FRMT_GIB, m_pszName,
674
0
                 FID_COLUMN, nFID);
675
0
    if (EQUAL(m_pszName, "SBP") || EQUAL(m_pszName, "SBPG"))
676
0
    {
677
0
        osSQL += " AND PORADOVE_CISLO_BODU = 1";
678
0
    }
679
0
    sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
680
681
0
    int rowId = -1;
682
0
    if (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
683
0
    {
684
0
        rowId = sqlite3_column_int(hStmt, 0);
685
0
    }
686
0
    sqlite3_finalize(hStmt);
687
688
0
    return GetFeatureByIndex(rowId - 1);
689
0
}
690
691
/*!
692
  \brief Get first found feature based on its property
693
694
  \param column property name
695
  \param value property value
696
  \param bGeom True to check also geometry != NULL
697
698
  \return pointer to feature definition or NULL on failure (not found)
699
*/
700
VFKFeatureSQLite *VFKDataBlockSQLite::GetFeature(const char *column,
701
                                                 GUIntBig value, bool bGeom)
702
11.3k
{
703
11.3k
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
704
705
11.3k
    CPLString osSQL;
706
11.3k
    osSQL.Printf("SELECT %s from %s WHERE %s = " CPL_FRMT_GUIB, FID_COLUMN,
707
11.3k
                 m_pszName, column, value);
708
11.3k
    if (bGeom)
709
0
    {
710
0
        CPLString osColumn;
711
712
0
        osColumn.Printf(" AND %s IS NOT NULL", GEOM_COLUMN);
713
0
        osSQL += osColumn;
714
0
    }
715
716
11.3k
    sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
717
11.3k
    if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE)
718
5.77k
        return nullptr;
719
720
5.62k
    const int idx = sqlite3_column_int(hStmt, 0) - 1;
721
5.62k
    sqlite3_finalize(hStmt);
722
723
5.62k
    if (idx < 0 || idx >= m_nFeatureCount)  // ? assert
724
3
        return nullptr;
725
726
5.62k
    return cpl::down_cast<VFKFeatureSQLite *>(GetFeatureByIndex(idx));
727
5.62k
}
728
729
/*!
730
  \brief Get first found feature based on its properties (AND)
731
732
  \param column array of property names
733
  \param value array of property values
734
  \param num number of array items
735
  \param bGeom True to check also geometry != NULL
736
737
  \return pointer to feature definition or NULL on failure (not found)
738
*/
739
VFKFeatureSQLite *VFKDataBlockSQLite::GetFeature(const char **column,
740
                                                 GUIntBig *value, int num,
741
                                                 bool bGeom)
742
8.55k
{
743
8.55k
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
744
745
8.55k
    CPLString osSQL;
746
8.55k
    osSQL.Printf("SELECT %s FROM %s WHERE ", FID_COLUMN, m_pszName);
747
748
8.55k
    CPLString osItem;
749
25.6k
    for (int i = 0; i < num; i++)
750
17.1k
    {
751
17.1k
        if (i > 0)
752
8.55k
            osItem.Printf(" AND %s = " CPL_FRMT_GUIB, column[i], value[i]);
753
8.55k
        else
754
8.55k
            osItem.Printf("%s = " CPL_FRMT_GUIB, column[i], value[i]);
755
17.1k
        osSQL += osItem;
756
17.1k
    }
757
8.55k
    if (bGeom)
758
8.55k
    {
759
8.55k
        osItem.Printf(" AND %s IS NOT NULL", GEOM_COLUMN);
760
8.55k
        osSQL += osItem;
761
8.55k
    }
762
763
8.55k
    sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
764
8.55k
    if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE)
765
4.56k
        return nullptr;
766
767
3.98k
    int idx = sqlite3_column_int(hStmt, 0) - 1; /* rowid starts at 1 */
768
3.98k
    sqlite3_finalize(hStmt);
769
770
3.98k
    if (idx < 0 || idx >= m_nFeatureCount)  // ? assert
771
0
        return nullptr;
772
773
3.98k
    return cpl::down_cast<VFKFeatureSQLite *>(GetFeatureByIndex(idx));
774
3.98k
}
775
776
/*!
777
  \brief Get features based on properties
778
779
  \param column array of property names
780
  \param value array of property values
781
  \param num number of array items
782
783
  \return list of features
784
*/
785
VFKFeatureSQLiteList VFKDataBlockSQLite::GetFeatures(const char **column,
786
                                                     GUIntBig *value, int num)
787
0
{
788
0
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
789
790
0
    CPLString osItem;
791
0
    CPLString osSQL;
792
0
    osSQL.Printf("SELECT rowid from %s WHERE ", m_pszName);
793
0
    for (int i = 0; i < num; i++)
794
0
    {
795
0
        if (i > 0)
796
0
            osItem.Printf(" OR %s = " CPL_FRMT_GUIB, column[i], value[i]);
797
0
        else
798
0
            osItem.Printf("%s = " CPL_FRMT_GUIB, column[i], value[i]);
799
0
        osSQL += osItem;
800
0
    }
801
0
    osSQL += " ORDER BY ";
802
0
    osSQL += FID_COLUMN;
803
804
0
    VFKFeatureSQLiteList fList;
805
806
0
    sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
807
0
    while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
808
0
    {
809
0
        const int iRowId = sqlite3_column_int(hStmt, 0);
810
0
        VFKFeatureSQLite *poFeature =
811
0
            dynamic_cast<VFKFeatureSQLite *>(GetFeatureByIndex(iRowId - 1));
812
0
        if (poFeature == nullptr)
813
0
        {
814
0
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot retrieve feature %d",
815
0
                     iRowId);
816
0
            sqlite3_finalize(hStmt);
817
0
            return VFKFeatureSQLiteList();
818
0
        }
819
0
        fList.push_back(poFeature);
820
0
    }
821
822
0
    return fList;
823
0
}
824
825
/*!
826
  \brief Save geometry to DB (as WKB)
827
828
  \param poGeom pointer to OGRGeometry to be saved
829
  \param iRowId row id to update
830
831
  \return OGRERR_NONE on success otherwise OGRERR_FAILURE
832
*/
833
OGRErr VFKDataBlockSQLite::SaveGeometryToDB(const OGRGeometry *poGeom,
834
                                            int iRowId)
835
8.25k
{
836
8.25k
    int rc;
837
8.25k
    CPLString osSQL;
838
839
8.25k
    sqlite3_stmt *hStmt = nullptr;
840
841
8.25k
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
842
843
    /* check if geometry column exists (see SUPPRESS_GEOMETRY open
844
       option) */
845
8.25k
    if (AddGeometryColumn() != OGRERR_NONE)
846
0
        return OGRERR_FAILURE;
847
848
8.25k
    if (poGeom)
849
8.25k
    {
850
8.25k
        const size_t nWKBLen = poGeom->WkbSize();
851
8.25k
        if (nWKBLen > static_cast<size_t>(std::numeric_limits<int>::max()))
852
0
        {
853
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too large geometry");
854
0
            return OGRERR_FAILURE;
855
0
        }
856
8.25k
        GByte *pabyWKB = (GByte *)VSI_MALLOC_VERBOSE(nWKBLen);
857
8.25k
        if (pabyWKB)
858
8.25k
        {
859
8.25k
            poGeom->exportToWkb(wkbNDR, pabyWKB);
860
861
8.25k
            osSQL.Printf("UPDATE %s SET %s = ? WHERE rowid = %d", m_pszName,
862
8.25k
                         GEOM_COLUMN, iRowId);
863
8.25k
            hStmt = poReader->PrepareStatement(osSQL.c_str());
864
865
8.25k
            rc = sqlite3_bind_blob(hStmt, 1, pabyWKB, static_cast<int>(nWKBLen),
866
8.25k
                                   CPLFree);
867
8.25k
            if (rc != SQLITE_OK)
868
0
            {
869
0
                sqlite3_finalize(hStmt);
870
0
                CPLError(CE_Failure, CPLE_AppDefined,
871
0
                         "Storing geometry in DB failed");
872
0
                return OGRERR_FAILURE;
873
0
            }
874
8.25k
        }
875
8.25k
    }
876
0
    else
877
0
    { /* invalid */
878
0
        osSQL.Printf("UPDATE %s SET %s = NULL WHERE rowid = %d", m_pszName,
879
0
                     GEOM_COLUMN, iRowId);
880
0
        hStmt = poReader->PrepareStatement(osSQL.c_str());
881
0
    }
882
883
8.25k
    return poReader->ExecuteSQL(hStmt); /* calls sqlite3_finalize() */
884
8.25k
}
885
886
/*!
887
  \brief Load geometry from DB
888
889
  \return true if geometry successfully loaded otherwise false
890
*/
891
bool VFKDataBlockSQLite::LoadGeometryFromDB()
892
775
{
893
775
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
894
895
775
    if (!poReader->IsSpatial()) /* check if DB is spatial */
896
0
        return false;
897
898
775
    CPLString osSQL;
899
775
    osSQL.Printf("SELECT num_geometries FROM %s WHERE table_name = '%s'",
900
775
                 VFK_DB_TABLE, m_pszName);
901
775
    sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
902
775
    if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE)
903
212
        return false;
904
563
    const int nGeometries = sqlite3_column_int(hStmt, 0);
905
563
    sqlite3_finalize(hStmt);
906
907
563
    if (nGeometries < 1)
908
472
        return false;
909
910
91
    const bool bSkipInvalid = EQUAL(m_pszName, "OB") ||
911
91
                              EQUAL(m_pszName, "OP") ||
912
91
                              EQUAL(m_pszName, "OBBP");
913
914
    /* load geometry from DB */
915
91
    osSQL.Printf("SELECT %s,rowid,%s FROM %s ", GEOM_COLUMN, FID_COLUMN,
916
91
                 m_pszName);
917
91
    if (EQUAL(m_pszName, "SBP") || EQUAL(m_pszName, "SBPG"))
918
20
        osSQL += "WHERE PORADOVE_CISLO_BODU = 1 ";
919
91
    osSQL += "ORDER BY ";
920
91
    osSQL += FID_COLUMN;
921
91
    hStmt = poReader->PrepareStatement(osSQL.c_str());
922
923
91
    int rowId = 0;
924
91
    int nInvalid = 0;
925
91
    int nGeometriesCount = 0;
926
927
7.08k
    while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
928
6.99k
    {
929
6.99k
        rowId++;  // =sqlite3_column_int(hStmt, 1);
930
6.99k
        const GIntBig iFID = sqlite3_column_int64(hStmt, 2);
931
6.99k
        VFKFeatureSQLite *poFeature =
932
6.99k
            dynamic_cast<VFKFeatureSQLite *>(GetFeatureByIndex(rowId - 1));
933
6.99k
        if (poFeature == nullptr || poFeature->GetFID() != iFID)
934
138
        {
935
138
            continue;
936
138
        }
937
938
        // read geometry from DB
939
6.85k
        const int nBytes = sqlite3_column_bytes(hStmt, 0);
940
6.85k
        OGRGeometry *poGeometry = nullptr;
941
6.85k
        if (nBytes > 0 && OGRGeometryFactory::createFromWkb(
942
1.17k
                              sqlite3_column_blob(hStmt, 0), nullptr,
943
1.17k
                              &poGeometry, nBytes) == OGRERR_NONE)
944
1.17k
        {
945
1.17k
            nGeometriesCount++;
946
1.17k
            if (!poFeature->SetGeometry(poGeometry))
947
0
            {
948
0
                nInvalid++;
949
0
            }
950
1.17k
            delete poGeometry;
951
1.17k
        }
952
5.68k
        else
953
5.68k
        {
954
5.68k
            nInvalid++;
955
5.68k
        }
956
6.85k
    }
957
958
91
    CPLDebug("OGR-VFK", "%s: %d geometries loaded from DB", m_pszName,
959
91
             nGeometriesCount);
960
961
91
    if (nGeometriesCount != nGeometries)
962
26
    {
963
26
        CPLError(CE_Warning, CPLE_AppDefined,
964
26
                 "%s: %d geometries loaded (should be %d)", m_pszName,
965
26
                 nGeometriesCount, nGeometries);
966
26
    }
967
968
91
    if (nInvalid > 0 && !bSkipInvalid)
969
66
    {
970
66
        CPLError(CE_Warning, CPLE_AppDefined,
971
66
                 "%s: %d features with invalid or empty geometry", m_pszName,
972
66
                 nInvalid);
973
66
    }
974
975
91
    return true;
976
563
}
977
978
/*!
979
  \brief Update VFK_DB_TABLE table
980
981
  \param nGeometries number of geometries to update
982
*/
983
void VFKDataBlockSQLite::UpdateVfkBlocks(int nGeometries)
984
684
{
985
684
    CPLString osSQL;
986
987
684
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
988
989
    /* update number of features in VFK_DB_TABLE table */
990
684
    const int nFeatCount = (int)GetFeatureCount();
991
684
    if (nFeatCount > 0)
992
231
    {
993
231
        osSQL.Printf("UPDATE %s SET num_features = %d WHERE table_name = '%s'",
994
231
                     VFK_DB_TABLE, nFeatCount, m_pszName);
995
231
        poReader->ExecuteSQL(osSQL.c_str());
996
231
    }
997
998
    /* update number of geometries in VFK_DB_TABLE table */
999
684
    if (nGeometries > 0)
1000
159
    {
1001
159
        CPLDebug("OGR-VFK",
1002
159
                 "VFKDataBlockSQLite::UpdateVfkBlocks(): name=%s -> "
1003
159
                 "%d geometries saved to internal DB",
1004
159
                 m_pszName, nGeometries);
1005
1006
159
        osSQL.Printf(
1007
159
            "UPDATE %s SET num_geometries = %d WHERE table_name = '%s'",
1008
159
            VFK_DB_TABLE, nGeometries, m_pszName);
1009
159
        poReader->ExecuteSQL(osSQL.c_str());
1010
159
    }
1011
684
}
1012
1013
/*!
1014
  \brief Update feature id (see SBP)
1015
1016
  \param iFID feature id to set up
1017
  \param rowId list of rows to update
1018
*/
1019
void VFKDataBlockSQLite::UpdateFID(GIntBig iFID, const std::vector<int> &rowId)
1020
6.00k
{
1021
6.00k
    CPLString osSQL, osValue;
1022
6.00k
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
1023
1024
    /* update number of geometries in VFK_DB_TABLE table */
1025
6.00k
    osSQL.Printf("UPDATE %s SET %s = " CPL_FRMT_GIB " WHERE rowid IN (",
1026
6.00k
                 m_pszName, FID_COLUMN, iFID);
1027
17.3k
    for (size_t i = 0; i < rowId.size(); i++)
1028
11.3k
    {
1029
11.3k
        if (i > 0)
1030
5.38k
            osValue.Printf(",%d", rowId[i]);
1031
6.00k
        else
1032
6.00k
            osValue.Printf("%d", rowId[i]);
1033
11.3k
        osSQL += osValue;
1034
11.3k
    }
1035
6.00k
    osSQL += ")";
1036
1037
6.00k
    poReader->ExecuteSQL(osSQL.c_str());
1038
6.00k
}
1039
1040
/*!
1041
  \brief Check is ring is closed
1042
1043
  \param poRing pointer to OGRLinearRing to check
1044
1045
  \return true if closed otherwise false
1046
*/
1047
bool VFKDataBlockSQLite::IsRingClosed(const OGRLinearRing *poRing)
1048
0
{
1049
0
    const int nPoints = poRing->getNumPoints();
1050
0
    if (nPoints < 3)
1051
0
        return false;
1052
1053
0
    if (poRing->getX(0) == poRing->getX(nPoints - 1) &&
1054
0
        poRing->getY(0) == poRing->getY(nPoints - 1))
1055
0
        return true;
1056
1057
0
    return false;
1058
0
}
1059
1060
/*!
1061
  \brief Get primary key
1062
1063
  \return property name or NULL
1064
*/
1065
const char *VFKDataBlockSQLite::GetKey() const
1066
1.09k
{
1067
1.09k
    if (GetPropertyCount() > 1)
1068
908
    {
1069
908
        const VFKPropertyDefn *poPropDefn = GetProperty(0);
1070
908
        const char *pszKey = poPropDefn->GetName();
1071
908
        if (EQUAL(pszKey, "ID"))
1072
782
            return pszKey;
1073
908
    }
1074
1075
316
    return nullptr;
1076
1.09k
}
1077
1078
/*!
1079
  \brief Get geometry SQL type (for geometry_columns table)
1080
1081
  \return geometry_type as integer
1082
*/
1083
int VFKDataBlockSQLite::GetGeometrySQLType() const
1084
1.65k
{
1085
1.65k
    if (m_nGeometryType == wkbMultiPolygon)
1086
35
        return 6;
1087
1.61k
    else if (m_nGeometryType == wkbPolygon)
1088
13
        return 3;
1089
1.60k
    else if (m_nGeometryType == wkbLineString)
1090
196
        return 2;
1091
1.40k
    else if (m_nGeometryType == wkbPoint)
1092
175
        return 1;
1093
1094
1.23k
    return 0; /* unknown geometry type */
1095
1.65k
}
1096
1097
/*!
1098
  \brief Add geometry column into table if not exists
1099
1100
  \return OGRERR_NONE on success otherwise OGRERR_FAILURE
1101
 */
1102
OGRErr VFKDataBlockSQLite::AddGeometryColumn() const
1103
8.54k
{
1104
8.54k
    CPLString osSQL;
1105
1106
8.54k
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
1107
1108
8.54k
    osSQL.Printf("SELECT %s FROM %s LIMIT 0", GEOM_COLUMN, m_pszName);
1109
8.54k
    if (poReader->ExecuteSQL(osSQL.c_str(), CE_None) == OGRERR_FAILURE)
1110
86
    {
1111
        /* query failed, we assume that geometry column not exists */
1112
86
        osSQL.Printf("ALTER TABLE %s ADD COLUMN %s blob", m_pszName,
1113
86
                     GEOM_COLUMN);
1114
86
        return poReader->ExecuteSQL(osSQL.c_str());
1115
86
    }
1116
1117
8.46k
    return OGRERR_NONE;
1118
8.54k
}
1119
1120
/*!
1121
  \brief Load feature properties
1122
1123
  Used for sequential access, see OGRVFKLayer:GetNextFeature().
1124
1125
  \return OGRERR_NONE on success otherwise OGRERR_FAILURE
1126
*/
1127
OGRErr VFKDataBlockSQLite::LoadProperties()
1128
4.93k
{
1129
4.93k
    CPLString osSQL;
1130
1131
4.93k
    if (m_hStmt)
1132
0
        sqlite3_finalize(m_hStmt);
1133
1134
4.93k
    osSQL.Printf("SELECT * FROM %s",  // TODO: where
1135
4.93k
                 m_pszName);
1136
4.93k
    if (EQUAL(m_pszName, "SBP") || EQUAL(m_pszName, "SBPG"))
1137
192
        osSQL += " WHERE PORADOVE_CISLO_BODU = 1";
1138
1139
4.93k
    m_hStmt = cpl::down_cast<VFKReaderSQLite *>(m_poReader)
1140
4.93k
                  ->PrepareStatement(osSQL.c_str());
1141
1142
4.93k
    if (m_hStmt == nullptr)
1143
4.05k
        return OGRERR_FAILURE;
1144
1145
876
    return OGRERR_NONE;
1146
4.93k
}
1147
1148
/*
1149
  \brief Clean feature properties for a next run
1150
1151
  \return OGRERR_NONE on success otherwise OGRERR_FAILURE
1152
*/
1153
OGRErr VFKDataBlockSQLite::CleanProperties()
1154
14.9k
{
1155
14.9k
    if (m_hStmt)
1156
876
    {
1157
876
        if (sqlite3_finalize(m_hStmt) != SQLITE_OK)
1158
0
        {
1159
0
            m_hStmt = nullptr;
1160
0
            return OGRERR_FAILURE;
1161
0
        }
1162
876
        m_hStmt = nullptr;
1163
876
    }
1164
1165
14.9k
    return OGRERR_NONE;
1166
14.9k
}