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