Coverage Report

Created: 2026-09-26 08:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/vfk/vfkreadersqlite.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  VFK Reader (SQLite)
4
 * Purpose:  Implements VFKReaderSQLite class.
5
 * Author:   Martin Landa, landa.martin gmail.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2012-2018, Martin Landa <landa.martin gmail.com>
9
 * Copyright (c) 2012-2018, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_vsi.h"
15
16
#include "vfkreader.h"
17
#include "vfkreaderp.h"
18
19
#include "cpl_conv.h"
20
#include "cpl_error.h"
21
22
#include <cstring>
23
24
#include "ogr_geometry.h"
25
26
/*!
27
  \brief VFKReaderSQLite constructor
28
*/
29
VFKReaderSQLite::VFKReaderSQLite(const GDALOpenInfo *poOpenInfo)
30
3.88k
    : VFKReader(poOpenInfo), m_pszDBname(nullptr), m_poDB(nullptr),
31
      // True - build geometry from DB
32
      // False - store also geometry in DB
33
3.88k
      m_bSpatial(CPLTestBool(CPLGetConfigOption("OGR_VFK_DB_SPATIAL", "YES"))),
34
3.88k
      m_bNewDb(false), m_bDbSource(false)
35
3.88k
{
36
3.88k
    size_t nLen = 0;
37
3.88k
    VSIStatBufL sStatBufDb;
38
39
3.88k
    m_bDbSource =
40
3.88k
        poOpenInfo->nHeaderBytes >= 16 &&
41
3.80k
        STARTS_WITH((const char *)poOpenInfo->pabyHeader, "SQLite format 3");
42
43
3.88k
    const char *pszDbNameConf = CPLGetConfigOption("OGR_VFK_DB_NAME", nullptr);
44
3.88k
    CPLString osDbName;
45
46
3.88k
    if (!m_bDbSource)
47
3.85k
    {
48
3.85k
        m_bNewDb = true;
49
50
        /* open tmp SQLite DB (re-use DB file if already exists) */
51
3.85k
        if (pszDbNameConf)
52
0
        {
53
0
            osDbName = pszDbNameConf;
54
0
        }
55
3.85k
        else
56
3.85k
        {
57
3.85k
            osDbName = CPLResetExtensionSafe(m_pszFilename, "db");
58
3.85k
        }
59
3.85k
        nLen = osDbName.length();
60
3.85k
        if (nLen > 2048)
61
13
        {
62
13
            nLen = 2048;
63
13
            osDbName.resize(nLen);
64
13
        }
65
3.85k
    }
66
23
    else
67
23
    {
68
        // m_bNewDb = false;
69
23
        nLen = strlen(m_pszFilename);
70
23
        osDbName = m_pszFilename;
71
23
    }
72
73
3.88k
    m_pszDBname = new char[nLen + 1];
74
3.88k
    std::strncpy(m_pszDBname, osDbName.c_str(), nLen);
75
3.88k
    m_pszDBname[nLen] = 0;
76
77
3.88k
    CPLDebug("OGR-VFK", "Using internal DB: %s", m_pszDBname);
78
79
3.88k
    if (!m_bDbSource && VSIStatL(osDbName, &sStatBufDb) == 0)
80
831
    {
81
        /* Internal DB exists */
82
831
        if (CPLTestBool(CPLGetConfigOption("OGR_VFK_DB_OVERWRITE", "NO")))
83
0
        {
84
0
            m_bNewDb = true;  // Overwrite existing DB.
85
0
            CPLDebug("OGR-VFK",
86
0
                     "Internal DB (%s) already exists and will be overwritten",
87
0
                     m_pszDBname);
88
0
            VSIUnlink(osDbName);
89
0
        }
90
831
        else
91
831
        {
92
831
            if (pszDbNameConf == nullptr &&
93
831
                m_poFStat->st_mtime > sStatBufDb.st_mtime)
94
277
            {
95
277
                CPLDebug("OGR-VFK",
96
277
                         "Found %s but ignoring because it appears\n"
97
277
                         "be older than the associated VFK file.",
98
277
                         osDbName.c_str());
99
277
                m_bNewDb = true;
100
277
                VSIUnlink(osDbName);
101
277
            }
102
554
            else
103
554
            {
104
554
                m_bNewDb = false; /* re-use existing DB */
105
554
            }
106
831
        }
107
831
    }
108
109
3.88k
    CPLDebug("OGR-VFK", "New DB: %s Spatial: %s", m_bNewDb ? "yes" : "no",
110
3.88k
             m_bSpatial ? "yes" : "no");
111
112
3.88k
    if (SQLITE_OK != sqlite3_open(osDbName, &m_poDB))
113
3.03k
    {
114
3.03k
        CPLError(CE_Failure, CPLE_AppDefined, "Creating SQLite DB failed: %s",
115
3.03k
                 sqlite3_errmsg(m_poDB));
116
3.03k
    }
117
118
3.88k
    CPLString osCommand;
119
3.88k
    if (m_bDbSource)
120
23
    {
121
        /* check if it is really VFK DB datasource */
122
23
        char *pszErrMsg = nullptr;
123
23
        char **papszResult = nullptr;
124
23
        int nRowCount = 0;
125
23
        int nColCount = 0;
126
127
23
        osCommand.Printf(
128
23
            "SELECT * FROM sqlite_master WHERE type='table' AND name='%s'",
129
23
            VFK_DB_TABLE);
130
23
        sqlite3_get_table(m_poDB, osCommand.c_str(), &papszResult, &nRowCount,
131
23
                          &nColCount, &pszErrMsg);
132
23
        sqlite3_free_table(papszResult);
133
23
        sqlite3_free(pszErrMsg);
134
135
23
        if (nRowCount != 1)
136
23
        {
137
            /* DB is not valid VFK datasource */
138
23
            sqlite3_close(m_poDB);
139
23
            m_poDB = nullptr;
140
23
            return;
141
23
        }
142
23
    }
143
144
3.85k
    if (!m_bNewDb)
145
554
    {
146
        /* check if DB is up-to-date datasource */
147
554
        char *pszErrMsg = nullptr;
148
554
        char **papszResult = nullptr;
149
554
        int nRowCount = 0;
150
554
        int nColCount = 0;
151
152
554
        osCommand.Printf("SELECT * FROM %s LIMIT 1", VFK_DB_TABLE);
153
554
        sqlite3_get_table(m_poDB, osCommand.c_str(), &papszResult, &nRowCount,
154
554
                          &nColCount, &pszErrMsg);
155
554
        sqlite3_free_table(papszResult);
156
554
        sqlite3_free(pszErrMsg);
157
158
554
        if (nColCount != 7)
159
139
        {
160
            /* it seems that DB is outdated, let's create new DB from
161
             * scratch */
162
139
            if (m_bDbSource)
163
0
            {
164
0
                CPLError(CE_Failure, CPLE_AppDefined,
165
0
                         "Invalid VFK DB datasource");
166
0
            }
167
168
139
            if (SQLITE_OK != sqlite3_close(m_poDB))
169
0
            {
170
0
                CPLError(CE_Failure, CPLE_AppDefined,
171
0
                         "Closing SQLite DB failed: %s",
172
0
                         sqlite3_errmsg(m_poDB));
173
0
            }
174
139
            VSIUnlink(osDbName);
175
139
            if (SQLITE_OK != sqlite3_open(osDbName, &m_poDB))
176
1
            {
177
1
                CPLError(CE_Failure, CPLE_AppDefined,
178
1
                         "Creating SQLite DB failed: %s",
179
1
                         sqlite3_errmsg(m_poDB));
180
1
            }
181
139
            CPLDebug("OGR-VFK",
182
139
                     "Internal DB (%s) is invalid - will be re-created",
183
139
                     m_pszDBname);
184
185
139
            m_bNewDb = true;
186
139
        }
187
554
    }
188
189
3.85k
    char *pszErrMsg = nullptr;
190
3.85k
    CPL_IGNORE_RET_VAL(sqlite3_exec(m_poDB, "PRAGMA synchronous = OFF", nullptr,
191
3.85k
                                    nullptr, &pszErrMsg));
192
3.85k
    sqlite3_free(pszErrMsg);
193
194
3.85k
    if (m_bNewDb)
195
3.44k
    {
196
3.44k
        OGRSpatialReference *poSRS;
197
198
        /* new DB, create support metadata tables */
199
3.44k
        osCommand.Printf(
200
3.44k
            "CREATE TABLE %s (file_name text, file_size integer, "
201
3.44k
            "table_name text, num_records integer, "
202
3.44k
            "num_features integer, num_geometries integer, table_defn text)",
203
3.44k
            VFK_DB_TABLE);
204
3.44k
        ExecuteSQL(osCommand.c_str());
205
206
        /* header table */
207
3.44k
        osCommand.Printf("CREATE TABLE %s (key text, value text)",
208
3.44k
                         VFK_DB_HEADER_TABLE);
209
3.44k
        ExecuteSQL(osCommand.c_str());
210
211
        /* geometry_columns */
212
3.44k
        osCommand.Printf(
213
3.44k
            "CREATE TABLE %s (f_table_name text, f_geometry_column text, "
214
3.44k
            "geometry_type integer, coord_dimension integer, "
215
3.44k
            "srid integer, geometry_format text)",
216
3.44k
            VFK_DB_GEOMETRY_TABLE);
217
3.44k
        ExecuteSQL(osCommand.c_str());
218
219
        /* spatial_ref_sys */
220
3.44k
        osCommand.Printf(
221
3.44k
            "CREATE TABLE %s (srid interer, auth_name text, auth_srid text, "
222
3.44k
            "srtext text)",
223
3.44k
            VFK_DB_SPATIAL_REF_TABLE);
224
3.44k
        ExecuteSQL(osCommand.c_str());
225
226
        /* insert S-JTSK into spatial_ref_sys table */
227
3.44k
        poSRS = new OGRSpatialReference();
228
3.44k
        poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
229
3.44k
        if (poSRS->importFromEPSG(5514) != OGRERR_FAILURE)
230
3.44k
        {
231
3.44k
            char *pszWKT = nullptr;
232
3.44k
            poSRS->exportToWkt(&pszWKT);
233
3.44k
            osCommand.Printf("INSERT INTO %s (srid, auth_name, auth_srid, "
234
3.44k
                             "srtext) VALUES (5514, 'EPSG', 5514, '%s')",
235
3.44k
                             VFK_DB_SPATIAL_REF_TABLE, pszWKT);
236
3.44k
            ExecuteSQL(osCommand.c_str());
237
3.44k
            CPLFree(pszWKT);
238
3.44k
        }
239
3.44k
        delete poSRS;
240
3.44k
    }
241
3.85k
}
242
243
/*!
244
  \brief VFKReaderSQLite destructor
245
*/
246
VFKReaderSQLite::~VFKReaderSQLite()
247
3.88k
{
248
    /* clean loaded properties */
249
16.4k
    for (int i = 0; i < m_nDataBlockCount; i++)
250
12.5k
        m_papoDataBlock[i]->CleanProperties();
251
252
    /* close backend SQLite DB */
253
3.88k
    if (SQLITE_OK != sqlite3_close(m_poDB))
254
0
    {
255
0
        CPLError(CE_Failure, CPLE_AppDefined, "Closing SQLite DB failed: %s",
256
0
                 sqlite3_errmsg(m_poDB));
257
0
    }
258
3.88k
    CPLDebug("OGR-VFK", "Internal DB (%s) closed", m_pszDBname);
259
260
    /* delete backend SQLite DB if requested */
261
3.88k
    if (CPLTestBool(CPLGetConfigOption("OGR_VFK_DB_DELETE", "NO")))
262
0
    {
263
0
        CPLDebug("OGR-VFK", "Internal DB (%s) deleted", m_pszDBname);
264
0
        VSIUnlink(m_pszDBname);
265
0
    }
266
3.88k
    delete[] m_pszDBname;
267
3.88k
}
268
269
/*!
270
  \brief Load data block definitions (&B)
271
272
  Call VFKReader::OpenFile() before this function.
273
274
  \return number of data blocks or -1 on error
275
*/
276
int VFKReaderSQLite::ReadDataBlocks(bool bSuppressGeometry)
277
3.85k
{
278
3.85k
    CPLString osSQL;
279
3.85k
    osSQL.Printf("SELECT table_name, table_defn FROM %s", VFK_DB_TABLE);
280
3.85k
    sqlite3_stmt *hStmt = PrepareStatement(osSQL.c_str());
281
5.80k
    while (ExecuteSQL(hStmt) == OGRERR_NONE)
282
1.94k
    {
283
1.94k
        const char *pszName = (const char *)sqlite3_column_text(hStmt, 0);
284
1.94k
        const char *pszDefn = (const char *)sqlite3_column_text(hStmt, 1);
285
1.94k
        if (pszName && pszDefn)
286
1.94k
        {
287
1.94k
            IVFKDataBlock *poNewDataBlock =
288
1.94k
                (IVFKDataBlock *)CreateDataBlock(pszName);
289
1.94k
            poNewDataBlock->SetGeometryType(bSuppressGeometry);
290
1.94k
            if (poNewDataBlock->GetGeometryType() != wkbNone)
291
320
            {
292
                /* may happen:
293
                 * first open with "-oo SUPPRESS_GEOMETRY=YES --config
294
                 * OGR_VFK_DB_READ_ALL_BLOCKS NO" than attempt to fetch feature
295
                 * from geometry-included layer: SOBR -fid 1
296
                 */
297
320
                cpl::down_cast<VFKDataBlockSQLite *>(poNewDataBlock)
298
320
                    ->AddGeometryColumn();
299
320
            }
300
1.94k
            poNewDataBlock->SetProperties(pszDefn);
301
1.94k
            VFKReader::AddDataBlock(poNewDataBlock, nullptr);
302
1.94k
        }
303
1.94k
    }
304
305
3.85k
    CPL_IGNORE_RET_VAL(
306
3.85k
        sqlite3_exec(m_poDB, "BEGIN", nullptr, nullptr, nullptr));
307
    /* Read data from VFK file */
308
3.85k
    const int nDataBlocks = VFKReader::ReadDataBlocks(bSuppressGeometry);
309
3.85k
    CPL_IGNORE_RET_VAL(
310
3.85k
        sqlite3_exec(m_poDB, "COMMIT", nullptr, nullptr, nullptr));
311
312
3.85k
    return nDataBlocks;
313
3.85k
}
314
315
/*!
316
  \brief Load data records (&D)
317
318
  Call VFKReader::OpenFile() before this function.
319
320
  \param poDataBlock limit to selected data block or NULL for all
321
322
  \return number of data records or -1 on error
323
*/
324
int64_t VFKReaderSQLite::ReadDataRecords(IVFKDataBlock *poDataBlock)
325
3.85k
{
326
3.85k
    CPLString osSQL;
327
3.85k
    IVFKDataBlock *poDataBlockCurrent = nullptr;
328
3.85k
    sqlite3_stmt *hStmt = nullptr;
329
3.85k
    const char *pszName = nullptr;
330
3.85k
    int64_t nDataRecords = 0;
331
3.85k
    bool bReadVfk = !m_bDbSource;
332
3.85k
    bool bReadDb = false;
333
334
3.85k
    if (poDataBlock)
335
0
    { /* read records only for selected data block */
336
        /* table name */
337
0
        pszName = poDataBlock->GetName();
338
339
        /* check for existing records (re-use already inserted data) */
340
0
        osSQL.Printf("SELECT num_records FROM %s WHERE "
341
0
                     "table_name = '%s'",
342
0
                     VFK_DB_TABLE, pszName);
343
0
        hStmt = PrepareStatement(osSQL.c_str());
344
0
        if (ExecuteSQL(hStmt) == OGRERR_NONE)
345
0
        {
346
0
            nDataRecords = sqlite3_column_int64(hStmt, 0);
347
0
            if (nDataRecords > 0)
348
0
                bReadDb = true; /* -> read from DB */
349
0
            else
350
0
                nDataRecords = 0;
351
0
        }
352
0
        sqlite3_finalize(hStmt);
353
0
    }
354
3.85k
    else
355
3.85k
    { /* read all data blocks */
356
        /* check for existing records (re-use already inserted data) */
357
3.85k
        osSQL.Printf("SELECT COUNT(*) FROM %s WHERE num_records > 0",
358
3.85k
                     VFK_DB_TABLE);
359
3.85k
        hStmt = PrepareStatement(osSQL.c_str());
360
3.85k
        if (ExecuteSQL(hStmt) == OGRERR_NONE &&
361
824
            sqlite3_column_int(hStmt, 0) != 0)
362
109
            bReadDb = true; /* -> read from DB */
363
3.85k
        sqlite3_finalize(hStmt);
364
365
        /* check if file is already registered in DB (requires file_size column)
366
         */
367
3.85k
        osSQL.Printf("SELECT COUNT(*) FROM %s WHERE file_name = '%s' AND "
368
3.85k
                     "file_size = " CPL_FRMT_GUIB " AND num_records > 0",
369
3.85k
                     VFK_DB_TABLE, CPLGetFilename(m_pszFilename),
370
3.85k
                     (GUIntBig)m_poFStat->st_size);
371
3.85k
        hStmt = PrepareStatement(osSQL.c_str());
372
3.85k
        if (ExecuteSQL(hStmt) == OGRERR_NONE &&
373
824
            sqlite3_column_int(hStmt, 0) > 0)
374
18
        {
375
            /* -> file already registered (filename & size is the same) */
376
18
            CPLDebug("OGR-VFK", "VFK file %s already loaded in DB",
377
18
                     m_pszFilename);
378
18
            bReadVfk = false;
379
18
        }
380
3.85k
        sqlite3_finalize(hStmt);
381
3.85k
    }
382
383
3.85k
    if (bReadDb)
384
109
    { /* read records from DB */
385
        /* read from  DB */
386
109
        VFKFeatureSQLite *poNewFeature = nullptr;
387
388
109
        poDataBlockCurrent = nullptr;
389
1.03k
        for (int iDataBlock = 0; iDataBlock < GetDataBlockCount(); iDataBlock++)
390
925
        {
391
925
            poDataBlockCurrent = GetDataBlock(iDataBlock);
392
393
925
            if (poDataBlock && poDataBlock != poDataBlockCurrent)
394
0
                continue;
395
396
925
            poDataBlockCurrent->SetFeatureCount(0); /* avoid recursive call */
397
398
925
            pszName = poDataBlockCurrent->GetName();
399
925
            CPLAssert(nullptr != pszName);
400
401
925
            osSQL.Printf("SELECT %s,_rowid_ FROM %s ", FID_COLUMN, pszName);
402
925
            if (EQUAL(pszName, "SBP") || EQUAL(pszName, "SBPG"))
403
64
                osSQL += "WHERE PORADOVE_CISLO_BODU = 1 ";
404
925
            osSQL += "ORDER BY ";
405
925
            osSQL += FID_COLUMN;
406
925
            hStmt = PrepareStatement(osSQL.c_str());
407
925
            nDataRecords = 0;
408
4.37k
            while (ExecuteSQL(hStmt) == OGRERR_NONE)
409
3.45k
            {
410
3.45k
                const long iFID = sqlite3_column_int(hStmt, 0);
411
3.45k
                int iRowId = sqlite3_column_int(hStmt, 1);
412
3.45k
                poNewFeature =
413
3.45k
                    new VFKFeatureSQLite(poDataBlockCurrent, iRowId, iFID);
414
3.45k
                poDataBlockCurrent->AddFeature(poNewFeature);
415
3.45k
                nDataRecords++;
416
3.45k
            }
417
418
            /* check DB consistency */
419
925
            osSQL.Printf("SELECT num_features FROM %s WHERE table_name = '%s'",
420
925
                         VFK_DB_TABLE, pszName);
421
925
            hStmt = PrepareStatement(osSQL.c_str());
422
925
            if (ExecuteSQL(hStmt) == OGRERR_NONE)
423
910
            {
424
910
                const int nFeatDB = sqlite3_column_int(hStmt, 0);
425
910
                if (nFeatDB > 0 &&
426
102
                    nFeatDB != poDataBlockCurrent->GetFeatureCount())
427
18
                    CPLError(CE_Failure, CPLE_AppDefined,
428
18
                             "%s: Invalid number of features " CPL_FRMT_GIB
429
18
                             " (should be %d)",
430
18
                             pszName, poDataBlockCurrent->GetFeatureCount(),
431
18
                             nFeatDB);
432
910
            }
433
925
            sqlite3_finalize(hStmt);
434
925
        }
435
109
    }
436
437
3.85k
    if (bReadVfk)
438
3.83k
    { /* read from VFK file and insert records into DB */
439
        /* begin transaction */
440
3.83k
        ExecuteSQL("BEGIN");
441
442
        /* Store VFK header to DB */
443
3.83k
        StoreInfo2DB();
444
445
        /* Insert VFK data records into DB */
446
3.83k
        const int64_t nExtraRecords = VFKReader::ReadDataRecords(poDataBlock);
447
3.83k
        if (nExtraRecords >= 0)
448
3.83k
        {
449
3.83k
            nDataRecords += nExtraRecords;
450
451
            /* update VFK_DB_TABLE table */
452
3.83k
            poDataBlockCurrent = nullptr;
453
16.3k
            for (int iDataBlock = 0; iDataBlock < GetDataBlockCount();
454
12.4k
                 iDataBlock++)
455
12.4k
            {
456
12.4k
                poDataBlockCurrent = GetDataBlock(iDataBlock);
457
458
12.4k
                if (poDataBlock && poDataBlock != poDataBlockCurrent)
459
0
                    continue;
460
461
                /* update number of records in metadata table */
462
12.4k
                osSQL.Printf("UPDATE %s SET num_records = %d WHERE "
463
12.4k
                             "table_name = '%s'",
464
12.4k
                             VFK_DB_TABLE, poDataBlockCurrent->GetRecordCount(),
465
12.4k
                             poDataBlockCurrent->GetName());
466
467
12.4k
                ExecuteSQL(osSQL);
468
12.4k
            }
469
3.83k
        }
470
471
        /* create indices if not exist */
472
3.83k
        CreateIndices();
473
474
        /* commit transaction */
475
3.83k
        ExecuteSQL("COMMIT");
476
3.83k
    }
477
478
3.85k
    return nDataRecords;
479
3.85k
}
480
481
/*!
482
  \brief Store header info to VFK_DB_HEADER
483
*/
484
void VFKReaderSQLite::StoreInfo2DB()
485
3.83k
{
486
3.83k
    for (std::map<CPLString, CPLString>::iterator i = poInfo.begin();
487
297k
         i != poInfo.end(); ++i)
488
293k
    {
489
293k
        const char *value = i->second.c_str();
490
491
293k
        const char q = (value[0] == '"') ? ' ' : '"';
492
493
293k
        CPLString osSQL;
494
293k
        osSQL.Printf("INSERT INTO %s VALUES(\"%s\", %c%s%c)",
495
293k
                     VFK_DB_HEADER_TABLE, i->first.c_str(), q, value, q);
496
293k
        ExecuteSQL(osSQL);
497
293k
    }
498
3.83k
}
499
500
/*!
501
  \brief Create indices for newly added db tables (datablocks)
502
*/
503
void VFKReaderSQLite::CreateIndices()
504
3.83k
{
505
3.83k
    const char *pszBlockName;
506
3.83k
    CPLString osIndexName, osSQL;
507
3.83k
    VFKDataBlockSQLite *poDataBlock;
508
509
3.83k
    sqlite3_stmt *hStmt;
510
511
16.3k
    for (int iLayer = 0; iLayer < GetDataBlockCount(); iLayer++)
512
12.4k
    {
513
12.4k
        poDataBlock = (VFKDataBlockSQLite *)GetDataBlock(iLayer);
514
12.4k
        pszBlockName = poDataBlock->GetName();
515
516
        /* ogr_fid */
517
12.4k
        osIndexName.Printf("%s_%s", pszBlockName, FID_COLUMN);
518
519
        /* check if index on ogr_fid column exists */
520
12.4k
        osSQL.Printf("SELECT COUNT(*) FROM sqlite_master WHERE type = 'index' "
521
12.4k
                     "AND name = '%s'",
522
12.4k
                     osIndexName.c_str());
523
12.4k
        hStmt = PrepareStatement(osSQL.c_str());
524
525
12.4k
        if (ExecuteSQL(hStmt) == OGRERR_NONE &&
526
3.57k
            sqlite3_column_int(hStmt, 0) > 0)
527
659
        {
528
            /* index on ogr_fid column exists, skip creating indices
529
               for current datablock */
530
659
            sqlite3_finalize(hStmt);
531
659
            continue;
532
659
        }
533
11.8k
        sqlite3_finalize(hStmt);
534
535
        /* create index on ogr_fid */
536
11.8k
        CreateIndex(
537
11.8k
            osIndexName.c_str(), pszBlockName, FID_COLUMN,
538
11.8k
            !(EQUAL(pszBlockName, "SBP") || EQUAL(pszBlockName, "SBPG")));
539
540
11.8k
        if (poDataBlock->GetGeometryType() == wkbNone)
541
10.8k
        {
542
            /* skip geometry-related indices */
543
10.8k
            continue;
544
10.8k
        }
545
546
925
        if (EQUAL(pszBlockName, "SOBR") || EQUAL(pszBlockName, "OBBP") ||
547
746
            EQUAL(pszBlockName, "SPOL") || EQUAL(pszBlockName, "OB") ||
548
684
            EQUAL(pszBlockName, "OP") || EQUAL(pszBlockName, "OBPEJ") ||
549
631
            EQUAL(pszBlockName, "SBP") || EQUAL(pszBlockName, "SBPG") ||
550
438
            EQUAL(pszBlockName, "HP") || EQUAL(pszBlockName, "DPM") ||
551
121
            EQUAL(pszBlockName, "ZVB") || EQUAL(pszBlockName, "PAR") ||
552
57
            EQUAL(pszBlockName, "BUD"))
553
925
        {
554
925
            const char *pszKey =
555
925
                cpl::down_cast<VFKDataBlockSQLite *>(poDataBlock)->GetKey();
556
925
            if (pszKey)
557
669
            {
558
                /* ID */
559
669
                osIndexName.Printf("%s_%s", pszBlockName, pszKey);
560
669
                CreateIndex(osIndexName.c_str(), pszBlockName, pszKey,
561
669
                            !m_bAmendment);
562
669
            }
563
925
        }
564
565
        /* create other indices used for building geometry */
566
925
        if (EQUAL(pszBlockName, "SBP"))
567
187
        {
568
            /* SBP */
569
187
            CreateIndex("SBP_OB", pszBlockName, "OB_ID", false);
570
187
            CreateIndex("SBP_HP", pszBlockName, "HP_ID", false);
571
187
            CreateIndex("SBP_DPM", pszBlockName, "DPM_ID", false);
572
187
            CreateIndex("SBP_OB_HP_DPM", pszBlockName, "OB_ID,HP_ID,DPM_ID",
573
187
                        true);
574
187
            CreateIndex("SBP_OB_POR", pszBlockName, "OB_ID,PORADOVE_CISLO_BODU",
575
187
                        false);
576
187
            CreateIndex("SBP_HP_POR", pszBlockName, "HP_ID,PORADOVE_CISLO_BODU",
577
187
                        false);
578
187
            CreateIndex("SBP_DPM_POR", pszBlockName,
579
187
                        "DPM_ID,PORADOVE_CISLO_BODU", false);
580
187
        }
581
738
        else if (EQUAL(pszBlockName, "HP"))
582
267
        {
583
            /* HP */
584
267
            CreateIndex("HP_PAR1", pszBlockName, "PAR_ID_1", false);
585
267
            CreateIndex("HP_PAR2", pszBlockName, "PAR_ID_2", false);
586
267
        }
587
471
        else if (EQUAL(pszBlockName, "OB"))
588
46
        {
589
            /* OP */
590
46
            CreateIndex("OB_BUD", pszBlockName, "BUD_ID", false);
591
46
        }
592
925
    }
593
3.83k
}
594
595
/*!
596
  \brief Create index
597
598
  If creating unique index fails, then non-unique index is created instead.
599
600
  \param name index name
601
  \param table table name
602
  \param column column(s) name
603
  \param unique true to create unique index
604
*/
605
void VFKReaderSQLite::CreateIndex(const char *name, const char *table,
606
                                  const char *column, bool unique)
