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 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 : const int64_t nExtraRecords = VFKReader::ReadDataRecords(poDataBlock);
446 15 : if (nExtraRecords >= 0)
447 : {
448 15 : nDataRecords += nExtraRecords;
449 :
450 : /* update VFK_DB_TABLE table */
451 15 : poDataBlockCurrent = nullptr;
452 930 : for (int iDataBlock = 0; iDataBlock < GetDataBlockCount();
453 : iDataBlock++)
454 : {
455 915 : poDataBlockCurrent = GetDataBlock(iDataBlock);
456 :
457 915 : if (poDataBlock && poDataBlock != poDataBlockCurrent)
458 0 : continue;
459 :
460 : /* update number of records in metadata table */
461 : osSQL.Printf("UPDATE %s SET num_records = %d WHERE "
462 : "table_name = '%s'",
463 : VFK_DB_TABLE, poDataBlockCurrent->GetRecordCount(),
464 915 : poDataBlockCurrent->GetName());
465 :
466 915 : ExecuteSQL(osSQL);
467 : }
468 : }
469 :
470 : /* create indices if not exist */
471 15 : CreateIndices();
472 :
473 : /* commit transaction */
474 15 : ExecuteSQL("COMMIT");
475 : }
476 :
477 32 : return nDataRecords;
478 : }
479 :
480 : /*!
481 : \brief Store header info to VFK_DB_HEADER
482 : */
483 15 : void VFKReaderSQLite::StoreInfo2DB()
484 : {
485 210 : for (std::map<CPLString, CPLString>::iterator i = poInfo.begin();
486 405 : i != poInfo.end(); ++i)
487 : {
488 195 : const char *value = i->second.c_str();
489 :
490 195 : const char q = (value[0] == '"') ? ' ' : '"';
491 :
492 390 : CPLString osSQL;
493 : osSQL.Printf("INSERT INTO %s VALUES(\"%s\", %c%s%c)",
494 195 : VFK_DB_HEADER_TABLE, i->first.c_str(), q, value, q);
495 195 : ExecuteSQL(osSQL);
496 : }
497 15 : }
498 :
499 : /*!
500 : \brief Create indices for newly added db tables (datablocks)
501 : */
502 15 : void VFKReaderSQLite::CreateIndices()
503 : {
504 : const char *pszBlockName;
505 30 : CPLString osIndexName, osSQL;
506 : VFKDataBlockSQLite *poDataBlock;
507 :
508 : sqlite3_stmt *hStmt;
509 :
510 930 : for (int iLayer = 0; iLayer < GetDataBlockCount(); iLayer++)
511 : {
512 915 : poDataBlock = (VFKDataBlockSQLite *)GetDataBlock(iLayer);
513 915 : pszBlockName = poDataBlock->GetName();
514 :
515 : /* ogr_fid */
516 915 : osIndexName.Printf("%s_%s", pszBlockName, FID_COLUMN);
517 :
518 : /* check if index on ogr_fid column exists */
519 : osSQL.Printf("SELECT COUNT(*) FROM sqlite_master WHERE type = 'index' "
520 : "AND name = '%s'",
521 915 : osIndexName.c_str());
522 915 : hStmt = PrepareStatement(osSQL.c_str());
523 :
524 1830 : if (ExecuteSQL(hStmt) == OGRERR_NONE &&
525 915 : sqlite3_column_int(hStmt, 0) > 0)
526 : {
527 : /* index on ogr_fid column exists, skip creating indices
528 : for current datablock */
529 0 : sqlite3_finalize(hStmt);
530 0 : continue;
531 : }
532 915 : sqlite3_finalize(hStmt);
533 :
534 : /* create index on ogr_fid */
535 915 : CreateIndex(
536 : osIndexName.c_str(), pszBlockName, FID_COLUMN,
537 915 : !(EQUAL(pszBlockName, "SBP") || EQUAL(pszBlockName, "SBPG")));
538 :
539 915 : if (poDataBlock->GetGeometryType() == wkbNone)
540 : {
541 : /* skip geometry-related indices */
542 747 : continue;
543 : }
544 :
545 168 : if (EQUAL(pszBlockName, "SOBR") || EQUAL(pszBlockName, "OBBP") ||
546 140 : EQUAL(pszBlockName, "SPOL") || EQUAL(pszBlockName, "OB") ||
547 112 : EQUAL(pszBlockName, "OP") || EQUAL(pszBlockName, "OBPEJ") ||
548 84 : EQUAL(pszBlockName, "SBP") || EQUAL(pszBlockName, "SBPG") ||
549 70 : EQUAL(pszBlockName, "HP") || EQUAL(pszBlockName, "DPM") ||
550 42 : EQUAL(pszBlockName, "ZVB") || EQUAL(pszBlockName, "PAR") ||
551 14 : EQUAL(pszBlockName, "BUD"))
552 : {
553 168 : const char *pszKey = ((VFKDataBlockSQLite *)poDataBlock)->GetKey();
554 168 : if (pszKey)
555 : {
556 : /* ID */
557 168 : osIndexName.Printf("%s_%s", pszBlockName, pszKey);
558 168 : CreateIndex(osIndexName.c_str(), pszBlockName, pszKey,
559 168 : !m_bAmendment);
560 : }
561 : }
562 :
563 : /* create other indices used for building geometry */
564 168 : if (EQUAL(pszBlockName, "SBP"))
565 : {
566 : /* SBP */
567 14 : CreateIndex("SBP_OB", pszBlockName, "OB_ID", false);
568 14 : CreateIndex("SBP_HP", pszBlockName, "HP_ID", false);
569 14 : CreateIndex("SBP_DPM", pszBlockName, "DPM_ID", false);
570 14 : CreateIndex("SBP_OB_HP_DPM", pszBlockName, "OB_ID,HP_ID,DPM_ID",
571 : true);
572 14 : CreateIndex("SBP_OB_POR", pszBlockName, "OB_ID,PORADOVE_CISLO_BODU",
573 : false);
574 14 : CreateIndex("SBP_HP_POR", pszBlockName, "HP_ID,PORADOVE_CISLO_BODU",
575 : false);
576 14 : CreateIndex("SBP_DPM_POR", pszBlockName,
577 : "DPM_ID,PORADOVE_CISLO_BODU", false);
578 : }
579 154 : else if (EQUAL(pszBlockName, "HP"))
580 : {
581 : /* HP */
582 14 : CreateIndex("HP_PAR1", pszBlockName, "PAR_ID_1", false);
583 14 : CreateIndex("HP_PAR2", pszBlockName, "PAR_ID_2", false);
584 : }
585 140 : else if (EQUAL(pszBlockName, "OB"))
586 : {
587 : /* OP */
588 14 : CreateIndex("OB_BUD", pszBlockName, "BUD_ID", false);
589 : }
590 : }
591 15 : }
592 :
593 : /*!
594 : \brief Create index
595 :
596 : If creating unique index fails, then non-unique index is created instead.
597 :
598 : \param name index name
599 : \param table table name
600 : \param column column(s) name
601 : \param unique true to create unique index
602 : */
603 1223 : void VFKReaderSQLite::CreateIndex(const char *name, const char *table,
604 : const char *column, bool unique)
605 : {
606 1223 : CPLString osSQL;
607 :
608 1223 : if (unique)
609 : {
610 1082 : osSQL.Printf("CREATE UNIQUE INDEX %s ON %s (%s)", name, table, column);
611 1082 : if (ExecuteSQL(osSQL.c_str()) == OGRERR_NONE)
612 : {
613 1082 : return;
614 : }
615 : }
616 :
617 141 : osSQL.Printf("CREATE INDEX %s ON %s (%s)", name, table, column);
618 141 : ExecuteSQL(osSQL.c_str());
619 : }
620 :
621 : /*!
622 : \brief Create new data block
623 :
624 : \param pszBlockName name of the block to be created
625 :
626 : \return pointer to VFKDataBlockSQLite instance
627 : */
628 976 : IVFKDataBlock *VFKReaderSQLite::CreateDataBlock(const char *pszBlockName)
629 : {
630 : /* create new data block, i.e. table in DB */
631 976 : return new VFKDataBlockSQLite(pszBlockName, (IVFKReader *)this);
632 : }
633 :
634 : /*!
635 : \brief Create DB table from VFKDataBlock (SQLITE only)
636 :
637 : \param poDataBlock pointer to VFKDataBlock instance
638 : */
639 915 : void VFKReaderSQLite::AddDataBlock(IVFKDataBlock *poDataBlock,
640 : const char *pszDefn)
641 : {
642 1830 : CPLString osColumn;
643 :
644 915 : const char *pszBlockName = poDataBlock->GetName();
645 :
646 : /* register table in VFK_DB_TABLE */
647 1830 : CPLString osCommand;
648 : osCommand.Printf("SELECT COUNT(*) FROM %s WHERE "
649 : "table_name = '%s'",
650 915 : VFK_DB_TABLE, pszBlockName);
651 915 : sqlite3_stmt *hStmt = PrepareStatement(osCommand.c_str());
652 :
653 915 : if (ExecuteSQL(hStmt) == OGRERR_NONE)
654 : {
655 915 : if (sqlite3_column_int(hStmt, 0) == 0)
656 : {
657 :
658 915 : osCommand.Printf("CREATE TABLE IF NOT EXISTS '%s' (", pszBlockName);
659 9570 : for (int i = 0; i < poDataBlock->GetPropertyCount(); i++)
660 : {
661 8655 : VFKPropertyDefn *poPropertyDefn = poDataBlock->GetProperty(i);
662 8655 : if (i > 0)
663 7740 : osCommand += ",";
664 : osColumn.Printf("%s %s", poPropertyDefn->GetName(),
665 8655 : poPropertyDefn->GetTypeSQL().c_str());
666 8655 : osCommand += osColumn;
667 : }
668 915 : osColumn.Printf(",%s integer", FID_COLUMN);
669 915 : osCommand += osColumn;
670 915 : if (poDataBlock->GetGeometryType() != wkbNone)
671 : {
672 168 : osColumn.Printf(",%s blob", GEOM_COLUMN);
673 168 : osCommand += osColumn;
674 : }
675 915 : osCommand += ")";
676 915 : ExecuteSQL(osCommand.c_str()); /* CREATE TABLE */
677 :
678 : /* update VFK_DB_TABLE meta-table */
679 : osCommand.Printf(
680 : "INSERT INTO %s (file_name, file_size, table_name, "
681 : "num_records, num_features, num_geometries, table_defn) VALUES "
682 : "('%s', " CPL_FRMT_GUIB ", '%s', -1, 0, 0, '%s')",
683 915 : VFK_DB_TABLE, CPLGetFilename(m_pszFilename),
684 915 : (GUIntBig)m_poFStat->st_size, pszBlockName, pszDefn);
685 915 : ExecuteSQL(osCommand.c_str());
686 :
687 : int geom_type =
688 915 : ((VFKDataBlockSQLite *)poDataBlock)->GetGeometrySQLType();
689 : /* update VFK_DB_GEOMETRY_TABLE */
690 : osCommand.Printf("INSERT INTO %s (f_table_name, f_geometry_column, "
691 : "geometry_type, "
692 : "coord_dimension, srid, geometry_format) VALUES "
693 : "('%s', '%s', %d, 2, 5514, 'WKB')",
694 : VFK_DB_GEOMETRY_TABLE, pszBlockName, GEOM_COLUMN,
695 915 : geom_type);
696 915 : ExecuteSQL(osCommand.c_str());
697 : }
698 915 : sqlite3_finalize(hStmt);
699 : }
700 :
701 1830 : return VFKReader::AddDataBlock(poDataBlock, nullptr);
702 : }
703 :
704 : /*!
705 : \brief Prepare SQL statement
706 :
707 : \param pszSQLCommand SQL statement to be prepared
708 :
709 : \return pointer to sqlite3_stmt instance or NULL on error
710 : */
711 3554 : sqlite3_stmt *VFKReaderSQLite::PrepareStatement(const char *pszSQLCommand)
712 : {
713 3554 : CPLDebug("OGR-VFK", "VFKReaderSQLite::PrepareStatement(): %s",
714 : pszSQLCommand);
715 :
716 3554 : sqlite3_stmt *hStmt = nullptr;
717 : const int rc =
718 3554 : sqlite3_prepare_v2(m_poDB, pszSQLCommand, -1, &hStmt, nullptr);
719 :
720 : // TODO(schwehr): if( rc == SQLITE_OK ) return NULL;
721 3554 : if (rc != SQLITE_OK)
722 : {
723 0 : CPLError(CE_Failure, CPLE_AppDefined,
724 : "In PrepareStatement(): sqlite3_prepare_v2(%s):\n %s",
725 : pszSQLCommand, sqlite3_errmsg(m_poDB));
726 :
727 0 : if (hStmt != nullptr)
728 : {
729 0 : sqlite3_finalize(hStmt);
730 : }
731 :
732 0 : return nullptr;
733 : }
734 :
735 3554 : return hStmt;
736 : }
737 :
738 : /*!
739 : \brief Execute prepared SQL statement
740 :
741 : \param hStmt pointer to sqlite3_stmt
742 :
743 : \return OGRERR_NONE on success
744 : */
745 4665 : OGRErr VFKReaderSQLite::ExecuteSQL(sqlite3_stmt *&hStmt)
746 : {
747 4665 : const int rc = sqlite3_step(hStmt);
748 4665 : if (rc != SQLITE_ROW)
749 : {
750 859 : if (rc == SQLITE_DONE)
751 : {
752 859 : sqlite3_finalize(hStmt);
753 859 : hStmt = nullptr;
754 859 : return OGRERR_NOT_ENOUGH_DATA;
755 : }
756 :
757 0 : CPLError(CE_Failure, CPLE_AppDefined,
758 : "In ExecuteSQL(): sqlite3_step:\n %s",
759 : sqlite3_errmsg(m_poDB));
760 0 : if (hStmt)
761 : {
762 0 : sqlite3_finalize(hStmt);
763 0 : hStmt = nullptr;
764 : }
765 0 : return OGRERR_FAILURE;
766 : }
767 :
768 3806 : return OGRERR_NONE;
769 : }
770 :
771 : /*!
772 : \brief Execute SQL statement (SQLITE only)
773 :
774 : \param pszSQLCommand SQL command to execute
775 : \param eErrLevel if equal to CE_None, no error message will be emitted on
776 : failure.
777 :
778 : \return OGRERR_NONE on success or OGRERR_FAILURE on failure
779 : */
780 7341 : OGRErr VFKReaderSQLite::ExecuteSQL(const char *pszSQLCommand, CPLErr eErrLevel)
781 : {
782 7341 : char *pszErrMsg = nullptr;
783 :
784 7341 : if (SQLITE_OK !=
785 7341 : sqlite3_exec(m_poDB, pszSQLCommand, nullptr, nullptr, &pszErrMsg))
786 : {
787 0 : if (eErrLevel != CE_None)
788 : {
789 0 : CPLError(eErrLevel, CPLE_AppDefined, "In ExecuteSQL(%s): %s",
790 0 : pszSQLCommand, pszErrMsg ? pszErrMsg : "(null)");
791 : }
792 0 : sqlite3_free(pszErrMsg);
793 :
794 0 : return OGRERR_FAILURE;
795 : }
796 :
797 7341 : return OGRERR_NONE;
798 : }
799 :
800 : /*!
801 : \brief Add feature
802 :
803 : \param poDataBlock pointer to VFKDataBlock instance
804 : \param poFeature pointer to VFKFeature instance
805 : */
806 870 : OGRErr VFKReaderSQLite::AddFeature(IVFKDataBlock *poDataBlock,
807 : VFKFeature *poFeature)
808 : {
809 1740 : CPLString osValue;
810 :
811 870 : const VFKProperty *poProperty = nullptr;
812 :
813 870 : const char *pszBlockName = poDataBlock->GetName();
814 1740 : CPLString osCommand;
815 870 : osCommand.Printf("INSERT INTO '%s' VALUES(", pszBlockName);
816 :
817 11790 : for (int i = 0; i < poDataBlock->GetPropertyCount(); i++)
818 : {
819 10920 : const OGRFieldType ftype = poDataBlock->GetProperty(i)->GetType();
820 10920 : poProperty = poFeature->GetProperty(i);
821 10920 : if (i > 0)
822 10050 : osCommand += ",";
823 :
824 10920 : if (poProperty->IsNull())
825 : {
826 3135 : osValue.Printf("NULL");
827 : }
828 : else
829 : {
830 7785 : switch (ftype)
831 : {
832 2205 : case OFTInteger:
833 2205 : osValue.Printf("%d", poProperty->GetValueI());
834 2205 : break;
835 3870 : case OFTInteger64:
836 3870 : osValue.Printf(CPL_FRMT_GIB, poProperty->GetValueI64());
837 3870 : break;
838 390 : case OFTReal:
839 390 : osValue.Printf("%f", poProperty->GetValueD());
840 390 : break;
841 1320 : case OFTString:
842 1320 : osValue.Printf("'%s'", poProperty->GetValueS(true));
843 1320 : break;
844 0 : default:
845 0 : osValue.Printf("'%s'", poProperty->GetValueS(true));
846 0 : break;
847 : }
848 : }
849 10920 : osCommand += osValue;
850 : }
851 870 : osValue.Printf("," CPL_FRMT_GIB, poFeature->GetFID());
852 870 : if (poDataBlock->GetGeometryType() != wkbNone)
853 : {
854 784 : osValue += ",NULL";
855 : }
856 870 : osCommand += osValue;
857 870 : osCommand += ")";
858 :
859 870 : if (ExecuteSQL(osCommand.c_str(), CE_Warning) != OGRERR_NONE)
860 0 : return OGRERR_FAILURE;
861 :
862 870 : if (EQUAL(pszBlockName, "SBP") || EQUAL(pszBlockName, "SBPG"))
863 : {
864 435 : poProperty = poFeature->GetProperty("PORADOVE_CISLO_BODU");
865 435 : if (poProperty == nullptr)
866 : {
867 0 : CPLError(CE_Failure, CPLE_AppDefined,
868 : "Cannot find property PORADOVE_CISLO_BODU");
869 0 : return OGRERR_FAILURE;
870 : }
871 435 : if (poProperty->GetValueI64() != 1)
872 225 : return OGRERR_NONE;
873 : }
874 :
875 : VFKFeatureSQLite *poNewFeature = new VFKFeatureSQLite(
876 645 : poDataBlock, poDataBlock->GetRecordCount(RecordValid) + 1,
877 645 : poFeature->GetFID());
878 645 : poDataBlock->AddFeature(poNewFeature);
879 :
880 645 : return OGRERR_NONE;
881 : }
|