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 : }
|