LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/vfk - vfkreadersqlite.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 320 378 84.7 %
Date: 2025-05-31 00:00:17 Functions: 14 14 100.0 %

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

Generated by: LCOV version 1.14