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