LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/vfk - vfkreadersqlite.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 318 376 84.6 %
Date: 2024-11-21 22:18:42 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 = CPLResetExtension(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          12 :                 ((VFKDataBlockSQLite *)poNewDataBlock)->AddGeometryColumn();
     298             :             }
     299          61 :             poNewDataBlock->SetProperties(pszDefn);
     300          61 :             VFKReader::AddDataBlock(poNewDataBlock, nullptr);
     301             :         }
     302             :     }
     303             : 
     304          16 :     CPL_IGNORE_RET_VAL(
     305          16 :         sqlite3_exec(m_poDB, "BEGIN", nullptr, nullptr, nullptr));
     306             :     /* Read data from VFK file */
     307          16 :     const int nDataBlocks = VFKReader::ReadDataBlocks(bSuppressGeometry);
     308          16 :     CPL_IGNORE_RET_VAL(
     309          16 :         sqlite3_exec(m_poDB, "COMMIT", nullptr, nullptr, nullptr));
     310             : 
     311          32 :     return nDataBlocks;
     312             : }
     313             : 
     314             : /*!
     315             :   \brief Load data records (&D)
     316             : 
     317             :   Call VFKReader::OpenFile() before this function.
     318             : 
     319             :   \param poDataBlock limit to selected data block or NULL for all
     320             : 
     321             :   \return number of data records or -1 on error
     322             : */
     323          16 : int64_t VFKReaderSQLite::ReadDataRecords(IVFKDataBlock *poDataBlock)
     324             : {
     325          16 :     CPLString osSQL;
     326          16 :     IVFKDataBlock *poDataBlockCurrent = nullptr;
     327          16 :     sqlite3_stmt *hStmt = nullptr;
     328          16 :     const char *pszName = nullptr;
     329          16 :     int64_t nDataRecords = 0;
     330          16 :     bool bReadVfk = !m_bDbSource;
     331          16 :     bool bReadDb = false;
     332             : 
     333          16 :     if (poDataBlock)
     334             :     { /* read records only for selected data block */
     335             :         /* table name */
     336           0 :         pszName = poDataBlock->GetName();
     337             : 
     338             :         /* check for existing records (re-use already inserted data) */
     339             :         osSQL.Printf("SELECT num_records FROM %s WHERE "
     340             :                      "table_name = '%s'",
     341           0 :                      VFK_DB_TABLE, pszName);
     342           0 :         hStmt = PrepareStatement(osSQL.c_str());
     343           0 :         if (ExecuteSQL(hStmt) == OGRERR_NONE)
     344             :         {
     345           0 :             nDataRecords = sqlite3_column_int64(hStmt, 0);
     346           0 :             if (nDataRecords > 0)
     347           0 :                 bReadDb = true; /* -> read from DB */
     348             :             else
     349           0 :                 nDataRecords = 0;
     350             :         }
     351           0 :         sqlite3_finalize(hStmt);
     352             :     }
     353             :     else
     354             :     { /* read all data blocks */
     355             :         /* check for existing records (re-use already inserted data) */
     356             :         osSQL.Printf("SELECT COUNT(*) FROM %s WHERE num_records > 0",
     357          16 :                      VFK_DB_TABLE);
     358          16 :         hStmt = PrepareStatement(osSQL.c_str());
     359          32 :         if (ExecuteSQL(hStmt) == OGRERR_NONE &&
     360          16 :             sqlite3_column_int(hStmt, 0) != 0)
     361           1 :             bReadDb = true; /* -> read from DB */
     362          16 :         sqlite3_finalize(hStmt);
     363             : 
     364             :         /* check if file is already registered in DB (requires file_size column)
     365             :          */
     366             :         osSQL.Printf("SELECT COUNT(*) FROM %s WHERE file_name = '%s' AND "
     367             :                      "file_size = " CPL_FRMT_GUIB " AND num_records > 0",
     368          16 :                      VFK_DB_TABLE, CPLGetFilename(m_pszFilename),
     369          16 :                      (GUIntBig)m_poFStat->st_size);
     370          16 :         hStmt = PrepareStatement(osSQL.c_str());
     371          32 :         if (ExecuteSQL(hStmt) == OGRERR_NONE &&
     372          16 :             sqlite3_column_int(hStmt, 0) > 0)
     373             :         {
     374             :             /* -> file already registered (filename & size is the same) */
     375           0 :             CPLDebug("OGR-VFK", "VFK file %s already loaded in DB",
     376             :                      m_pszFilename);
     377           0 :             bReadVfk = false;
     378             :         }
     379          16 :         sqlite3_finalize(hStmt);
     380             :     }
     381             : 
     382          16 :     if (bReadDb)
     383             :     { /* read records from DB */
     384             :         /* read from  DB */
     385           1 :         VFKFeatureSQLite *poNewFeature = nullptr;
     386             : 
     387           1 :         poDataBlockCurrent = nullptr;
     388          62 :         for (int iDataBlock = 0; iDataBlock < GetDataBlockCount(); iDataBlock++)
     389             :         {
     390          61 :             poDataBlockCurrent = GetDataBlock(iDataBlock);
     391             : 
     392          61 :             if (poDataBlock && poDataBlock != poDataBlockCurrent)
     393           0 :                 continue;
     394             : 
     395          61 :             poDataBlockCurrent->SetFeatureCount(0); /* avoid recursive call */
     396             : 
     397          61 :             pszName = poDataBlockCurrent->GetName();
     398          61 :             CPLAssert(nullptr != pszName);
     399             : 
     400          61 :             osSQL.Printf("SELECT %s,_rowid_ FROM %s ", FID_COLUMN, pszName);
     401          61 :             if (EQUAL(pszName, "SBP") || EQUAL(pszName, "SBPG"))
     402           1 :                 osSQL += "WHERE PORADOVE_CISLO_BODU = 1 ";
     403          61 :             osSQL += "ORDER BY ";
     404          61 :             osSQL += FID_COLUMN;
     405          61 :             hStmt = PrepareStatement(osSQL.c_str());
     406          61 :             nDataRecords = 0;
     407         104 :             while (ExecuteSQL(hStmt) == OGRERR_NONE)
     408             :             {
     409          43 :                 const long iFID = sqlite3_column_int(hStmt, 0);
     410          43 :                 int iRowId = sqlite3_column_int(hStmt, 1);
     411          43 :                 poNewFeature =
     412          43 :                     new VFKFeatureSQLite(poDataBlockCurrent, iRowId, iFID);
     413          43 :                 poDataBlockCurrent->AddFeature(poNewFeature);
     414          43 :                 nDataRecords++;
     415             :             }
     416             : 
     417             :             /* check DB consistency */
     418             :             osSQL.Printf("SELECT num_features FROM %s WHERE table_name = '%s'",
     419          61 :                          VFK_DB_TABLE, pszName);
     420          61 :             hStmt = PrepareStatement(osSQL.c_str());
     421          61 :             if (ExecuteSQL(hStmt) == OGRERR_NONE)
     422             :             {
     423          61 :                 const int nFeatDB = sqlite3_column_int(hStmt, 0);
     424          65 :                 if (nFeatDB > 0 &&
     425           4 :                     nFeatDB != poDataBlockCurrent->GetFeatureCount())
     426           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     427             :                              "%s: Invalid number of features " CPL_FRMT_GIB
     428             :                              " (should be %d)",
     429             :                              pszName, poDataBlockCurrent->GetFeatureCount(),
     430             :                              nFeatDB);
     431             :             }
     432          61 :             sqlite3_finalize(hStmt);
     433             :         }
     434             :     }
     435             : 
     436          16 :     if (bReadVfk)
     437             :     { /* read from VFK file and insert records into DB */
     438             :         /* begin transaction */
     439          15 :         ExecuteSQL("BEGIN");
     440             : 
     441             :         /* Store VFK header to DB */
     442          15 :         StoreInfo2DB();
     443             : 
     444             :         /* Insert VFK data records into DB */
     445          15 :         nDataRecords += VFKReader::ReadDataRecords(poDataBlock);
     446             : 
     447             :         /* update VFK_DB_TABLE table */
     448          15 :         poDataBlockCurrent = nullptr;
     449         930 :         for (int iDataBlock = 0; iDataBlock < GetDataBlockCount(); iDataBlock++)
     450             :         {
     451         915 :             poDataBlockCurrent = GetDataBlock(iDataBlock);
     452             : 
     453         915 :             if (poDataBlock && poDataBlock != poDataBlockCurrent)
     454           0 :                 continue;
     455             : 
     456             :             /* update number of records in metadata table */
     457             :             osSQL.Printf("UPDATE %s SET num_records = %d WHERE "
     458             :                          "table_name = '%s'",
     459             :                          VFK_DB_TABLE, poDataBlockCurrent->GetRecordCount(),
     460         915 :                          poDataBlockCurrent->GetName());
     461             : 
     462         915 :             ExecuteSQL(osSQL);
     463             :         }
     464             : 
     465             :         /* create indices if not exist */
     466          15 :         CreateIndices();
     467             : 
     468             :         /* commit transaction */
     469          15 :         ExecuteSQL("COMMIT");
     470             :     }
     471             : 
     472          32 :     return nDataRecords;
     473             : }
     474             : 
     475             : /*!
     476             :   \brief Store header info to VFK_DB_HEADER
     477             : */
     478          15 : void VFKReaderSQLite::StoreInfo2DB()
     479             : {
     480         210 :     for (std::map<CPLString, CPLString>::iterator i = poInfo.begin();
     481         405 :          i != poInfo.end(); ++i)
     482             :     {
     483         195 :         const char *value = i->second.c_str();
     484             : 
     485         195 :         const char q = (value[0] == '"') ? ' ' : '"';
     486             : 
     487         390 :         CPLString osSQL;
     488             :         osSQL.Printf("INSERT INTO %s VALUES(\"%s\", %c%s%c)",
     489         195 :                      VFK_DB_HEADER_TABLE, i->first.c_str(), q, value, q);
     490         195 :         ExecuteSQL(osSQL);
     491             :     }
     492          15 : }
     493             : 
     494             : /*!
     495             :   \brief Create indices for newly added db tables (datablocks)
     496             : */
     497          15 : void VFKReaderSQLite::CreateIndices()
     498             : {
     499             :     const char *pszBlockName;
     500          30 :     CPLString osIndexName, osSQL;
     501             :     VFKDataBlockSQLite *poDataBlock;
     502             : 
     503             :     sqlite3_stmt *hStmt;
     504             : 
     505         930 :     for (int iLayer = 0; iLayer < GetDataBlockCount(); iLayer++)
     506             :     {
     507         915 :         poDataBlock = (VFKDataBlockSQLite *)GetDataBlock(iLayer);
     508         915 :         pszBlockName = poDataBlock->GetName();
     509             : 
     510             :         /* ogr_fid */
     511         915 :         osIndexName.Printf("%s_%s", pszBlockName, FID_COLUMN);
     512             : 
     513             :         /* check if index on ogr_fid column exists */
     514             :         osSQL.Printf("SELECT COUNT(*) FROM sqlite_master WHERE type = 'index' "
     515             :                      "AND name = '%s'",
     516         915 :                      osIndexName.c_str());
     517         915 :         hStmt = PrepareStatement(osSQL.c_str());
     518             : 
     519        1830 :         if (ExecuteSQL(hStmt) == OGRERR_NONE &&
     520         915 :             sqlite3_column_int(hStmt, 0) > 0)
     521             :         {
     522             :             /* index on ogr_fid column exists, skip creating indices
     523             :                for current datablock */
     524           0 :             sqlite3_finalize(hStmt);
     525           0 :             continue;
     526             :         }
     527         915 :         sqlite3_finalize(hStmt);
     528             : 
     529             :         /* create index on ogr_fid */
     530         915 :         CreateIndex(
     531             :             osIndexName.c_str(), pszBlockName, FID_COLUMN,
     532         915 :             !(EQUAL(pszBlockName, "SBP") || EQUAL(pszBlockName, "SBPG")));
     533             : 
     534         915 :         if (poDataBlock->GetGeometryType() == wkbNone)
     535             :         {
     536             :             /* skip geometry-related indices */
     537         747 :             continue;
     538             :         }
     539             : 
     540         168 :         if (EQUAL(pszBlockName, "SOBR") || EQUAL(pszBlockName, "OBBP") ||
     541         140 :             EQUAL(pszBlockName, "SPOL") || EQUAL(pszBlockName, "OB") ||
     542         112 :             EQUAL(pszBlockName, "OP") || EQUAL(pszBlockName, "OBPEJ") ||
     543          84 :             EQUAL(pszBlockName, "SBP") || EQUAL(pszBlockName, "SBPG") ||
     544          70 :             EQUAL(pszBlockName, "HP") || EQUAL(pszBlockName, "DPM") ||
     545          42 :             EQUAL(pszBlockName, "ZVB") || EQUAL(pszBlockName, "PAR") ||
     546          14 :             EQUAL(pszBlockName, "BUD"))
     547             :         {
     548         168 :             const char *pszKey = ((VFKDataBlockSQLite *)poDataBlock)->GetKey();
     549         168 :             if (pszKey)
     550             :             {
     551             :                 /* ID */
     552         168 :                 osIndexName.Printf("%s_%s", pszBlockName, pszKey);
     553         168 :                 CreateIndex(osIndexName.c_str(), pszBlockName, pszKey,
     554         168 :                             !m_bAmendment);
     555             :             }
     556             :         }
     557             : 
     558             :         /* create other indices used for building geometry */
     559         168 :         if (EQUAL(pszBlockName, "SBP"))
     560             :         {
     561             :             /* SBP */
     562          14 :             CreateIndex("SBP_OB", pszBlockName, "OB_ID", false);
     563          14 :             CreateIndex("SBP_HP", pszBlockName, "HP_ID", false);
     564          14 :             CreateIndex("SBP_DPM", pszBlockName, "DPM_ID", false);
     565          14 :             CreateIndex("SBP_OB_HP_DPM", pszBlockName, "OB_ID,HP_ID,DPM_ID",
     566             :                         true);
     567          14 :             CreateIndex("SBP_OB_POR", pszBlockName, "OB_ID,PORADOVE_CISLO_BODU",
     568             :                         false);
     569          14 :             CreateIndex("SBP_HP_POR", pszBlockName, "HP_ID,PORADOVE_CISLO_BODU",
     570             :                         false);
     571          14 :             CreateIndex("SBP_DPM_POR", pszBlockName,
     572             :                         "DPM_ID,PORADOVE_CISLO_BODU", false);
     573             :         }
     574         154 :         else if (EQUAL(pszBlockName, "HP"))
     575             :         {
     576             :             /* HP */
     577          14 :             CreateIndex("HP_PAR1", pszBlockName, "PAR_ID_1", false);
     578          14 :             CreateIndex("HP_PAR2", pszBlockName, "PAR_ID_2", false);
     579             :         }
     580         140 :         else if (EQUAL(pszBlockName, "OB"))
     581             :         {
     582             :             /* OP */
     583          14 :             CreateIndex("OB_BUD", pszBlockName, "BUD_ID", false);
     584             :         }
     585             :     }
     586          15 : }
     587             : 
     588             : /*!
     589             :   \brief Create index
     590             : 
     591             :   If creating unique index fails, then non-unique index is created instead.
     592             : 
     593             :   \param name index name
     594             :   \param table table name
     595             :   \param column column(s) name
     596             :   \param unique true to create unique index
     597             : */
     598        1223 : void VFKReaderSQLite::CreateIndex(const char *name, const char *table,
     599             :                                   const char *column, bool unique)
     600             : {
     601        1223 :     CPLString osSQL;
     602             : 
     603        1223 :     if (unique)
     604             :     {
     605        1082 :         osSQL.Printf("CREATE UNIQUE INDEX %s ON %s (%s)", name, table, column);
     606        1082 :         if (ExecuteSQL(osSQL.c_str()) == OGRERR_NONE)
     607             :         {
     608        1082 :             return;
     609             :         }
     610             :     }
     611             : 
     612         141 :     osSQL.Printf("CREATE INDEX %s ON %s (%s)", name, table, column);
     613         141 :     ExecuteSQL(osSQL.c_str());
     614             : }
     615             : 
     616             : /*!
     617             :   \brief Create new data block
     618             : 
     619             :   \param pszBlockName name of the block to be created
     620             : 
     621             :   \return pointer to VFKDataBlockSQLite instance
     622             : */
     623         976 : IVFKDataBlock *VFKReaderSQLite::CreateDataBlock(const char *pszBlockName)
     624             : {
     625             :     /* create new data block, i.e. table in DB */
     626         976 :     return new VFKDataBlockSQLite(pszBlockName, (IVFKReader *)this);
     627             : }
     628             : 
     629             : /*!
     630             :   \brief Create DB table from VFKDataBlock (SQLITE only)
     631             : 
     632             :   \param poDataBlock pointer to VFKDataBlock instance
     633             : */
     634         915 : void VFKReaderSQLite::AddDataBlock(IVFKDataBlock *poDataBlock,
     635             :                                    const char *pszDefn)
     636             : {
     637        1830 :     CPLString osColumn;
     638             : 
     639         915 :     const char *pszBlockName = poDataBlock->GetName();
     640             : 
     641             :     /* register table in VFK_DB_TABLE */
     642        1830 :     CPLString osCommand;
     643             :     osCommand.Printf("SELECT COUNT(*) FROM %s WHERE "
     644             :                      "table_name = '%s'",
     645         915 :                      VFK_DB_TABLE, pszBlockName);
     646         915 :     sqlite3_stmt *hStmt = PrepareStatement(osCommand.c_str());
     647             : 
     648         915 :     if (ExecuteSQL(hStmt) == OGRERR_NONE)
     649             :     {
     650         915 :         if (sqlite3_column_int(hStmt, 0) == 0)
     651             :         {
     652             : 
     653         915 :             osCommand.Printf("CREATE TABLE IF NOT EXISTS '%s' (", pszBlockName);
     654        9570 :             for (int i = 0; i < poDataBlock->GetPropertyCount(); i++)
     655             :             {
     656        8655 :                 VFKPropertyDefn *poPropertyDefn = poDataBlock->GetProperty(i);
     657        8655 :                 if (i > 0)
     658        7740 :                     osCommand += ",";
     659             :                 osColumn.Printf("%s %s", poPropertyDefn->GetName(),
     660        8655 :                                 poPropertyDefn->GetTypeSQL().c_str());
     661        8655 :                 osCommand += osColumn;
     662             :             }
     663         915 :             osColumn.Printf(",%s integer", FID_COLUMN);
     664         915 :             osCommand += osColumn;
     665         915 :             if (poDataBlock->GetGeometryType() != wkbNone)
     666             :             {
     667         168 :                 osColumn.Printf(",%s blob", GEOM_COLUMN);
     668         168 :                 osCommand += osColumn;
     669             :             }
     670         915 :             osCommand += ")";
     671         915 :             ExecuteSQL(osCommand.c_str()); /* CREATE TABLE */
     672             : 
     673             :             /* update VFK_DB_TABLE meta-table */
     674             :             osCommand.Printf(
     675             :                 "INSERT INTO %s (file_name, file_size, table_name, "
     676             :                 "num_records, num_features, num_geometries, table_defn) VALUES "
     677             :                 "('%s', " CPL_FRMT_GUIB ", '%s', -1, 0, 0, '%s')",
     678         915 :                 VFK_DB_TABLE, CPLGetFilename(m_pszFilename),
     679         915 :                 (GUIntBig)m_poFStat->st_size, pszBlockName, pszDefn);
     680         915 :             ExecuteSQL(osCommand.c_str());
     681             : 
     682             :             int geom_type =
     683         915 :                 ((VFKDataBlockSQLite *)poDataBlock)->GetGeometrySQLType();
     684             :             /* update VFK_DB_GEOMETRY_TABLE */
     685             :             osCommand.Printf("INSERT INTO %s (f_table_name, f_geometry_column, "
     686             :                              "geometry_type, "
     687             :                              "coord_dimension, srid, geometry_format) VALUES "
     688             :                              "('%s', '%s', %d, 2, 5514, 'WKB')",
     689             :                              VFK_DB_GEOMETRY_TABLE, pszBlockName, GEOM_COLUMN,
     690         915 :                              geom_type);
     691         915 :             ExecuteSQL(osCommand.c_str());
     692             :         }
     693         915 :         sqlite3_finalize(hStmt);
     694             :     }
     695             : 
     696        1830 :     return VFKReader::AddDataBlock(poDataBlock, nullptr);
     697             : }
     698             : 
     699             : /*!
     700             :   \brief Prepare SQL statement
     701             : 
     702             :   \param pszSQLCommand SQL statement to be prepared
     703             : 
     704             :   \return pointer to sqlite3_stmt instance or NULL on error
     705             : */
     706        3554 : sqlite3_stmt *VFKReaderSQLite::PrepareStatement(const char *pszSQLCommand)
     707             : {
     708        3554 :     CPLDebug("OGR-VFK", "VFKReaderSQLite::PrepareStatement(): %s",
     709             :              pszSQLCommand);
     710             : 
     711        3554 :     sqlite3_stmt *hStmt = nullptr;
     712             :     const int rc =
     713        3554 :         sqlite3_prepare_v2(m_poDB, pszSQLCommand, -1, &hStmt, nullptr);
     714             : 
     715             :     // TODO(schwehr): if( rc == SQLITE_OK ) return NULL;
     716        3554 :     if (rc != SQLITE_OK)
     717             :     {
     718           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     719             :                  "In PrepareStatement(): sqlite3_prepare_v2(%s):\n  %s",
     720             :                  pszSQLCommand, sqlite3_errmsg(m_poDB));
     721             : 
     722           0 :         if (hStmt != nullptr)
     723             :         {
     724           0 :             sqlite3_finalize(hStmt);
     725             :         }
     726             : 
     727           0 :         return nullptr;
     728             :     }
     729             : 
     730        3554 :     return hStmt;
     731             : }
     732             : 
     733             : /*!
     734             :   \brief Execute prepared SQL statement
     735             : 
     736             :   \param hStmt pointer to sqlite3_stmt
     737             : 
     738             :   \return OGRERR_NONE on success
     739             : */
     740        4665 : OGRErr VFKReaderSQLite::ExecuteSQL(sqlite3_stmt *&hStmt)
     741             : {
     742        4665 :     const int rc = sqlite3_step(hStmt);
     743        4665 :     if (rc != SQLITE_ROW)
     744             :     {
     745         859 :         if (rc == SQLITE_DONE)
     746             :         {
     747         859 :             sqlite3_finalize(hStmt);
     748         859 :             hStmt = nullptr;
     749         859 :             return OGRERR_NOT_ENOUGH_DATA;
     750             :         }
     751             : 
     752           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     753             :                  "In ExecuteSQL(): sqlite3_step:\n  %s",
     754             :                  sqlite3_errmsg(m_poDB));
     755           0 :         if (hStmt)
     756             :         {
     757           0 :             sqlite3_finalize(hStmt);
     758           0 :             hStmt = nullptr;
     759             :         }
     760           0 :         return OGRERR_FAILURE;
     761             :     }
     762             : 
     763        3806 :     return OGRERR_NONE;
     764             : }
     765             : 
     766             : /*!
     767             :   \brief Execute SQL statement (SQLITE only)
     768             : 
     769             :   \param pszSQLCommand SQL command to execute
     770             :   \param eErrLevel if equal to CE_None, no error message will be emitted on
     771             :   failure.
     772             : 
     773             :   \return OGRERR_NONE on success or OGRERR_FAILURE on failure
     774             : */
     775        7341 : OGRErr VFKReaderSQLite::ExecuteSQL(const char *pszSQLCommand, CPLErr eErrLevel)
     776             : {
     777        7341 :     char *pszErrMsg = nullptr;
     778             : 
     779        7341 :     if (SQLITE_OK !=
     780        7341 :         sqlite3_exec(m_poDB, pszSQLCommand, nullptr, nullptr, &pszErrMsg))
     781             :     {
     782           0 :         if (eErrLevel != CE_None)
     783             :         {
     784           0 :             CPLError(eErrLevel, CPLE_AppDefined, "In ExecuteSQL(%s): %s",
     785           0 :                      pszSQLCommand, pszErrMsg ? pszErrMsg : "(null)");
     786             :         }
     787           0 :         sqlite3_free(pszErrMsg);
     788             : 
     789           0 :         return OGRERR_FAILURE;
     790             :     }
     791             : 
     792        7341 :     return OGRERR_NONE;
     793             : }
     794             : 
     795             : /*!
     796             :   \brief Add feature
     797             : 
     798             :   \param poDataBlock pointer to VFKDataBlock instance
     799             :   \param poFeature pointer to VFKFeature instance
     800             : */
     801         870 : OGRErr VFKReaderSQLite::AddFeature(IVFKDataBlock *poDataBlock,
     802             :                                    VFKFeature *poFeature)
     803             : {
     804        1740 :     CPLString osValue;
     805             : 
     806         870 :     const VFKProperty *poProperty = nullptr;
     807             : 
     808         870 :     const char *pszBlockName = poDataBlock->GetName();
     809        1740 :     CPLString osCommand;
     810         870 :     osCommand.Printf("INSERT INTO '%s' VALUES(", pszBlockName);
     811             : 
     812       11790 :     for (int i = 0; i < poDataBlock->GetPropertyCount(); i++)
     813             :     {
     814       10920 :         const OGRFieldType ftype = poDataBlock->GetProperty(i)->GetType();
     815       10920 :         poProperty = poFeature->GetProperty(i);
     816       10920 :         if (i > 0)
     817       10050 :             osCommand += ",";
     818             : 
     819       10920 :         if (poProperty->IsNull())
     820             :         {
     821        3135 :             osValue.Printf("NULL");
     822             :         }
     823             :         else
     824             :         {
     825        7785 :             switch (ftype)
     826             :             {
     827        2205 :                 case OFTInteger:
     828        2205 :                     osValue.Printf("%d", poProperty->GetValueI());
     829        2205 :                     break;
     830        3870 :                 case OFTInteger64:
     831        3870 :                     osValue.Printf(CPL_FRMT_GIB, poProperty->GetValueI64());
     832        3870 :                     break;
     833         390 :                 case OFTReal:
     834         390 :                     osValue.Printf("%f", poProperty->GetValueD());
     835         390 :                     break;
     836        1320 :                 case OFTString:
     837        1320 :                     osValue.Printf("'%s'", poProperty->GetValueS(true));
     838        1320 :                     break;
     839           0 :                 default:
     840           0 :                     osValue.Printf("'%s'", poProperty->GetValueS(true));
     841           0 :                     break;
     842             :             }
     843             :         }
     844       10920 :         osCommand += osValue;
     845             :     }
     846         870 :     osValue.Printf("," CPL_FRMT_GIB, poFeature->GetFID());
     847         870 :     if (poDataBlock->GetGeometryType() != wkbNone)
     848             :     {
     849         784 :         osValue += ",NULL";
     850             :     }
     851         870 :     osCommand += osValue;
     852         870 :     osCommand += ")";
     853             : 
     854         870 :     if (ExecuteSQL(osCommand.c_str(), CE_Warning) != OGRERR_NONE)
     855           0 :         return OGRERR_FAILURE;
     856             : 
     857         870 :     if (EQUAL(pszBlockName, "SBP") || EQUAL(pszBlockName, "SBPG"))
     858             :     {
     859         435 :         poProperty = poFeature->GetProperty("PORADOVE_CISLO_BODU");
     860         435 :         if (poProperty == nullptr)
     861             :         {
     862           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     863             :                      "Cannot find property PORADOVE_CISLO_BODU");
     864           0 :             return OGRERR_FAILURE;
     865             :         }
     866         435 :         if (poProperty->GetValueI64() != 1)
     867         225 :             return OGRERR_NONE;
     868             :     }
     869             : 
     870             :     VFKFeatureSQLite *poNewFeature = new VFKFeatureSQLite(
     871         645 :         poDataBlock, poDataBlock->GetRecordCount(RecordValid) + 1,
     872         645 :         poFeature->GetFID());
     873         645 :     poDataBlock->AddFeature(poNewFeature);
     874             : 
     875         645 :     return OGRERR_NONE;
     876             : }

Generated by: LCOV version 1.14