607
14.3k
{
608
14.3k
    CPLString osSQL;
609
610
14.3k
    if (unique)
611
12.4k
    {
612
12.4k
        osSQL.Printf("CREATE UNIQUE INDEX %s ON %s (%s)", name, table, column);
613
12.4k
        if (ExecuteSQL(osSQL.c_str()) == OGRERR_NONE)
614
1.03k
        {
615
1.03k
            return;
616
1.03k
        }
617
12.4k
    }
618
619
13.3k
    osSQL.Printf("CREATE INDEX %s ON %s (%s)", name, table, column);
620
13.3k
    ExecuteSQL(osSQL.c_str());
621
13.3k
}
622
623
/*!
624
  \brief Create new data block
625
626
  \param pszBlockName name of the block to be created
627
628
  \return pointer to VFKDataBlockSQLite instance
629
*/
630
IVFKDataBlock *VFKReaderSQLite::CreateDataBlock(const char *pszBlockName)
631
12.5k
{
632
    /* create new data block, i.e. table in DB */
633
12.5k
    return new VFKDataBlockSQLite(pszBlockName, (IVFKReader *)this);
634
12.5k
}
635
636
/*!
637
  \brief Create DB table from VFKDataBlock (SQLITE only)
638
639
  \param poDataBlock pointer to VFKDataBlock instance
640
*/
641
void VFKReaderSQLite::AddDataBlock(IVFKDataBlock *poDataBlock,
642
                                   const char *pszDefn)
