Line data Source code
1 : /**********************************************************************
2 : *
3 : * Name: mitab_tabfile.cpp
4 : * Project: MapInfo TAB Read/Write library
5 : * Language: C++
6 : * Purpose: Implementation of the TABView class, used to handle .TAB
7 : * datasets composed of a number of .TAB files linked through
8 : * indexed fields.
9 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca
10 : *
11 : **********************************************************************
12 : * Copyright (c) 1999-2002, Daniel Morissette
13 : * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
14 : *
15 : * SPDX-License-Identifier: MIT
16 : **********************************************************************/
17 :
18 : #include "cpl_port.h"
19 : #include "mitab.h"
20 :
21 : #include <cctype>
22 : #include <cstddef>
23 : #include <cstdio>
24 : #include <cstring>
25 :
26 : #include "cpl_conv.h"
27 : #include "cpl_error.h"
28 : #include "cpl_string.h"
29 : #include "cpl_vsi.h"
30 : #include "mitab_priv.h"
31 : #include "mitab_utils.h"
32 : #include "ogr_core.h"
33 : #include "ogr_feature.h"
34 : #include "ogr_geometry.h"
35 : #include "ogr_spatialref.h"
36 :
37 : /*=====================================================================
38 : * class TABView
39 : *====================================================================*/
40 :
41 : /**********************************************************************
42 : * TABView::TABView()
43 : *
44 : * Constructor.
45 : **********************************************************************/
46 2 : TABView::TABView(GDALDataset *poDS)
47 : : IMapInfoFile(poDS), m_pszFname(nullptr), m_eAccessMode(TABRead),
48 : m_papszTABFile(nullptr), m_pszVersion(nullptr), m_papszTABFnames(nullptr),
49 : m_papoTABFiles(nullptr), m_numTABFiles(0), m_nMainTableIndex(-1),
50 : m_papszFieldNames(nullptr), m_papszWhereClause(nullptr),
51 2 : m_poRelation(nullptr), m_bRelFieldsCreated(FALSE)
52 : {
53 2 : }
54 :
55 : /**********************************************************************
56 : * TABView::~TABView()
57 : *
58 : * Destructor.
59 : **********************************************************************/
60 4 : TABView::~TABView()
61 : {
62 2 : TABView::Close();
63 4 : }
64 :
65 0 : GIntBig TABView::GetFeatureCount(int bForce)
66 : {
67 :
68 0 : if (m_nMainTableIndex != -1)
69 0 : return m_papoTABFiles[m_nMainTableIndex]->GetFeatureCount(bForce);
70 :
71 0 : return 0;
72 : }
73 :
74 0 : void TABView::ResetReading()
75 : {
76 0 : if (m_nMainTableIndex != -1)
77 0 : m_papoTABFiles[m_nMainTableIndex]->ResetReading();
78 0 : }
79 :
80 : /**********************************************************************
81 : * TABView::Open()
82 : *
83 : * Open a .TAB dataset and the associated files, and initialize the
84 : * structures to be ready to read features from it.
85 : *
86 : * This class is used to open .TAB files that define a view on
87 : * two other .TAB files. Regular .TAB datasets should be opened using
88 : * the TABFile class instead.
89 : *
90 : * Set bTestOpenNoError=TRUE to silently return -1 with no error message
91 : * if the file cannot be opened. This is intended to be used in the
92 : * context of a TestOpen() function. The default value is FALSE which
93 : * means that an error is reported if the file cannot be opened.
94 : *
95 : * Returns 0 on success, -1 on error.
96 : **********************************************************************/
97 2 : int TABView::Open(const char *pszFname, TABAccess eAccess,
98 : GBool bTestOpenNoError /*= FALSE*/,
99 : const char *pszCharset /* = NULL */)
100 : {
101 2 : char nStatus = 0;
102 :
103 2 : if (m_numTABFiles > 0)
104 : {
105 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
106 : "Open() failed: object already contains an open file");
107 0 : return -1;
108 : }
109 :
110 : /*-----------------------------------------------------------------
111 : * Validate access mode and call the right open method
112 : *----------------------------------------------------------------*/
113 2 : if (eAccess == TABRead)
114 : {
115 2 : m_eAccessMode = TABRead;
116 2 : nStatus = static_cast<char>(OpenForRead(pszFname, bTestOpenNoError));
117 : }
118 0 : else if (eAccess == TABWrite)
119 : {
120 0 : m_eAccessMode = TABWrite;
121 0 : if (pszCharset != nullptr)
122 0 : SetCharset(pszCharset);
123 0 : nStatus = static_cast<char>(OpenForWrite(pszFname));
124 : }
125 : else
126 : {
127 0 : CPLError(CE_Failure, CPLE_NotSupported,
128 : "Open() failed: access mode \"%d\" not supported", eAccess);
129 0 : return -1;
130 : }
131 :
132 2 : return nStatus;
133 : }
134 :
135 : /**********************************************************************
136 : * TABView::OpenForRead()
137 : *
138 : * Open for reading
139 : *
140 : * Returns 0 on success, -1 on error.
141 : **********************************************************************/
142 2 : int TABView::OpenForRead(const char *pszFname,
143 : GBool bTestOpenNoError /*= FALSE*/)
144 : {
145 2 : char *pszPath = nullptr;
146 2 : int nFnameLen = 0;
147 :
148 2 : m_eAccessMode = TABRead;
149 :
150 : /*-----------------------------------------------------------------
151 : * Read main .TAB (text) file
152 : *----------------------------------------------------------------*/
153 2 : m_pszFname = CPLStrdup(pszFname);
154 :
155 : #ifndef _WIN32
156 : /*-----------------------------------------------------------------
157 : * On Unix, make sure extension uses the right cases
158 : * We do it even for write access because if a file with the same
159 : * extension already exists we want to overwrite it.
160 : *----------------------------------------------------------------*/
161 2 : TABAdjustFilenameExtension(m_pszFname);
162 : #endif
163 :
164 : /*-----------------------------------------------------------------
165 : * Open .TAB file... since it is a small text file, we will just load
166 : * it as a stringlist in memory.
167 : *----------------------------------------------------------------*/
168 2 : m_papszTABFile = TAB_CSLLoad(m_pszFname);
169 2 : if (m_papszTABFile == nullptr)
170 : {
171 0 : if (!bTestOpenNoError)
172 : {
173 0 : CPLError(CE_Failure, CPLE_FileIO, "Failed opening %s.", m_pszFname);
174 : }
175 :
176 0 : CPLFree(m_pszFname);
177 0 : m_pszFname = nullptr;
178 0 : return -1;
179 : }
180 :
181 : /*-------------------------------------------------------------
182 : * Look for a line with the "create view" keyword.
183 : * If there is no "create view", then we may have a valid .TAB file,
184 : * but we do not support it in this class.
185 : *------------------------------------------------------------*/
186 2 : GBool bCreateViewFound = FALSE;
187 14 : for (int i = 0; !bCreateViewFound && m_papszTABFile[i]; i++)
188 : {
189 12 : const char *pszStr = m_papszTABFile[i];
190 12 : while (*pszStr != '\0' && isspace(static_cast<unsigned char>(*pszStr)))
191 0 : pszStr++;
192 12 : if (STARTS_WITH_CI(pszStr, "create view"))
193 2 : bCreateViewFound = TRUE;
194 : }
195 :
196 2 : if (!bCreateViewFound)
197 : {
198 0 : if (!bTestOpenNoError)
199 0 : CPLError(CE_Failure, CPLE_NotSupported,
200 : "%s contains no table view definition. "
201 : "This type of .TAB file cannot be read by this library.",
202 : m_pszFname);
203 : else
204 0 : CPLErrorReset();
205 :
206 0 : CPLFree(m_pszFname);
207 0 : m_pszFname = nullptr;
208 :
209 0 : return -1;
210 : }
211 :
212 : /*-----------------------------------------------------------------
213 : * OK, this appears to be a valid TAB view dataset...
214 : * Extract the path component from the main .TAB filename
215 : * to build the filename of the sub-tables
216 : *----------------------------------------------------------------*/
217 2 : pszPath = CPLStrdup(m_pszFname);
218 2 : nFnameLen = static_cast<int>(strlen(pszPath));
219 79 : for (; nFnameLen > 0; nFnameLen--)
220 : {
221 79 : if (pszPath[nFnameLen - 1] == '/' || pszPath[nFnameLen - 1] == '\\')
222 : {
223 : break;
224 : }
225 77 : pszPath[nFnameLen - 1] = '\0';
226 : }
227 :
228 : /*-----------------------------------------------------------------
229 : * Extract the useful info from the TAB header
230 : *----------------------------------------------------------------*/
231 2 : if (ParseTABFile(pszPath, bTestOpenNoError) != 0)
232 : {
233 : // Failed parsing... an error has already been produced if necessary
234 0 : CPLFree(pszPath);
235 0 : Close();
236 0 : return -1;
237 : }
238 2 : CPLFree(pszPath);
239 2 : pszPath = nullptr;
240 :
241 : /*-----------------------------------------------------------------
242 : * __TODO__ For now, we support only 2 files linked through a single
243 : * field... so we'll do some validation first to make sure
244 : * that what we found in the header respects these limitations.
245 : *----------------------------------------------------------------*/
246 2 : if (m_numTABFiles != 2)
247 : {
248 0 : if (!bTestOpenNoError)
249 0 : CPLError(CE_Failure, CPLE_NotSupported,
250 : "Open Failed: Dataset %s defines a view on %d tables. "
251 : "This is not currently supported.",
252 : m_pszFname, m_numTABFiles);
253 0 : Close();
254 0 : return -1;
255 : }
256 :
257 : /*-----------------------------------------------------------------
258 : * Open all the tab files listed in the view
259 : *----------------------------------------------------------------*/
260 2 : m_papoTABFiles =
261 2 : static_cast<TABFile **>(CPLCalloc(m_numTABFiles, sizeof(TABFile *)));
262 :
263 6 : for (int iFile = 0; iFile < m_numTABFiles; iFile++)
264 : {
265 : #ifndef _WIN32
266 4 : TABAdjustFilenameExtension(m_papszTABFnames[iFile]);
267 : #endif
268 :
269 4 : m_papoTABFiles[iFile] = new TABFile(m_poDS);
270 :
271 4 : if (m_papoTABFiles[iFile]->Open(m_papszTABFnames[iFile], m_eAccessMode,
272 4 : bTestOpenNoError) != 0)
273 : {
274 : // Open Failed... an error has already been reported, just return.
275 0 : if (bTestOpenNoError)
276 0 : CPLErrorReset();
277 0 : Close();
278 0 : return -1;
279 : }
280 : }
281 :
282 : /*-----------------------------------------------------------------
283 : * Create TABRelation... this will build FeatureDefn, etc.
284 : * __TODO__ For now this assumes only 2 tables in the view...
285 : *----------------------------------------------------------------*/
286 2 : m_poRelation = new TABRelation;
287 :
288 2 : CPLAssert(m_nMainTableIndex == 0);
289 2 : CPLAssert(CSLCount(m_papszWhereClause) == 5);
290 2 : char *pszTableName = TABGetBasename(m_pszFname);
291 4 : if (m_poRelation->Init(pszTableName, m_papoTABFiles[0], m_papoTABFiles[1],
292 2 : m_papszWhereClause[4], m_papszWhereClause[2],
293 2 : m_papszFieldNames) != 0)
294 : {
295 : // An error should already have been reported
296 0 : CPLFree(pszTableName);
297 0 : Close();
298 0 : return -1;
299 : }
300 2 : CPLFree(pszTableName);
301 :
302 2 : return 0;
303 : }
304 :
305 : /**********************************************************************
306 : * TABView::OpenForWrite()
307 : *
308 : * Create a new TABView dataset
309 : *
310 : * Returns 0 on success, -1 on error.
311 : **********************************************************************/
312 0 : int TABView::OpenForWrite(const char *pszFname)
313 : {
314 0 : int nFnameLen = 0;
315 :
316 0 : m_eAccessMode = TABWrite;
317 :
318 : /*-----------------------------------------------------------------
319 : * Read main .TAB (text) file
320 : *----------------------------------------------------------------*/
321 0 : m_pszFname = CPLStrdup(pszFname);
322 :
323 : #ifndef _WIN32
324 : /*-----------------------------------------------------------------
325 : * On Unix, make sure extension uses the right cases
326 : * We do it even for write access because if a file with the same
327 : * extension already exists we want to overwrite it.
328 : *----------------------------------------------------------------*/
329 0 : TABAdjustFilenameExtension(m_pszFname);
330 : #endif
331 :
332 : /*-----------------------------------------------------------------
333 : * Extract the path component from the main .TAB filename
334 : *----------------------------------------------------------------*/
335 0 : char *pszPath = CPLStrdup(m_pszFname);
336 0 : nFnameLen = static_cast<int>(strlen(pszPath));
337 0 : for (; nFnameLen > 0; nFnameLen--)
338 : {
339 0 : if (pszPath[nFnameLen - 1] == '/' || pszPath[nFnameLen - 1] == '\\')
340 : {
341 : break;
342 : }
343 0 : pszPath[nFnameLen - 1] = '\0';
344 : }
345 :
346 0 : char *pszBasename = TABGetBasename(m_pszFname);
347 :
348 : /*-----------------------------------------------------------------
349 : * Create the 2 TAB files for the view.
350 : *
351 : * __TODO__ For now, we support only 2 files linked through a single
352 : * field... not sure if anything else than that can be useful
353 : * anyways.
354 : *----------------------------------------------------------------*/
355 0 : m_numTABFiles = 2;
356 0 : m_papszTABFnames = nullptr;
357 0 : m_nMainTableIndex = 0;
358 0 : m_bRelFieldsCreated = FALSE;
359 :
360 0 : m_papoTABFiles =
361 0 : static_cast<TABFile **>(CPLCalloc(m_numTABFiles, sizeof(TABFile *)));
362 :
363 0 : for (int iFile = 0; iFile < m_numTABFiles; iFile++)
364 : {
365 0 : m_papszTABFnames = CSLAppendPrintf(m_papszTABFnames, "%s%s%d.tab",
366 : pszPath, pszBasename, iFile + 1);
367 : #ifndef _WIN32
368 : /* coverity[var_deref_op] */
369 0 : TABAdjustFilenameExtension(m_papszTABFnames[iFile]);
370 : #endif
371 :
372 0 : m_papoTABFiles[iFile] = new TABFile(m_poDS);
373 :
374 0 : if (m_papoTABFiles[iFile]->Open(m_papszTABFnames[iFile], m_eAccessMode,
375 0 : FALSE, GetCharset()) != 0)
376 : {
377 : // Open Failed... an error has already been reported, just return.
378 0 : CPLFree(pszPath);
379 0 : CPLFree(pszBasename);
380 0 : Close();
381 0 : return -1;
382 : }
383 : }
384 :
385 : /*-----------------------------------------------------------------
386 : * Create TABRelation...
387 : *----------------------------------------------------------------*/
388 0 : m_poRelation = new TABRelation;
389 :
390 0 : if (m_poRelation->Init(pszBasename, m_papoTABFiles[0], m_papoTABFiles[1],
391 0 : nullptr, nullptr, nullptr) != 0)
392 : {
393 : // An error should already have been reported
394 0 : CPLFree(pszPath);
395 0 : CPLFree(pszBasename);
396 0 : Close();
397 0 : return -1;
398 : }
399 :
400 0 : CPLFree(pszPath);
401 0 : CPLFree(pszBasename);
402 :
403 0 : return 0;
404 : }
405 :
406 : /**********************************************************************
407 : * TABView::ParseTABFile()
408 : *
409 : * Scan the lines of the TAB file, and store any useful information into
410 : * class members. The main piece of information being the sub-table
411 : * names, and the list of fields to include in the view that we will
412 : * use to build the OGRFeatureDefn for this file.
413 : *
414 : * It is assumed that the TAB header file is already loaded in m_papszTABFile
415 : *
416 : * This private method should be used only during the Open() call.
417 : *
418 : * Returns 0 on success, -1 on error.
419 : **********************************************************************/
420 2 : int TABView::ParseTABFile(const char *pszDatasetPath,
421 : GBool bTestOpenNoError /*=FALSE*/)
422 : {
423 : int iLine, numLines;
424 2 : char **papszTok = nullptr;
425 2 : GBool bInsideTableDef = FALSE;
426 :
427 2 : if (m_eAccessMode != TABRead)
428 : {
429 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
430 : "ParseTABFile() can be used only with Read access.");
431 0 : return -1;
432 : }
433 :
434 2 : numLines = CSLCount(m_papszTABFile);
435 :
436 20 : for (iLine = 0; iLine < numLines; iLine++)
437 : {
438 : /*-------------------------------------------------------------
439 : * Tokenize the next .TAB line, and check first keyword
440 : *------------------------------------------------------------*/
441 18 : CSLDestroy(papszTok);
442 18 : papszTok = CSLTokenizeStringComplex(m_papszTABFile[iLine], " \t(),;",
443 : TRUE, FALSE);
444 18 : if (CSLCount(papszTok) < 2)
445 4 : continue; // All interesting lines have at least 2 tokens
446 :
447 14 : if (EQUAL(papszTok[0], "!version"))
448 : {
449 2 : CPLFree(m_pszVersion);
450 2 : m_pszVersion = CPLStrdup(papszTok[1]);
451 : }
452 12 : else if (EQUAL(papszTok[0], "!charset"))
453 : {
454 0 : CPLFree(m_pszCharset);
455 0 : m_pszCharset = CPLStrdup(papszTok[1]);
456 : }
457 16 : else if (EQUAL(papszTok[0], "open") && EQUAL(papszTok[1], "table") &&
458 4 : CSLCount(papszTok) >= 3)
459 : {
460 : // Source table name may be either "filename" or "filename.tab"
461 4 : int nLen = static_cast<int>(strlen(papszTok[2]));
462 4 : if (nLen > 4 && EQUAL(papszTok[2] + nLen - 4, ".tab"))
463 0 : papszTok[2][nLen - 4] = '\0';
464 :
465 4 : m_papszTABFnames = CSLAppendPrintf(m_papszTABFnames, "%s%s.tab",
466 4 : pszDatasetPath, papszTok[2]);
467 : }
468 8 : else if (EQUAL(papszTok[0], "create") && EQUAL(papszTok[1], "view"))
469 : {
470 2 : bInsideTableDef = TRUE;
471 : }
472 6 : else if (bInsideTableDef && (EQUAL(papszTok[0], "Select")))
473 : {
474 : /*---------------------------------------------------------
475 : * We found the list of table fields (comma-delimited list)
476 : *--------------------------------------------------------*/
477 5 : for (int iTok = 1; papszTok[iTok] != nullptr; iTok++)
478 3 : m_papszFieldNames =
479 5 : CSLAddString(m_papszFieldNames, papszTok[iTok]);
480 : }
481 4 : else if (bInsideTableDef && (EQUAL(papszTok[0], "where")))
482 : {
483 : /*---------------------------------------------------------
484 : * We found the where clause that relates the 2 tables
485 : * Something in the form:
486 : * where table1.field1=table2.field2
487 : * The tokenized array will contain:
488 : * {"where", "table1", "field1", "table2", "field2"}
489 : *--------------------------------------------------------*/
490 2 : CSLDestroy(m_papszWhereClause);
491 4 : m_papszWhereClause = CSLTokenizeStringComplex(
492 2 : m_papszTABFile[iLine], " \t(),;=.", TRUE, FALSE);
493 :
494 : /*---------------------------------------------------------
495 : * For now we are very limiting on the format of the WHERE
496 : * clause... we will be more permitting as we learn more about
497 : * what it can contain... (I don't want to implement a full SQL
498 : * parser here!!!). If you encountered this error,
499 : * (and are reading this!) please report the test dataset
500 : * that produced the error and I'll see if we can support it.
501 : *--------------------------------------------------------*/
502 2 : if (CSLCount(m_papszWhereClause) != 5)
503 : {
504 0 : if (!bTestOpenNoError)
505 0 : CPLError(CE_Failure, CPLE_NotSupported,
506 : "WHERE clause in %s is not in a supported format: "
507 : "\"%s\"",
508 0 : m_pszFname, m_papszTABFile[iLine]);
509 0 : CSLDestroy(papszTok);
510 0 : return -1;
511 : }
512 : }
513 : else
514 : {
515 : // Simply Ignore unrecognized lines
516 : }
517 : }
518 :
519 2 : CSLDestroy(papszTok);
520 :
521 : /*-----------------------------------------------------------------
522 : * The main table is the one from which we read the geometries, etc...
523 : * For now we assume it is always the first one in the list
524 : *----------------------------------------------------------------*/
525 2 : m_nMainTableIndex = 0;
526 :
527 : /*-----------------------------------------------------------------
528 : * Make sure all required class members are set
529 : *----------------------------------------------------------------*/
530 2 : m_numTABFiles = CSLCount(m_papszTABFnames);
531 :
532 2 : if (m_pszCharset == nullptr)
533 2 : m_pszCharset = CPLStrdup("Neutral");
534 2 : if (m_pszVersion == nullptr)
535 0 : m_pszVersion = CPLStrdup("100");
536 :
537 2 : if (CSLCount(m_papszFieldNames) == 0)
538 : {
539 0 : if (!bTestOpenNoError)
540 0 : CPLError(CE_Failure, CPLE_NotSupported,
541 : "%s: header contains no table field definition. "
542 : "This type of .TAB file cannot be read by this library.",
543 : m_pszFname);
544 0 : return -1;
545 : }
546 :
547 2 : if (CSLCount(m_papszWhereClause) == 0)
548 : {
549 0 : if (!bTestOpenNoError)
550 0 : CPLError(CE_Failure, CPLE_NotSupported,
551 : "%s: WHERE clause not found or missing in header. "
552 : "This type of .TAB file cannot be read by this library.",
553 : m_pszFname);
554 0 : return -1;
555 : }
556 2 : return 0;
557 : }
558 :
559 : /**********************************************************************
560 : * TABView::WriteTABFile()
561 : *
562 : * Generate the TAB header file. This is usually done during the
563 : * Close() call.
564 : *
565 : * Returns 0 on success, -1 on error.
566 : **********************************************************************/
567 0 : int TABView::WriteTABFile()
568 : {
569 0 : CPLAssert(m_eAccessMode == TABWrite);
570 0 : CPLAssert(m_numTABFiles == 2);
571 0 : CPLAssert(GetLayerDefn());
572 :
573 0 : char *pszTable = TABGetBasename(m_pszFname);
574 0 : char *pszTable1 = TABGetBasename(m_papszTABFnames[0]);
575 0 : char *pszTable2 = TABGetBasename(m_papszTABFnames[1]);
576 :
577 0 : VSILFILE *fp = VSIFOpenL(m_pszFname, "wt");
578 0 : if (fp != nullptr)
579 : {
580 : // Version is always 100, no matter what the sub-table's version is
581 0 : VSIFPrintfL(fp, "!Table\n");
582 0 : VSIFPrintfL(fp, "!Version 100\n");
583 :
584 0 : VSIFPrintfL(fp, "Open Table \"%s\" Hide\n", pszTable1);
585 0 : VSIFPrintfL(fp, "Open Table \"%s\" Hide\n", pszTable2);
586 0 : VSIFPrintfL(fp, "\n");
587 0 : VSIFPrintfL(fp, "Create View %s As\n", pszTable);
588 0 : VSIFPrintfL(fp, "Select ");
589 :
590 0 : OGRFeatureDefn *poDefn = GetLayerDefn();
591 0 : for (int iField = 0; iField < poDefn->GetFieldCount(); iField++)
592 : {
593 0 : OGRFieldDefn *poFieldDefn = poDefn->GetFieldDefn(iField);
594 0 : if (iField == 0)
595 0 : VSIFPrintfL(fp, "%s", poFieldDefn->GetNameRef());
596 : else
597 0 : VSIFPrintfL(fp, ",%s", poFieldDefn->GetNameRef());
598 : }
599 0 : VSIFPrintfL(fp, "\n");
600 :
601 0 : VSIFPrintfL(fp, "From %s, %s\n", pszTable2, pszTable1);
602 0 : VSIFPrintfL(fp, "Where %s.%s=%s.%s\n", pszTable2,
603 0 : m_poRelation->GetRelFieldName(), pszTable1,
604 0 : m_poRelation->GetMainFieldName());
605 :
606 0 : VSIFCloseL(fp);
607 : }
608 : else
609 : {
610 0 : CPLFree(pszTable);
611 0 : CPLFree(pszTable1);
612 0 : CPLFree(pszTable2);
613 :
614 0 : CPLError(CE_Failure, CPLE_FileIO, "Failed to create file `%s'",
615 : m_pszFname);
616 0 : return -1;
617 : }
618 :
619 0 : CPLFree(pszTable);
620 0 : CPLFree(pszTable1);
621 0 : CPLFree(pszTable2);
622 :
623 0 : return 0;
624 : }
625 :
626 : /**********************************************************************
627 : * TABView::Close()
628 : *
629 : * Close current file, and release all memory used.
630 : *
631 : * Returns 0 on success, -1 on error.
632 : **********************************************************************/
633 2 : int TABView::Close()
634 : {
635 : // In write access, the main .TAB file has not been written yet.
636 2 : if (m_eAccessMode == TABWrite && m_poRelation)
637 0 : WriteTABFile();
638 :
639 6 : for (int i = 0; m_papoTABFiles && i < m_numTABFiles; i++)
640 : {
641 4 : if (m_papoTABFiles[i])
642 4 : delete m_papoTABFiles[i]; // Automatically closes.
643 : }
644 2 : CPLFree(m_papoTABFiles);
645 2 : m_papoTABFiles = nullptr;
646 2 : m_numTABFiles = 0;
647 :
648 : /*-----------------------------------------------------------------
649 : * __TODO__ OK, MapInfo does not like to see a .map and .id file
650 : * attached to the second table, even if they're empty.
651 : * We'll use a little hack to delete them now, but eventually we
652 : * should avoid creating them at all.
653 : *----------------------------------------------------------------*/
654 2 : if (m_eAccessMode == TABWrite && m_pszFname)
655 : {
656 0 : m_pszFname[strlen(m_pszFname) - 4] = '\0';
657 0 : char *pszFile = CPLStrdup(CPLSPrintf("%s2.map", m_pszFname));
658 0 : TABAdjustFilenameExtension(pszFile);
659 0 : VSIUnlink(pszFile);
660 :
661 0 : snprintf(pszFile, strlen(pszFile) + 1, "%s2.id", m_pszFname);
662 0 : TABAdjustFilenameExtension(pszFile);
663 0 : VSIUnlink(pszFile);
664 :
665 0 : CPLFree(pszFile);
666 : }
667 : // End of hack!
668 :
669 2 : CPLFree(m_pszFname);
670 2 : m_pszFname = nullptr;
671 :
672 2 : CSLDestroy(m_papszTABFile);
673 2 : m_papszTABFile = nullptr;
674 :
675 2 : CPLFree(m_pszVersion);
676 2 : m_pszVersion = nullptr;
677 2 : CPLFree(m_pszCharset);
678 2 : m_pszCharset = nullptr;
679 :
680 2 : CSLDestroy(m_papszTABFnames);
681 2 : m_papszTABFnames = nullptr;
682 :
683 2 : CSLDestroy(m_papszFieldNames);
684 2 : m_papszFieldNames = nullptr;
685 2 : CSLDestroy(m_papszWhereClause);
686 2 : m_papszWhereClause = nullptr;
687 :
688 2 : m_nMainTableIndex = -1;
689 :
690 2 : if (m_poRelation)
691 2 : delete m_poRelation;
692 2 : m_poRelation = nullptr;
693 :
694 2 : m_bRelFieldsCreated = FALSE;
695 :
696 2 : return 0;
697 : }
698 :
699 : /**********************************************************************
700 : * TABView::SetQuickSpatialIndexMode()
701 : *
702 : * Select "quick spatial index mode".
703 : *
704 : * The default behavior of MITAB is to generate an optimized spatial index,
705 : * but this results in slower write speed.
706 : *
707 : * Applications that want faster write speed and do not care
708 : * about the performance of spatial queries on the resulting file can
709 : * use SetQuickSpatialIndexMode() to require the creation of a non-optimal
710 : * spatial index (actually emulating the type of spatial index produced
711 : * by MITAB before version 1.6.0). In this mode writing files can be
712 : * about 5 times faster, but spatial queries can be up to 30 times slower.
713 : *
714 : * Returns 0 on success, -1 on error.
715 : **********************************************************************/
716 0 : int TABView::SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode /*=TRUE*/)
717 : {
718 0 : if (m_eAccessMode != TABWrite || m_numTABFiles == 0)
719 : {
720 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
721 : "SetQuickSpatialIndexMode() failed: file not opened for write "
722 : "access.");
723 0 : return -1;
724 : }
725 :
726 0 : for (int iFile = 0; iFile < m_numTABFiles; iFile++)
727 : {
728 0 : if (m_papoTABFiles[iFile]->SetQuickSpatialIndexMode(
729 0 : bQuickSpatialIndexMode) != 0)
730 : {
731 : // An error has already been reported, just return.
732 0 : return -1;
733 : }
734 : }
735 :
736 0 : return 0;
737 : }
738 :
739 : /**********************************************************************
740 : * TABView::GetNextFeatureId()
741 : *
742 : * Returns feature id that follows nPrevId, or -1 if it is the
743 : * last feature id. Pass nPrevId=-1 to fetch the first valid feature id.
744 : **********************************************************************/
745 2 : GIntBig TABView::GetNextFeatureId(GIntBig nPrevId)
746 : {
747 2 : if (m_nMainTableIndex != -1)
748 2 : return m_papoTABFiles[m_nMainTableIndex]->GetNextFeatureId(nPrevId);
749 :
750 0 : return -1;
751 : }
752 :
753 : /**********************************************************************
754 : * TABView::GetFeatureRef()
755 : *
756 : * Fill and return a TABFeature object for the specified feature id.
757 : *
758 : * The returned pointer is a reference to an object owned and maintained
759 : * by this TABView object. It should not be altered or freed by the
760 : * caller and its contents is guaranteed to be valid only until the next
761 : * call to GetFeatureRef() or Close().
762 : *
763 : * Returns NULL if the specified feature id does not exist of if an
764 : * error happened. In any case, CPLError() will have been called to
765 : * report the reason of the failure.
766 : **********************************************************************/
767 2 : TABFeature *TABView::GetFeatureRef(GIntBig nFeatureId)
768 : {
769 :
770 : /*-----------------------------------------------------------------
771 : * Make sure file is open.
772 : *----------------------------------------------------------------*/
773 2 : if (m_poRelation == nullptr)
774 : {
775 0 : CPLError(CE_Failure, CPLE_IllegalArg,
776 : "GetFeatureRef() failed: file is not opened!");
777 0 : return nullptr;
778 : }
779 :
780 2 : if (!CPL_INT64_FITS_ON_INT32(nFeatureId))
781 0 : return nullptr;
782 :
783 2 : if (m_poCurFeature)
784 : {
785 0 : delete m_poCurFeature;
786 0 : m_poCurFeature = nullptr;
787 : }
788 :
789 2 : m_poCurFeature = m_poRelation->GetFeature(static_cast<int>(nFeatureId));
790 2 : m_nCurFeatureId = nFeatureId;
791 2 : if (m_poCurFeature)
792 : {
793 2 : m_poCurFeature->SetFID(m_nCurFeatureId);
794 : }
795 2 : return m_poCurFeature;
796 : }
797 :
798 : /**********************************************************************
799 : * TABView::CreateFeature()
800 : *
801 : * Write a new feature to this dataset. The passed in feature is updated
802 : * with the new feature id.
803 : *
804 : * Returns OGRERR_NONE on success, or an appropriate OGRERR_ code if an
805 : * error happened in which case, CPLError() will have been called to
806 : * report the reason of the failure.
807 : **********************************************************************/
808 0 : OGRErr TABView::CreateFeature(TABFeature *poFeature)
809 : {
810 0 : if (m_eAccessMode != TABWrite)
811 : {
812 0 : CPLError(CE_Failure, CPLE_NotSupported,
813 : "CreateFeature() can be used only with Write access.");
814 0 : return OGRERR_UNSUPPORTED_OPERATION;
815 : }
816 :
817 0 : if (m_poRelation == nullptr)
818 : {
819 0 : CPLError(CE_Failure, CPLE_IllegalArg,
820 : "CreateFeature() failed: file is not opened!");
821 0 : return OGRERR_FAILURE;
822 : }
823 :
824 : /*-----------------------------------------------------------------
825 : * If we're about to write the first feature, then we must finish
826 : * the initialization of the view first by creating the MI_refnum fields
827 : *----------------------------------------------------------------*/
828 0 : if (!m_bRelFieldsCreated)
829 : {
830 0 : if (m_poRelation->CreateRelFields() != 0)
831 0 : return OGRERR_FAILURE;
832 0 : m_bRelFieldsCreated = TRUE;
833 : }
834 :
835 0 : int nFeatureId = m_poRelation->WriteFeature(poFeature);
836 0 : if (nFeatureId < 0)
837 0 : return OGRERR_FAILURE;
838 :
839 0 : poFeature->SetFID(nFeatureId);
840 :
841 0 : return OGRERR_NONE;
842 : }
843 :
844 : /**********************************************************************
845 : * TABView::GetLayerDefn()
846 : *
847 : * Returns a reference to the OGRFeatureDefn that will be used to create
848 : * features in this dataset.
849 : *
850 : * Returns a reference to an object that is maintained by this TABView
851 : * object (and thus should not be modified or freed by the caller) or
852 : * NULL if the OGRFeatureDefn has not been initialized yet (i.e. no file
853 : * opened yet)
854 : **********************************************************************/
855 4 : OGRFeatureDefn *TABView::GetLayerDefn()
856 : {
857 4 : if (m_poRelation)
858 4 : return m_poRelation->GetFeatureDefn();
859 :
860 0 : return nullptr;
861 : }
862 :
863 : /**********************************************************************
864 : * TABView::SetFeatureDefn()
865 : *
866 : * Set the FeatureDefn for this dataset.
867 : *
868 : * For now, fields passed through SetFeatureDefn will not be mapped
869 : * properly, so this function can be used only with an empty feature defn.
870 : **********************************************************************/
871 0 : int TABView::SetFeatureDefn(
872 : OGRFeatureDefn *poFeatureDefn,
873 : CPL_UNUSED TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */)
874 : {
875 0 : if (m_poRelation)
876 0 : return m_poRelation->SetFeatureDefn(poFeatureDefn);
877 :
878 0 : return -1;
879 : }
880 :
881 : /**********************************************************************
882 : * TABView::GetNativeFieldType()
883 : *
884 : * Returns the native MapInfo field type for the specified field.
885 : *
886 : * Returns TABFUnknown if file is not opened, or if specified field index is
887 : * invalid.
888 : *
889 : * Note that field ids are positive and start at 0.
890 : **********************************************************************/
891 0 : TABFieldType TABView::GetNativeFieldType(int nFieldId)
892 : {
893 0 : if (m_poRelation)
894 0 : return m_poRelation->GetNativeFieldType(nFieldId);
895 :
896 0 : return TABFUnknown;
897 : }
898 :
899 : /**********************************************************************
900 : * TABView::AddFieldNative()
901 : *
902 : * Create a new field using a native mapinfo data type... this is an
903 : * alternative to defining fields through the OGR interface.
904 : * This function should be called after creating a new dataset, but before
905 : * writing the first feature.
906 : *
907 : * This function will build/update the OGRFeatureDefn that will have to be
908 : * used when writing features to this dataset.
909 : *
910 : * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn().
911 : *
912 : * Returns 0 on success, -1 on error.
913 : **********************************************************************/
914 0 : int TABView::AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
915 : int nWidth /*=0*/, int nPrecision /*=0*/,
916 : GBool bIndexed /*=FALSE*/, GBool bUnique /*=FALSE*/,
917 : int bApproxOK)
918 : {
919 0 : if (m_poRelation)
920 0 : return m_poRelation->AddFieldNative(pszName, eMapInfoType, nWidth,
921 : nPrecision, bIndexed, bUnique,
922 0 : bApproxOK);
923 :
924 0 : return -1;
925 : }
926 :
927 : /**********************************************************************
928 : * TABView::SetFieldIndexed()
929 : *
930 : * Request that a field be indexed. This will create the .IND file if
931 : * necessary, etc.
932 : *
933 : * Note that field ids are positive and start at 0.
934 : *
935 : * Returns 0 on success, -1 on error.
936 : **********************************************************************/
937 0 : int TABView::SetFieldIndexed(int nFieldId)
938 : {
939 0 : if (m_poRelation)
940 0 : return m_poRelation->SetFieldIndexed(nFieldId);
941 :
942 0 : return -1;
943 : }
944 :
945 0 : int TABView::SetCharset(const char *pszCharset)
946 : {
947 0 : if (0 != IMapInfoFile::SetCharset(pszCharset))
948 : {
949 0 : return -1;
950 : }
951 :
952 0 : for (int i = 0; i != m_numTABFiles; ++i)
953 : {
954 0 : m_papoTABFiles[i]->SetCharset(pszCharset);
955 : }
956 :
957 0 : return 0;
958 : }
959 :
960 : /**********************************************************************
961 : * TABView::IsFieldIndexed()
962 : *
963 : * Returns TRUE if field is indexed, or FALSE otherwise.
964 : **********************************************************************/
965 0 : GBool TABView::IsFieldIndexed(int nFieldId)
966 : {
967 0 : if (m_poRelation)
968 0 : return m_poRelation->IsFieldIndexed(nFieldId);
969 :
970 0 : return FALSE;
971 : }
972 :
973 : /**********************************************************************
974 : * TABView::IsFieldUnique()
975 : *
976 : * Returns TRUE if field is in the Unique table, or FALSE otherwise.
977 : **********************************************************************/
978 0 : GBool TABView::IsFieldUnique(int nFieldId)
979 : {
980 0 : if (m_poRelation)
981 0 : return m_poRelation->IsFieldUnique(nFieldId);
982 :
983 0 : return FALSE;
984 : }
985 :
986 : /**********************************************************************
987 : * TABView::GetBounds()
988 : *
989 : * Fetch projection coordinates bounds of a dataset.
990 : *
991 : * The bForce flag has no effect on TAB files since the bounds are
992 : * always in the header.
993 : *
994 : * Returns 0 on success, -1 on error.
995 : **********************************************************************/
996 0 : int TABView::GetBounds(double &dXMin, double &dYMin, double &dXMax,
997 : double &dYMax, GBool bForce /*= TRUE*/)
998 : {
999 0 : if (m_nMainTableIndex == -1)
1000 : {
1001 0 : CPLError(
1002 : CE_Failure, CPLE_AppDefined,
1003 : "GetBounds() can be called only after dataset has been opened.");
1004 0 : return -1;
1005 : }
1006 :
1007 0 : return m_papoTABFiles[m_nMainTableIndex]->GetBounds(dXMin, dYMin, dXMax,
1008 0 : dYMax, bForce);
1009 : }
1010 :
1011 : /**********************************************************************
1012 : * TABView::IGetExtent()
1013 : *
1014 : * Fetch extent of the data currently stored in the dataset.
1015 : *
1016 : * The bForce flag has no effect on TAB files since that value is
1017 : * always in the header.
1018 : *
1019 : * Returns OGRERR_NONE/OGRRERR_FAILURE.
1020 : **********************************************************************/
1021 0 : OGRErr TABView::IGetExtent(int iGeomField, OGREnvelope *psExtent, bool bForce)
1022 : {
1023 0 : if (m_nMainTableIndex == -1)
1024 : {
1025 0 : CPLError(
1026 : CE_Failure, CPLE_AppDefined,
1027 : "GetExtent() can be called only after dataset has been opened.");
1028 0 : return OGRERR_FAILURE;
1029 : }
1030 :
1031 0 : return m_papoTABFiles[m_nMainTableIndex]->GetExtent(iGeomField, psExtent,
1032 0 : bForce);
1033 : }
1034 :
1035 : /**********************************************************************
1036 : * TABView::GetFeatureCountByType()
1037 : *
1038 : * Return number of features of each type.
1039 : *
1040 : * Note that the sum of the 4 returned values may be different from
1041 : * the total number of features since features with NONE geometry
1042 : * are not taken into account here.
1043 : *
1044 : * Note: the bForce flag has nmo effect on .TAB files since the info
1045 : * is always in the header.
1046 : *
1047 : * Returns 0 on success, or silently returns -1 (with no error) if this
1048 : * information is not available.
1049 : **********************************************************************/
1050 0 : int TABView::GetFeatureCountByType(int &numPoints, int &numLines,
1051 : int &numRegions, int &numTexts,
1052 : GBool bForce /*= TRUE*/)
1053 : {
1054 0 : if (m_nMainTableIndex == -1)
1055 0 : return -1;
1056 :
1057 0 : return m_papoTABFiles[m_nMainTableIndex]->GetFeatureCountByType(
1058 0 : numPoints, numLines, numRegions, numTexts, bForce);
1059 : }
1060 :
1061 : /**********************************************************************
1062 : * TABView::GetSpatialRef()
1063 : *
1064 : * Returns a reference to an OGRSpatialReference for this dataset.
1065 : * If the projection parameters have not been parsed yet, then we will
1066 : * parse them before returning.
1067 : *
1068 : * The returned object is owned and maintained by this TABFile and
1069 : * should not be modified or freed by the caller.
1070 : *
1071 : * Returns NULL if the SpatialRef cannot be accessed.
1072 : **********************************************************************/
1073 2 : OGRSpatialReference *TABView::GetSpatialRef()
1074 : {
1075 2 : if (m_nMainTableIndex == -1)
1076 : {
1077 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1078 : "GetSpatialRef() failed: file has not been opened yet.");
1079 0 : return nullptr;
1080 : }
1081 :
1082 2 : return m_papoTABFiles[m_nMainTableIndex]->GetSpatialRef();
1083 : }
1084 :
1085 : /**********************************************************************
1086 : * TABView::SetSpatialRef()
1087 : **********************************************************************/
1088 0 : int TABView::SetSpatialRef(OGRSpatialReference *poSpatialRef)
1089 : {
1090 0 : if (m_nMainTableIndex == -1)
1091 : {
1092 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1093 : "SetSpatialRef() failed: file has not been opened yet.");
1094 0 : return -1;
1095 : }
1096 :
1097 0 : return m_papoTABFiles[m_nMainTableIndex]->SetSpatialRef(poSpatialRef);
1098 : }
1099 :
1100 : /**********************************************************************
1101 : * TABView::SetBounds()
1102 : **********************************************************************/
1103 0 : int TABView::SetBounds(double dXMin, double dYMin, double dXMax, double dYMax)
1104 : {
1105 0 : if (m_nMainTableIndex == -1)
1106 : {
1107 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1108 : "SetBounds() failed: file has not been opened yet.");
1109 0 : return -1;
1110 : }
1111 :
1112 0 : return m_papoTABFiles[m_nMainTableIndex]->SetBounds(dXMin, dYMin, dXMax,
1113 0 : dYMax);
1114 : }
1115 :
1116 : /************************************************************************/
1117 : /* TestCapability() */
1118 : /************************************************************************/
1119 :
1120 0 : int TABView::TestCapability(const char *pszCap)
1121 :
1122 : {
1123 0 : if (EQUAL(pszCap, OLCRandomRead))
1124 0 : return TRUE;
1125 :
1126 0 : else if (EQUAL(pszCap, OLCSequentialWrite))
1127 0 : return TRUE;
1128 :
1129 0 : else if (EQUAL(pszCap, OLCRandomWrite))
1130 0 : return FALSE;
1131 :
1132 0 : else if (EQUAL(pszCap, OLCFastFeatureCount))
1133 0 : return m_poFilterGeom == nullptr;
1134 :
1135 0 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
1136 0 : return FALSE;
1137 :
1138 0 : else if (EQUAL(pszCap, OLCFastGetExtent))
1139 0 : return TRUE;
1140 :
1141 0 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
1142 0 : return TestUtf8Capability();
1143 :
1144 : else
1145 0 : return FALSE;
1146 : }
1147 :
1148 : /**********************************************************************
1149 : * TABView::Dump()
1150 : *
1151 : * Dump block contents... available only in DEBUG mode.
1152 : **********************************************************************/
1153 : #ifdef DEBUG
1154 :
1155 0 : void TABView::Dump(FILE *fpOut /*=NULL*/)
1156 : {
1157 0 : if (fpOut == nullptr)
1158 0 : fpOut = stdout;
1159 :
1160 0 : fprintf(fpOut, "----- TABView::Dump() -----\n");
1161 :
1162 0 : if (m_numTABFiles > 0)
1163 : {
1164 0 : fprintf(fpOut, "File is not opened.\n");
1165 : }
1166 : else
1167 : {
1168 0 : fprintf(fpOut, "File is opened: %s\n", m_pszFname);
1169 0 : fprintf(fpOut, "View contains %d tables\n", m_numTABFiles);
1170 : }
1171 :
1172 0 : fflush(fpOut);
1173 0 : }
1174 :
1175 : #endif // DEBUG
1176 :
1177 : /*=====================================================================
1178 : * class TABRelation
1179 : *====================================================================*/
1180 :
1181 : /**********************************************************************
1182 : * TABRelation::TABRelation()
1183 : *
1184 : * Constructor.
1185 : **********************************************************************/
1186 2 : TABRelation::TABRelation()
1187 : : m_poMainTable(nullptr), m_pszMainFieldName(nullptr), m_nMainFieldNo(-1),
1188 : m_poRelTable(nullptr), m_pszRelFieldName(nullptr), m_nRelFieldNo(-1),
1189 : m_poRelINDFileRef(nullptr), m_nRelFieldIndexNo(-1), m_nUniqueRecordNo(0),
1190 : m_panMainTableFieldMap(nullptr), m_panRelTableFieldMap(nullptr),
1191 2 : m_poDefn(nullptr)
1192 : {
1193 2 : }
1194 :
1195 : /**********************************************************************
1196 : * TABRelation::~TABRelation()
1197 : *
1198 : * Destructor.
1199 : **********************************************************************/
1200 4 : TABRelation::~TABRelation()
1201 : {
1202 2 : ResetAllMembers();
1203 2 : }
1204 :
1205 : /**********************************************************************
1206 : * TABRelation::ResetAllMembers()
1207 : *
1208 : * Reset all class members.
1209 : **********************************************************************/
1210 4 : void TABRelation::ResetAllMembers()
1211 : {
1212 4 : m_poMainTable = nullptr;
1213 4 : CPLFree(m_pszMainFieldName);
1214 4 : m_pszMainFieldName = nullptr;
1215 4 : m_nMainFieldNo = -1;
1216 :
1217 4 : m_poRelTable = nullptr;
1218 4 : CPLFree(m_pszRelFieldName);
1219 4 : m_pszRelFieldName = nullptr;
1220 4 : m_nRelFieldNo = -1;
1221 4 : m_nRelFieldIndexNo = -1;
1222 :
1223 4 : m_nUniqueRecordNo = 0;
1224 :
1225 : // No need to close m_poRelINDFileRef since we only got a ref. to it
1226 4 : m_poRelINDFileRef = nullptr;
1227 :
1228 4 : CPLFree(m_panMainTableFieldMap);
1229 4 : m_panMainTableFieldMap = nullptr;
1230 4 : CPLFree(m_panRelTableFieldMap);
1231 4 : m_panRelTableFieldMap = nullptr;
1232 :
1233 : /*-----------------------------------------------------------------
1234 : * Note: we have to check the reference count before deleting m_poDefn
1235 : *----------------------------------------------------------------*/
1236 4 : if (m_poDefn && m_poDefn->Dereference() == 0)
1237 0 : delete m_poDefn;
1238 4 : m_poDefn = nullptr;
1239 4 : }
1240 :
1241 : /**********************************************************************
1242 : * TABRelation::Init()
1243 : *
1244 : * Set the details of the relation: the main and related tables, the fields
1245 : * through which they will be connected, and the list of fields to select.
1246 : * After this call, we are ready to read data records.
1247 : *
1248 : * For write access, Init() is called with pszMain/RelFieldName and
1249 : * **papszSelectedFields passed as NULL. They will have to be set through
1250 : * other methods before a first feature can be written.
1251 : *
1252 : * A new OGRFeatureDefn is also built for the combined tables.
1253 : *
1254 : * Returns 0 on success, or -1 or error.
1255 : **********************************************************************/
1256 2 : int TABRelation::Init(const char *pszViewName, TABFile *poMainTable,
1257 : TABFile *poRelTable, const char *pszMainFieldName,
1258 : const char *pszRelFieldName, char **papszSelectedFields)
1259 : {
1260 2 : if (poMainTable == nullptr || poRelTable == nullptr)
1261 0 : return -1;
1262 :
1263 : // We'll need the feature Defn later...
1264 2 : OGRFeatureDefn *poMainDefn = poMainTable->GetLayerDefn();
1265 2 : OGRFeatureDefn *poRelDefn = poRelTable->GetLayerDefn();
1266 :
1267 : /*-----------------------------------------------------------------
1268 : * Keep info for later use about source tables, etc.
1269 : *----------------------------------------------------------------*/
1270 2 : ResetAllMembers();
1271 :
1272 2 : m_poMainTable = poMainTable;
1273 2 : if (pszMainFieldName)
1274 : {
1275 2 : m_pszMainFieldName = CPLStrdup(pszMainFieldName);
1276 2 : m_nMainFieldNo = poMainDefn->GetFieldIndex(pszMainFieldName);
1277 : }
1278 :
1279 2 : m_poRelTable = poRelTable;
1280 2 : if (pszRelFieldName)
1281 : {
1282 2 : m_pszRelFieldName = CPLStrdup(pszRelFieldName);
1283 2 : m_nRelFieldNo = poRelDefn->GetFieldIndex(pszRelFieldName);
1284 2 : m_nRelFieldIndexNo = poRelTable->GetFieldIndexNumber(m_nRelFieldNo);
1285 2 : m_poRelINDFileRef = poRelTable->GetINDFileRef();
1286 :
1287 2 : if (m_nRelFieldIndexNo >= 0 && m_poRelINDFileRef == nullptr)
1288 : {
1289 0 : CPLError(CE_Failure, CPLE_FileIO,
1290 : "Field %s is indexed but the .IND file is missing.",
1291 : pszRelFieldName);
1292 0 : return -1;
1293 : }
1294 : }
1295 :
1296 : /*-----------------------------------------------------------------
1297 : * Init field maps. For each field in each table, a -1 means that
1298 : * the field is not selected, and a value >=0 is the index of the
1299 : * field in the view's FeatureDefn
1300 : *----------------------------------------------------------------*/
1301 2 : const int numFields1 = poMainDefn ? poMainDefn->GetFieldCount() : 0;
1302 2 : const int numFields2 = poRelDefn ? poRelDefn->GetFieldCount() : 0;
1303 :
1304 2 : m_panMainTableFieldMap =
1305 2 : static_cast<int *>(CPLMalloc((numFields1 + 1) * sizeof(int)));
1306 6 : for (int i = 0; i < numFields1; i++)
1307 4 : m_panMainTableFieldMap[i] = -1;
1308 2 : m_panRelTableFieldMap =
1309 2 : static_cast<int *>(CPLMalloc((numFields2 + 1) * sizeof(int)));
1310 6 : for (int i = 0; i < numFields2; i++)
1311 4 : m_panRelTableFieldMap[i] = -1;
1312 :
1313 : /*-----------------------------------------------------------------
1314 : * If selectedFields = "*" then select all fields from both tables
1315 : *----------------------------------------------------------------*/
1316 2 : papszSelectedFields = CSLDuplicate(papszSelectedFields);
1317 2 : if (papszSelectedFields != nullptr && papszSelectedFields[0] != nullptr &&
1318 2 : papszSelectedFields[1] == nullptr && EQUAL(papszSelectedFields[0], "*"))
1319 : {
1320 1 : CSLDestroy(papszSelectedFields);
1321 1 : papszSelectedFields = nullptr;
1322 :
1323 3 : for (int i = 0; i < numFields1; i++)
1324 : {
1325 2 : OGRFieldDefn *poFieldDefn = poMainDefn->GetFieldDefn(i);
1326 :
1327 : papszSelectedFields =
1328 2 : CSLAddString(papszSelectedFields, poFieldDefn->GetNameRef());
1329 : }
1330 :
1331 3 : for (int i = 0; i < numFields2; i++)
1332 : {
1333 2 : OGRFieldDefn *poFieldDefn = poRelDefn->GetFieldDefn(i);
1334 :
1335 2 : if (CSLFindString(papszSelectedFields, poFieldDefn->GetNameRef()) !=
1336 : -1)
1337 1 : continue; // Avoid duplicate field name in view
1338 :
1339 : papszSelectedFields =
1340 1 : CSLAddString(papszSelectedFields, poFieldDefn->GetNameRef());
1341 : }
1342 : }
1343 :
1344 : /*-----------------------------------------------------------------
1345 : * Create new FeatureDefn and copy selected fields definitions
1346 : * while updating the appropriate field maps.
1347 : *----------------------------------------------------------------*/
1348 2 : OGRFieldDefn *poFieldDefn = nullptr;
1349 :
1350 2 : m_poDefn = new OGRFeatureDefn(pszViewName);
1351 : // Ref count defaults to 0... set it to 1
1352 2 : m_poDefn->Reference();
1353 :
1354 7 : for (int i = 0;
1355 7 : papszSelectedFields != nullptr && papszSelectedFields[i] != nullptr;
1356 : i++)
1357 : {
1358 : int nIndex;
1359 10 : if (poMainDefn &&
1360 5 : (nIndex = poMainDefn->GetFieldIndex(papszSelectedFields[i])) >= 0)
1361 : {
1362 : /* Field from the main table
1363 : */
1364 3 : poFieldDefn = poMainDefn->GetFieldDefn(nIndex);
1365 3 : m_poDefn->AddFieldDefn(poFieldDefn);
1366 3 : m_panMainTableFieldMap[nIndex] = m_poDefn->GetFieldCount() - 1;
1367 : }
1368 4 : else if (poRelDefn && (nIndex = poRelDefn->GetFieldIndex(
1369 2 : papszSelectedFields[i])) >= 0)
1370 : {
1371 : /* Field from the related table
1372 : */
1373 2 : poFieldDefn = poRelDefn->GetFieldDefn(nIndex);
1374 2 : m_poDefn->AddFieldDefn(poFieldDefn);
1375 2 : m_panRelTableFieldMap[nIndex] = m_poDefn->GetFieldCount() - 1;
1376 : }
1377 : else
1378 : {
1379 : // Hummm... field does not exist... likely an unsupported feature!
1380 : // At least send a warning and ignore the field.
1381 0 : CPLError(CE_Warning, CPLE_IllegalArg,
1382 : "Selected Field %s not found in source tables %s and %s",
1383 0 : papszSelectedFields[i],
1384 0 : poMainDefn ? poMainDefn->GetName() : "(null)",
1385 0 : poRelDefn ? poRelDefn->GetName() : "(null)");
1386 : }
1387 : }
1388 2 : CSLDestroy(papszSelectedFields);
1389 2 : return 0;
1390 : }
1391 :
1392 : /**********************************************************************
1393 : * TABRelation::CreateRelFields()
1394 : *
1395 : * For write access, create the integer fields in each table that will
1396 : * link them, and setup everything to be ready to write the first feature.
1397 : *
1398 : * This function should be called just before writing the first feature.
1399 : *
1400 : * Returns 0 on success, or -1 or error.
1401 : **********************************************************************/
1402 0 : int TABRelation::CreateRelFields()
1403 : {
1404 : /*-----------------------------------------------------------------
1405 : * Create the field in each table.
1406 : * The default name is "MI_refnum" but if a field with the same name
1407 : * already exists then we'll try to generate a unique name.
1408 : *----------------------------------------------------------------*/
1409 0 : m_pszMainFieldName = CPLStrdup("MI_Refnum ");
1410 0 : const size_t nLen = strlen(m_pszMainFieldName) + 1;
1411 0 : strcpy(m_pszMainFieldName, "MI_Refnum");
1412 0 : int i = 1;
1413 0 : while (m_poDefn->GetFieldIndex(m_pszMainFieldName) >= 0)
1414 : {
1415 0 : snprintf(m_pszMainFieldName, nLen, "MI_Refnum_%d", i++);
1416 : }
1417 0 : m_pszRelFieldName = CPLStrdup(m_pszMainFieldName);
1418 :
1419 0 : m_nMainFieldNo = m_nRelFieldNo = -1;
1420 0 : if (m_poMainTable->AddFieldNative(m_pszMainFieldName, TABFInteger, 0, 0) ==
1421 : 0)
1422 0 : m_nMainFieldNo = m_poMainTable->GetLayerDefn()->GetFieldCount() - 1;
1423 :
1424 0 : if (m_poRelTable->AddFieldNative(m_pszRelFieldName, TABFInteger, 0, 0) == 0)
1425 0 : m_nRelFieldNo = m_poRelTable->GetLayerDefn()->GetFieldCount() - 1;
1426 :
1427 0 : if (m_nMainFieldNo == -1 || m_nRelFieldNo == -1)
1428 0 : return -1;
1429 :
1430 0 : if (m_poMainTable->SetFieldIndexed(m_nMainFieldNo) == -1)
1431 0 : return -1;
1432 :
1433 0 : if ((m_nRelFieldIndexNo = m_poRelTable->SetFieldIndexed(m_nRelFieldNo)) ==
1434 : -1)
1435 0 : return -1;
1436 :
1437 0 : m_poRelINDFileRef = m_poRelTable->GetINDFileRef();
1438 :
1439 : /*-----------------------------------------------------------------
1440 : * Update field maps
1441 : *----------------------------------------------------------------*/
1442 0 : OGRFeatureDefn *poMainDefn = m_poMainTable->GetLayerDefn();
1443 0 : OGRFeatureDefn *poRelDefn = m_poRelTable->GetLayerDefn();
1444 :
1445 0 : m_panMainTableFieldMap = static_cast<int *>(CPLRealloc(
1446 0 : m_panMainTableFieldMap, poMainDefn->GetFieldCount() * sizeof(int)));
1447 0 : m_panMainTableFieldMap[poMainDefn->GetFieldCount() - 1] = -1;
1448 :
1449 0 : m_panRelTableFieldMap = static_cast<int *>(CPLRealloc(
1450 0 : m_panRelTableFieldMap, poRelDefn->GetFieldCount() * sizeof(int)));
1451 0 : m_panRelTableFieldMap[poRelDefn->GetFieldCount() - 1] = -1;
1452 :
1453 : /*-----------------------------------------------------------------
1454 : * Make sure the first unique field (in poRelTable) is indexed since
1455 : * it is the one against which we will try to match records.
1456 : *----------------------------------------------------------------*/
1457 0 : if (m_poRelTable->SetFieldIndexed(0) == -1)
1458 0 : return -1;
1459 :
1460 0 : return 0;
1461 : }
1462 :
1463 : /**********************************************************************
1464 : * TABRelation::GetFeature()
1465 : *
1466 : * Fill and return a TABFeature object for the specified feature id.
1467 : *
1468 : * The returned pointer is a new TABFeature that will have to be freed
1469 : * by the caller.
1470 : *
1471 : * Returns NULL if the specified feature id does not exist of if an
1472 : * error happened. In any case, CPLError() will have been called to
1473 : * report the reason of the failure.
1474 : *
1475 : * __TODO__ The current implementation fetches the features from each table
1476 : * and creates a 3rd feature to merge them. There would be room for
1477 : * optimization, at least by avoiding the duplication of the geometry
1478 : * which can be big sometimes... but this would imply changes at the
1479 : * lower-level in the lib. and we won't go there yet.
1480 : **********************************************************************/
1481 2 : TABFeature *TABRelation::GetFeature(int nFeatureId)
1482 : {
1483 : /*-----------------------------------------------------------------
1484 : * Make sure init() has been called
1485 : *----------------------------------------------------------------*/
1486 2 : if (m_poMainTable == nullptr || m_poRelTable == nullptr)
1487 : {
1488 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1489 : "GetFeatureRef() failed: object not initialized yet!");
1490 0 : return nullptr;
1491 : }
1492 :
1493 : /*-----------------------------------------------------------------
1494 : * Read main feature and create a new one of the right type
1495 : *----------------------------------------------------------------*/
1496 2 : TABFeature *poMainFeature = m_poMainTable->GetFeatureRef(nFeatureId);
1497 2 : if (poMainFeature == nullptr)
1498 : {
1499 : // Feature cannot be read from main table...
1500 : // an error has already been reported.
1501 0 : return nullptr;
1502 : }
1503 :
1504 2 : TABFeature *poCurFeature = poMainFeature->CloneTABFeature(m_poDefn);
1505 :
1506 : /*-----------------------------------------------------------------
1507 : * Keep track of FID and copy the geometry
1508 : *----------------------------------------------------------------*/
1509 2 : poCurFeature->SetFID(nFeatureId);
1510 :
1511 2 : if (poCurFeature->GetFeatureClass() != TABFCNoGeomFeature)
1512 : {
1513 2 : OGRGeometry *poGeom = poMainFeature->GetGeometryRef();
1514 2 : poCurFeature->SetGeometry(poGeom);
1515 : }
1516 :
1517 : /*-----------------------------------------------------------------
1518 : * Fetch feature from related table
1519 : *
1520 : * __TODO__ Right now we support only many-to-1 relationships, but
1521 : * it might be possible to have several related entries
1522 : * for a single key, and in this case we should return
1523 : * one new feature for each of them.
1524 : *----------------------------------------------------------------*/
1525 2 : TABFeature *poRelFeature = nullptr;
1526 2 : if (m_poRelINDFileRef)
1527 : {
1528 : GByte *pKey =
1529 2 : BuildFieldKey(poMainFeature, m_nMainFieldNo,
1530 2 : m_poMainTable->GetNativeFieldType(m_nMainFieldNo),
1531 : m_nRelFieldIndexNo);
1532 : int nRelFeatureId =
1533 2 : m_poRelINDFileRef->FindFirst(m_nRelFieldIndexNo, pKey);
1534 :
1535 2 : if (nRelFeatureId > 0)
1536 2 : poRelFeature = m_poRelTable->GetFeatureRef(nRelFeatureId);
1537 : }
1538 :
1539 : /*-----------------------------------------------------------------
1540 : * Copy fields from poMainFeature
1541 : *----------------------------------------------------------------*/
1542 6 : for (int i = 0; i < poMainFeature->GetFieldCount(); i++)
1543 : {
1544 4 : if (m_panMainTableFieldMap[i] != -1)
1545 : {
1546 3 : poCurFeature->SetField(m_panMainTableFieldMap[i],
1547 3 : poMainFeature->GetRawFieldRef(i));
1548 : }
1549 : }
1550 :
1551 : /*-----------------------------------------------------------------
1552 : * Copy fields from poRelFeature...
1553 : *
1554 : * NOTE: For now, if no corresponding feature is found in RelTable
1555 : * then we will just leave the corresponding fields unset.
1556 : *----------------------------------------------------------------*/
1557 6 : for (int i = 0; poRelFeature && i < poRelFeature->GetFieldCount(); i++)
1558 : {
1559 4 : if (m_panRelTableFieldMap[i] != -1)
1560 : {
1561 2 : poCurFeature->SetField(m_panRelTableFieldMap[i],
1562 2 : poRelFeature->GetRawFieldRef(i));
1563 : }
1564 : }
1565 :
1566 2 : return poCurFeature;
1567 : }
1568 :
1569 : /**********************************************************************
1570 : * TABRelation::BuildFieldKey()
1571 : *
1572 : * Return the index key for the specified field in poFeature.
1573 : * Simply maps the call to the proper method in the TABINDFile class.
1574 : *
1575 : * Returns a reference to a TABINDFile internal buffer that should not
1576 : * be freed by the caller.
1577 : **********************************************************************/
1578 2 : GByte *TABRelation::BuildFieldKey(TABFeature *poFeature, int nFieldNo,
1579 : TABFieldType eType, int nIndexNo)
1580 : {
1581 2 : GByte *pKey = nullptr;
1582 :
1583 2 : switch (eType)
1584 : {
1585 0 : case TABFChar:
1586 0 : pKey = m_poRelINDFileRef->BuildKey(
1587 : nIndexNo, poFeature->GetFieldAsString(nFieldNo));
1588 0 : break;
1589 :
1590 0 : case TABFDecimal:
1591 : case TABFFloat:
1592 0 : pKey = m_poRelINDFileRef->BuildKey(
1593 : nIndexNo, poFeature->GetFieldAsDouble(nFieldNo));
1594 0 : break;
1595 :
1596 : // __TODO__ DateTime fields are 8 bytes long, not supported yet by
1597 : // the indexing code (see bug #1844).
1598 0 : case TABFDateTime:
1599 0 : CPLError(
1600 : CE_Failure, CPLE_NotSupported,
1601 : "TABRelation on field of type DateTime not supported yet.");
1602 0 : break;
1603 :
1604 2 : case TABFInteger:
1605 : case TABFSmallInt:
1606 : case TABFDate:
1607 : case TABFTime:
1608 : case TABFLogical:
1609 : default:
1610 2 : pKey = m_poRelINDFileRef->BuildKey(
1611 : nIndexNo, poFeature->GetFieldAsInteger(nFieldNo));
1612 2 : break;
1613 : }
1614 :
1615 2 : return pKey;
1616 : }
1617 :
1618 : /**********************************************************************
1619 : * TABRelation::GetNativeFieldType()
1620 : *
1621 : * Returns the native MapInfo field type for the specified field.
1622 : *
1623 : * Returns TABFUnknown if file is not opened, or if specified field index is
1624 : * invalid.
1625 : *
1626 : * Note that field ids are positive and start at 0.
1627 : **********************************************************************/
1628 0 : TABFieldType TABRelation::GetNativeFieldType(int nFieldId)
1629 : {
1630 : int i, numFields;
1631 :
1632 0 : if (m_poMainTable == nullptr || m_poRelTable == nullptr ||
1633 0 : m_panMainTableFieldMap == nullptr || m_panRelTableFieldMap == nullptr)
1634 0 : return TABFUnknown;
1635 :
1636 : /*-----------------------------------------------------------------
1637 : * Look for nFieldId in the field maps and call the corresponding
1638 : * TAB file's GetNativeFieldType()
1639 : *----------------------------------------------------------------*/
1640 0 : numFields = m_poMainTable->GetLayerDefn()->GetFieldCount();
1641 0 : for (i = 0; i < numFields; i++)
1642 : {
1643 0 : if (m_panMainTableFieldMap[i] == nFieldId)
1644 : {
1645 0 : return m_poMainTable->GetNativeFieldType(i);
1646 : }
1647 : }
1648 :
1649 0 : numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
1650 0 : for (i = 0; i < numFields; i++)
1651 : {
1652 0 : if (m_panRelTableFieldMap[i] == nFieldId)
1653 : {
1654 0 : return m_poRelTable->GetNativeFieldType(i);
1655 : }
1656 : }
1657 :
1658 0 : return TABFUnknown;
1659 : }
1660 :
1661 : /**********************************************************************
1662 : * TABRelation::AddFieldNative()
1663 : *
1664 : * Create a new field using a native mapinfo data type... this is an
1665 : * alternative to defining fields through the OGR interface.
1666 : * This function should be called after creating a new dataset, but before
1667 : * writing the first feature.
1668 : *
1669 : * This function will build/update the OGRFeatureDefn that will have to be
1670 : * used when writing features to this dataset.
1671 : *
1672 : * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn().
1673 : *
1674 : * Returns 0 on success, -1 on error.
1675 : **********************************************************************/
1676 0 : int TABRelation::AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
1677 : int nWidth /*=0*/, int nPrecision /*=0*/,
1678 : GBool bIndexed /*=FALSE*/,
1679 : GBool bUnique /*=FALSE*/, int bApproxOK)
1680 : {
1681 0 : if (m_poMainTable == nullptr || m_poRelTable == nullptr ||
1682 0 : m_panMainTableFieldMap == nullptr || m_panRelTableFieldMap == nullptr)
1683 0 : return -1;
1684 :
1685 0 : if (!bUnique)
1686 : {
1687 : /*-------------------------------------------------------------
1688 : * Add field to poMainTable and to m_poDefn
1689 : *------------------------------------------------------------*/
1690 0 : if (m_poMainTable->AddFieldNative(pszName, eMapInfoType, nWidth,
1691 : nPrecision, bIndexed, bUnique,
1692 0 : bApproxOK) != 0)
1693 0 : return -1;
1694 :
1695 0 : OGRFeatureDefn *poMainDefn = m_poMainTable->GetLayerDefn();
1696 :
1697 0 : m_panMainTableFieldMap = static_cast<int *>(CPLRealloc(
1698 0 : m_panMainTableFieldMap, poMainDefn->GetFieldCount() * sizeof(int)));
1699 :
1700 0 : m_poDefn->AddFieldDefn(
1701 0 : poMainDefn->GetFieldDefn(poMainDefn->GetFieldCount() - 1));
1702 :
1703 0 : m_panMainTableFieldMap[poMainDefn->GetFieldCount() - 1] =
1704 0 : m_poDefn->GetFieldCount() - 1;
1705 : }
1706 : else
1707 : {
1708 : /*-------------------------------------------------------------
1709 : * Add field to poRelTable and to m_poDefn
1710 : *------------------------------------------------------------*/
1711 0 : if (m_poRelTable->AddFieldNative(pszName, eMapInfoType, nWidth,
1712 : nPrecision, bIndexed, bUnique,
1713 0 : bApproxOK) != 0)
1714 0 : return -1;
1715 :
1716 0 : OGRFeatureDefn *poRelDefn = m_poRelTable->GetLayerDefn();
1717 :
1718 0 : m_panRelTableFieldMap = static_cast<int *>(CPLRealloc(
1719 0 : m_panRelTableFieldMap, poRelDefn->GetFieldCount() * sizeof(int)));
1720 :
1721 0 : m_poDefn->AddFieldDefn(
1722 0 : poRelDefn->GetFieldDefn(poRelDefn->GetFieldCount() - 1));
1723 :
1724 0 : m_panRelTableFieldMap[poRelDefn->GetFieldCount() - 1] =
1725 0 : m_poDefn->GetFieldCount() - 1;
1726 :
1727 : // The first field in this table must be indexed.
1728 0 : if (poRelDefn->GetFieldCount() == 1)
1729 0 : m_poRelTable->SetFieldIndexed(0);
1730 : }
1731 :
1732 0 : return 0;
1733 : }
1734 :
1735 : /**********************************************************************
1736 : * TABRelation::IsFieldIndexed()
1737 : *
1738 : * Returns TRUE is specified field is indexed.
1739 : *
1740 : * Note that field ids are positive and start at 0.
1741 : **********************************************************************/
1742 0 : GBool TABRelation::IsFieldIndexed(int nFieldId)
1743 : {
1744 : int i, numFields;
1745 :
1746 0 : if (m_poMainTable == nullptr || m_poRelTable == nullptr ||
1747 0 : m_panMainTableFieldMap == nullptr || m_panRelTableFieldMap == nullptr)
1748 0 : return FALSE;
1749 :
1750 : /*-----------------------------------------------------------------
1751 : * Look for nFieldId in the field maps and call the corresponding
1752 : * TAB file's GetNativeFieldType()
1753 : *----------------------------------------------------------------*/
1754 0 : numFields = m_poMainTable->GetLayerDefn()->GetFieldCount();
1755 0 : for (i = 0; i < numFields; i++)
1756 : {
1757 0 : if (m_panMainTableFieldMap[i] == nFieldId)
1758 : {
1759 0 : return m_poMainTable->IsFieldIndexed(i);
1760 : }
1761 : }
1762 :
1763 0 : numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
1764 0 : for (i = 0; i < numFields; i++)
1765 : {
1766 0 : if (m_panRelTableFieldMap[i] == nFieldId)
1767 : {
1768 0 : return m_poRelTable->IsFieldIndexed(i);
1769 : }
1770 : }
1771 :
1772 0 : return FALSE;
1773 : }
1774 :
1775 : /**********************************************************************
1776 : * TABRelation::SetFieldIndexed()
1777 : *
1778 : * Request that the specified field be indexed. This will create the .IND
1779 : * file, etc.
1780 : *
1781 : * Note that field ids are positive and start at 0.
1782 : *
1783 : * Returns 0 on success, -1 on error.
1784 : **********************************************************************/
1785 0 : int TABRelation::SetFieldIndexed(int nFieldId)
1786 : {
1787 : int i, numFields;
1788 :
1789 0 : if (m_poMainTable == nullptr || m_poRelTable == nullptr ||
1790 0 : m_panMainTableFieldMap == nullptr || m_panRelTableFieldMap == nullptr)
1791 0 : return -1;
1792 :
1793 : /*-----------------------------------------------------------------
1794 : * Look for nFieldId in the field maps and call the corresponding
1795 : * TAB file's GetNativeFieldType()
1796 : *----------------------------------------------------------------*/
1797 0 : numFields = m_poMainTable->GetLayerDefn()->GetFieldCount();
1798 0 : for (i = 0; i < numFields; i++)
1799 : {
1800 0 : if (m_panMainTableFieldMap[i] == nFieldId)
1801 : {
1802 0 : return m_poMainTable->SetFieldIndexed(i);
1803 : }
1804 : }
1805 :
1806 0 : numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
1807 0 : for (i = 0; i < numFields; i++)
1808 : {
1809 0 : if (m_panRelTableFieldMap[i] == nFieldId)
1810 : {
1811 0 : return m_poRelTable->SetFieldIndexed(i);
1812 : }
1813 : }
1814 :
1815 0 : return -1;
1816 : }
1817 :
1818 : /**********************************************************************
1819 : * TABRelation::IsFieldUnique()
1820 : *
1821 : * Returns TRUE is specified field is part of the unique table (poRelTable).
1822 : *
1823 : * Note that field ids are positive and start at 0.
1824 : **********************************************************************/
1825 0 : GBool TABRelation::IsFieldUnique(int nFieldId)
1826 : {
1827 : int i, numFields;
1828 :
1829 0 : if (m_poMainTable == nullptr || m_poRelTable == nullptr ||
1830 0 : m_panMainTableFieldMap == nullptr || m_panRelTableFieldMap == nullptr)
1831 0 : return FALSE;
1832 :
1833 : /*-----------------------------------------------------------------
1834 : * Look for nFieldId in the poRelTable field map
1835 : *----------------------------------------------------------------*/
1836 0 : numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
1837 0 : for (i = 0; i < numFields; i++)
1838 : {
1839 0 : if (m_panRelTableFieldMap[i] == nFieldId)
1840 : {
1841 0 : return TRUE; // If it is here then it is unique!
1842 : }
1843 : }
1844 :
1845 0 : return FALSE;
1846 : }
1847 :
1848 : /**********************************************************************
1849 : * TABRelation::WriteFeature()
1850 : *
1851 : * Write a feature to this dataset.
1852 : *
1853 : * For now only sequential writes are supported (i.e. with nFeatureId=-1)
1854 : * but eventually we should be able to do random access by specifying
1855 : * a value through nFeatureId.
1856 : *
1857 : * Returns the new featureId (> 0) on success, or -1 if an
1858 : * error happened in which case, CPLError() will have been called to
1859 : * report the reason of the failure.
1860 : **********************************************************************/
1861 0 : int TABRelation::WriteFeature(TABFeature *poFeature, int nFeatureId /*=-1*/)
1862 : {
1863 0 : TABFeature *poMainFeature = nullptr;
1864 :
1865 0 : if (nFeatureId != -1)
1866 : {
1867 0 : CPLError(CE_Failure, CPLE_NotSupported,
1868 : "WriteFeature(): random access not implemented yet.");
1869 0 : return -1;
1870 : }
1871 :
1872 0 : CPLAssert(m_poMainTable && m_poRelTable);
1873 :
1874 : // We'll need the feature Defn later...
1875 0 : OGRFeatureDefn *poMainDefn = m_poMainTable->GetLayerDefn();
1876 0 : OGRFeatureDefn *poRelDefn = m_poRelTable->GetLayerDefn();
1877 :
1878 : /*-----------------------------------------------------------------
1879 : * Create one feature for each table
1880 : * Copy the geometry only to the feature from the main table
1881 : *----------------------------------------------------------------*/
1882 0 : poMainFeature = poFeature->CloneTABFeature(poMainDefn);
1883 :
1884 0 : if (poFeature->GetFeatureClass() != TABFCNoGeomFeature)
1885 : {
1886 0 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1887 0 : poMainFeature->SetGeometry(poGeom);
1888 : }
1889 :
1890 : /*-----------------------------------------------------------------
1891 : * Copy fields to poMainFeature
1892 : *----------------------------------------------------------------*/
1893 0 : for (int i = 0; i < poMainDefn->GetFieldCount(); i++)
1894 : {
1895 0 : if (m_panMainTableFieldMap[i] != -1)
1896 : {
1897 0 : poMainFeature->SetField(
1898 0 : i, poFeature->GetRawFieldRef(m_panMainTableFieldMap[i]));
1899 : }
1900 : }
1901 :
1902 : /*-----------------------------------------------------------------
1903 : * Look for a record id for the unique fields, and write a new
1904 : * record if necessary
1905 : *----------------------------------------------------------------*/
1906 0 : int nRecordNo = 0;
1907 0 : int nUniqueIndexNo = -1;
1908 0 : if (m_panMainTableFieldMap[0] != -1)
1909 0 : nUniqueIndexNo = m_poRelTable->GetFieldIndexNumber(0);
1910 :
1911 0 : if (nUniqueIndexNo > 0)
1912 : {
1913 0 : GByte *pKey = BuildFieldKey(
1914 0 : poFeature, 0, m_poRelTable->GetNativeFieldType(0), nUniqueIndexNo);
1915 :
1916 0 : if ((nRecordNo = m_poRelINDFileRef->FindFirst(nUniqueIndexNo, pKey)) ==
1917 : -1)
1918 0 : return -1;
1919 :
1920 0 : if (nRecordNo == 0)
1921 : {
1922 : /*---------------------------------------------------------
1923 : * No record in poRelTable yet for this unique value...
1924 : * add one now...
1925 : *--------------------------------------------------------*/
1926 0 : TABFeature *poRelFeature = new TABFeature(poRelDefn);
1927 :
1928 0 : for (int i = 0; i < poRelDefn->GetFieldCount(); i++)
1929 : {
1930 0 : if (m_panRelTableFieldMap[i] != -1)
1931 : {
1932 0 : poRelFeature->SetField(
1933 0 : i, poFeature->GetRawFieldRef(m_panRelTableFieldMap[i]));
1934 : }
1935 : }
1936 :
1937 0 : nRecordNo = ++m_nUniqueRecordNo;
1938 :
1939 0 : poRelFeature->SetField(m_nRelFieldNo, nRecordNo);
1940 :
1941 0 : if (m_poRelTable->CreateFeature(poRelFeature) == OGRERR_NONE)
1942 0 : return -1;
1943 :
1944 0 : delete poRelFeature;
1945 : }
1946 : }
1947 :
1948 : /*-----------------------------------------------------------------
1949 : * Write poMainFeature to the main table
1950 : *----------------------------------------------------------------*/
1951 0 : poMainFeature->SetField(m_nMainFieldNo, nRecordNo);
1952 :
1953 0 : if (m_poMainTable->CreateFeature(poMainFeature) != OGRERR_NONE)
1954 0 : nFeatureId = static_cast<int>(poMainFeature->GetFID());
1955 : else
1956 0 : nFeatureId = -1;
1957 :
1958 0 : delete poMainFeature;
1959 :
1960 0 : return nFeatureId;
1961 : }
1962 :
1963 : /**********************************************************************
1964 : * TABFile::SetFeatureDefn()
1965 : *
1966 : * NOT FULLY IMPLEMENTED YET...
1967 : *
1968 : * Returns 0 on success, -1 on error.
1969 : **********************************************************************/
1970 0 : int TABRelation::SetFeatureDefn(
1971 : OGRFeatureDefn *poFeatureDefn,
1972 : CPL_UNUSED TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */)
1973 : {
1974 0 : if (m_poDefn && m_poDefn->GetFieldCount() > 0)
1975 : {
1976 0 : CPLAssert(m_poDefn == nullptr);
1977 0 : return -1;
1978 : }
1979 :
1980 : /*-----------------------------------------------------------------
1981 : * Keep a reference to the OGRFeatureDefn... we'll have to take the
1982 : * reference count into account when we are done with it.
1983 : *----------------------------------------------------------------*/
1984 0 : if (m_poDefn && m_poDefn->Dereference() == 0)
1985 0 : delete m_poDefn;
1986 :
1987 0 : m_poDefn = poFeatureDefn;
1988 0 : m_poDefn->Reference();
1989 :
1990 0 : return 0;
1991 : }
|