643
10.6k
{
644
10.6k
    CPLString osColumn;
645
646
10.6k
    const char *pszBlockName = poDataBlock->GetName();
647
648
    /* register table in VFK_DB_TABLE */
649
10.6k
    CPLString osCommand;
650
10.6k
    osCommand.Printf("SELECT COUNT(*) FROM %s WHERE "
651
10.6k
                     "table_name = '%s'",
652
10.6k
                     VFK_DB_TABLE, pszBlockName);
653
10.6k
    sqlite3_stmt *hStmt = PrepareStatement(osCommand.c_str());
654
655
10.6k
    if (ExecuteSQL(hStmt) == OGRERR_NONE)
656
1.69k
    {
657
1.69k
        if (sqlite3_column_int(hStmt, 0) == 0)
658
1.67k
        {
659
660
1.67k
            osCommand.Printf("CREATE TABLE IF NOT EXISTS '%s' (", pszBlockName);
661
11.0k
            for (int i = 0; i < poDataBlock->GetPropertyCount(); i++)
662
9.42k
            {
663
9.42k
                VFKPropertyDefn *poPropertyDefn = poDataBlock->GetProperty(i);
664
9.42k
                if (i > 0)
665
8.07k
                    osCommand += ",";
666
9.42k
                osColumn.Printf("%s %s", poPropertyDefn->GetName(),
667
9.42k
                                poPropertyDefn->GetTypeSQL().c_str());
668
9.42k
                osCommand += osColumn;
669
9.42k
            }
670
1.67k
            osColumn.Printf(",%s integer", FID_COLUMN);
671
1.67k
            osCommand += osColumn;
672
1.67k
            if (poDataBlock->GetGeometryType() != wkbNone)
673
371
            {
674
371
                osColumn.Printf(",%s blob", GEOM_COLUMN);
675
371
                osCommand += osColumn;
676
371
            }
677
1.67k
            osCommand += ")";
678
1.67k
            ExecuteSQL(osCommand.c_str()); /* CREATE TABLE */
679
680
            /* update VFK_DB_TABLE meta-table */
681
1.67k
            osCommand.Printf(
682
1.67k
                "INSERT INTO %s (file_name, file_size, table_name, "
683
1.67k
                "num_records, num_features, num_geometries, table_defn) VALUES "
684
1.67k
                "('%s', " CPL_FRMT_GUIB ", '%s', -1, 0, 0, '%s')",
685
1.67k
                VFK_DB_TABLE, CPLGetFilename(m_pszFilename),
686
1.67k
                (GUIntBig)m_poFStat->st_size, pszBlockName, pszDefn);
687
1.67k
            ExecuteSQL(osCommand.c_str());
688
689
1.67k
            int geom_type = cpl::down_cast<VFKDataBlockSQLite *>(poDataBlock)
690
1.67k
                                ->GetGeometrySQLType();
691
            /* update VFK_DB_GEOMETRY_TABLE */
692
1.67k
            osCommand.Printf("INSERT INTO %s (f_table_name, f_geometry_column, "
693
1.67k
                             "geometry_type, "
694
1.67k
                             "coord_dimension, srid, geometry_format) VALUES "
695
1.67k
                             "('%s', '%s', %d, 2, 5514, 'WKB')",
696
1.67k
                             VFK_DB_GEOMETRY_TABLE, pszBlockName, GEOM_COLUMN,
697
1.67k
                             geom_type);
698
1.67k
            ExecuteSQL(osCommand.c_str());
699
1.67k
        }
700
1.69k
        sqlite3_finalize(hStmt);
701
1.69k
    }
702
703
10.6k
    return VFKReader::AddDataBlock(poDataBlock, nullptr);
704
10.6k
}
705
706
/*!
707
  \brief Prepare SQL statement
708
709
  \param pszSQLCommand SQL statement to be prepared
710
711
  \return pointer to sqlite3_stmt instance or NULL on error
712
*/
713
sqlite3_stmt *VFKReaderSQLite::PrepareStatement(const char *pszSQLCommand)
714
63.9k
{
715
63.9k
    CPLDebug("OGR-VFK", "VFKReaderSQLite::PrepareStatement(): %s",
716
63.9k
             pszSQLCommand);
717
718
63.9k
    sqlite3_stmt *hStmt = nullptr;
719
63.9k
    const int rc =
720
63.9k
        sqlite3_prepare_v2(m_poDB, pszSQLCommand, -1, &hStmt, nullptr);
721
722
    // TODO(schwehr): if( rc == SQLITE_OK ) return NULL;
723
63.9k
    if (rc != SQLITE_OK)
724
33.8k
    {
725
33.8k
        CPLError(CE_Failure, CPLE_AppDefined,
726
33.8k
                 "In PrepareStatement(): sqlite3_prepare_v2(%s):\n  %s",
727
33.8k
                 pszSQLCommand, sqlite3_errmsg(m_poDB));
728
729
33.8k
        if (hStmt != nullptr)
730
0
        {
731
0
            sqlite3_finalize(hStmt);
732
0
        }
733
734
33.8k
        return nullptr;
735
33.8k
    }
736
737
30.0k
    return hStmt;
738
63.9k
}
739
740
/*!
741
  \brief Execute prepared SQL statement
742
743
  \param hStmt pointer to sqlite3_stmt
744
745
  \return OGRERR_NONE on success
746
*/
747
OGRErr VFKReaderSQLite::ExecuteSQL(sqlite3_stmt *&hStmt)
748
97.7k
{
749
97.7k
    const int rc = sqlite3_step(hStmt);
750
97.7k
    if (rc != SQLITE_ROW)
751
42.2k
    {
752
42.2k
        if (rc == SQLITE_DONE)
753
13.7k
        {
754
13.7k
            sqlite3_finalize(hStmt);
755
13.7k
            hStmt = nullptr;
756
13.7k
            return OGRERR_NOT_ENOUGH_DATA;
757
13.7k
        }
758
759
28.5k
        CPLError(CE_Failure, CPLE_AppDefined,
760
28.5k
                 "In ExecuteSQL(): sqlite3_step:\n  %s",
761
28.5k
                 sqlite3_errmsg(m_poDB));
762
28.5k
        if (hStmt)
763
0
        {
764
0
            sqlite3_finalize(hStmt);
765
0
            hStmt = nullptr;
766
0
        }
767
28.5k
        return OGRERR_FAILURE;
768
42.2k
    }
769
770
55.4k
    return OGRERR_NONE;
771
97.7k
}
772
773
/*!
774
  \brief Execute SQL statement (SQLITE only)
775
776
  \param pszSQLCommand SQL command to execute
777
  \param eErrLevel if equal to CE_None, no error message will be emitted on
778
  failure.
779
780
  \return OGRERR_NONE on success or OGRERR_FAILURE on failure
781
*/
782
OGRErr VFKReaderSQLite::ExecuteSQL(const char *pszSQLCommand, CPLErr eErrLevel)
783
483k
{
784
483k
    char *pszErrMsg = nullptr;
785
786
483k
    if (SQLITE_OK !=
787
483k
        sqlite3_exec(m_poDB, pszSQLCommand, nullptr, nullptr, &pszErrMsg))
788
408k
    {
789
408k
        if (eErrLevel != CE_None)
790
408k
        {
791
408k
            CPLError(eErrLevel, CPLE_AppDefined, "In ExecuteSQL(%s): %s",
792
408k
                     pszSQLCommand, pszErrMsg ? pszErrMsg : "(null)");
793
408k
        }
794
408k
        sqlite3_free(pszErrMsg);
795
796
408k
        return OGRERR_FAILURE;
797
408k
    }
798
799
74.9k
    return OGRERR_NONE;
800
483k
}
801
802
/*!
803
  \brief Add feature
804
805
  \param poDataBlock pointer to VFKDataBlock instance
806
  \param poFeature pointer to VFKFeature instance
807
*/
808
OGRErr VFKReaderSQLite::AddFeature(IVFKDataBlock *poDataBlock,
809
                                   VFKFeature *poFeature)
810
108k
{
811
108k
    CPLString osValue;
812
813
108k
    const char *pszBlockName = poDataBlock->GetName();
814
108k
    CPLString osCommand;
815
108k
    osCommand.Printf("INSERT INTO '%s' VALUES(", pszBlockName);
816
817
516k
    for (int i = 0; i < poDataBlock->GetPropertyCount(); i++)
818
408k
    {
819
408k
        const OGRFieldType ftype = poDataBlock->GetProperty(i)->GetType();
820
408k
        const VFKProperty *poProperty = poFeature->GetProperty(i);
821
408k
        if (i > 0)
822
300k
            osCommand += ",";
823
824
408k
        if (!poProperty || poProperty->IsNull())
825
102k
        {
826
102k
            osValue.Printf("NULL");
827
102k
        }
828
305k
        else
829
305k
        {
830
305k
            switch (ftype)
831
305k
            {
832
73.4k
                case OFTInteger:
833
73.4k
                    osValue.Printf("%d", poProperty->GetValueI());
834
73.4k
                    break;
835
103k
                case OFTInteger64:
836
103k
                    osValue.Printf(CPL_FRMT_GIB, poProperty->GetValueI64());
837
103k
                    break;
838
20.2k
                case OFTReal:
839
20.2k
                    osValue.Printf("%f", poProperty->GetValueD());
840
20.2k
                    break;
841
109k
                case OFTString:
842
109k
                    osValue.Printf("'%s'", poProperty->GetValueS(true));
843
109k
                    break;
844
0
                default:
845
0
                    osValue.Printf("'%s'", poProperty->GetValueS(true));
846
0
                    break;
847
305k
            }
848
305k
        }
849
408k
        osCommand += osValue;
850
408k
    }
851
108k
    osValue.Printf("," CPL_FRMT_GIB, poFeature->GetFID());
852
108k
    if (poDataBlock->GetGeometryType() != wkbNone)
853
28.4k
    {
854
28.4k
        osValue += ",NULL";
855
28.4k
    }
856
108k
    osCommand += osValue;
857
108k
    osCommand += ")";
858
859
108k
    if (ExecuteSQL(osCommand.c_str(), CE_Warning) != OGRERR_NONE)
860
85.4k
        return OGRERR_FAILURE;
861
862
22.7k
    if (EQUAL(pszBlockName, "SBP") || EQUAL(pszBlockName, "SBPG"))
863
10.7k
    {
864
10.7k
        const VFKProperty *poProperty =
865
10.7k
            poFeature->GetProperty("PORADOVE_CISLO_BODU");
866
10.7k
        if (poProperty == nullptr)
867
58
        {
868
58
            CPLError(CE_Failure, CPLE_AppDefined,
869
58
                     "Cannot find property PORADOVE_CISLO_BODU");
870
58
            return OGRERR_FAILURE;
871
58
        }
872
10.6k
        if (poProperty->GetValueI64() != 1)
873
5.22k
            return OGRERR_NONE;
874
10.6k
    }
875
876
17.4k
    VFKFeatureSQLite *poNewFeature = new VFKFeatureSQLite(
877
17.4k
        poDataBlock, poDataBlock->GetRecordCount(RecordValid) + 1,
878
17.4k
        poFeature->GetFID());
879
17.4k
    poDataBlock->AddFeature(poNewFeature);
880
881
17.4k
    return OGRERR_NONE;
882
22.7k
}