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::GetExtent()
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::GetExtent(OGREnvelope *psExtent, int 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(psExtent, bForce);
1032 : }
1033 :
1034 : /**********************************************************************
1035 : * TABView::GetFeatureCountByType()
1036 : *
1037 : * Return number of features of each type.
1038 : *
1039 : * Note that the sum of the 4 returned values may be different from
1040 : * the total number of features since features with NONE geometry
1041 : * are not taken into account here.
1042 : *
1043 : * Note: the bForce flag has nmo effect on .TAB files since the info
1044 : * is always in the header.
1045 : *
1046 : * Returns 0 on success, or silently returns -1 (with no error) if this
1047 : * information is not available.
1048 : **********************************************************************/
1049 0 : int TABView::GetFeatureCountByType(int &numPoints, int &numLines,
1050 : int &numRegions, int &numTexts,
1051 : GBool bForce /*= TRUE*/)
1052 : {
1053 0 : if (m_nMainTableIndex == -1)
1054 0 : return -1;
1055 :
1056 0 : return m_papoTABFiles[m_nMainTableIndex]->GetFeatureCountByType(
1057 0 : numPoints, numLines, numRegions, numTexts, bForce);
1058 : }
1059 :
1060 : /**********************************************************************
1061 : * TABView::GetSpatialRef()
1062 : *
1063 : * Returns a reference to an OGRSpatialReference for this dataset.
1064 : * If the projection parameters have not been parsed yet, then we will
1065 : * parse them before returning.
1066 : *
1067 : * The returned object is owned and maintained by this TABFile and
1068 : * should not be modified or freed by the caller.
1069 : *
1070 : * Returns NULL if the SpatialRef cannot be accessed.
1071 : **********************************************************************/
1072 2 : OGRSpatialReference *TABView::GetSpatialRef()
1073 : {
1074 2 : if (m_nMainTableIndex == -1)
1075 : {
1076 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1077 : "GetSpatialRef() failed: file has not been opened yet.");
1078 0 : return nullptr;
1079 : }
1080 :
1081 2 : return m_papoTABFiles[m_nMainTableIndex]->GetSpatialRef();
1082 : }
1083 :
1084 : /**********************************************************************
1085 : * TABView::SetSpatialRef()
1086 : **********************************************************************/
1087 0 : int TABView::SetSpatialRef(OGRSpatialReference *poSpatialRef)
1088 : {
1089 0 : if (m_nMainTableIndex == -1)
1090 : {
1091 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1092 : "SetSpatialRef() failed: file has not been opened yet.");
1093 0 : return -1;
1094 : }
1095 :
1096 0 : return m_papoTABFiles[m_nMainTableIndex]->SetSpatialRef(poSpatialRef);
1097 : }
1098 :
1099 : /**********************************************************************
1100 : * TABView::SetBounds()
1101 : **********************************************************************/
1102 0 : int TABView::SetBounds(double dXMin, double dYMin, double dXMax, double dYMax)
1103 : {
1104 0 : if (m_nMainTableIndex == -1)
1105 : {
1106 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1107 : "SetBounds() failed: file has not been opened yet.");
1108 0 : return -1;
1109 : }
1110 :
1111 0 : return m_papoTABFiles[m_nMainTableIndex]->SetBounds(dXMin, dYMin, dXMax,
1112 0 : dYMax);
1113 : }
1114 :
1115 : /************************************************************************/
1116 : /* TestCapability() */
1117 : /************************************************************************/
1118 :
1119 0 : int TABView::TestCapability(const char *pszCap)
1120 :
1121 : {
1122 0 : if (EQUAL(pszCap, OLCRandomRead))
1123 0 : return TRUE;
1124 :
1125 0 : else if (EQUAL(pszCap, OLCSequentialWrite))
1126 0 : return TRUE;
1127 :
1128 0 : else if (EQUAL(pszCap, OLCRandomWrite))
1129 0 : return FALSE;
1130 :
1131 0 : else if (EQUAL(pszCap, OLCFastFeatureCount))
1132 0 : return m_poFilterGeom == nullptr;
1133 :
1134 0 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
1135 0 : return FALSE;
1136 :
1137 0 : else if (EQUAL(pszCap, OLCFastGetExtent))
1138 0 : return TRUE;
1139 :
1140 0 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
1141 0 : return TestUtf8Capability();
1142 :
1143 : else
1144 0 : return FALSE;
1145 : }
1146 :
1147 : /**********************************************************************
1148 : * TABView::Dump()
1149 : *
1150 : * Dump block contents... available only in DEBUG mode.
1151 : **********************************************************************/
1152 : #ifdef DEBUG
1153 :
1154 0 : void TABView::Dump(FILE *fpOut /*=NULL*/)
1155 : {
1156 0 : if (fpOut == nullptr)
1157 0 : fpOut = stdout;
1158 :
1159 0 : fprintf(fpOut, "----- TABView::Dump() -----\n");
1160 :
1161 0 : if (m_numTABFiles > 0)
1162 : {
1163 0 : fprintf(fpOut, "File is not opened.\n");
1164 : }
1165 : else
1166 : {
1167 0 : fprintf(fpOut, "File is opened: %s\n", m_pszFname);
1168 0 : fprintf(fpOut, "View contains %d tables\n", m_numTABFiles);
1169 : }
1170 :
1171 0 : fflush(fpOut);
1172 0 : }
1173 :
1174 : #endif // DEBUG
1175 :
1176 : /*=====================================================================
1177 : * class TABRelation
1178 : *====================================================================*/
1179 :
1180 : /**********************************************************************
1181 : * TABRelation::TABRelation()
1182 : *
1183 : * Constructor.
1184 : **********************************************************************/
1185 2 : TABRelation::TABRelation()
1186 : : m_poMainTable(nullptr), m_pszMainFieldName(nullptr), m_nMainFieldNo(-1),
1187 : m_poRelTable(nullptr), m_pszRelFieldName(nullptr), m_nRelFieldNo(-1),
1188 : m_poRelINDFileRef(nullptr), m_nRelFieldIndexNo(-1), m_nUniqueRecordNo(0),
1189 : m_panMainTableFieldMap(nullptr), m_panRelTableFieldMap(nullptr),
1190 2 : m_poDefn(nullptr)
1191 : {
1192 2 : }
1193 :
1194 : /**********************************************************************
1195 : * TABRelation::~TABRelation()
1196 : *
1197 : * Destructor.
1198 : **********************************************************************/
1199 4 : TABRelation::~TABRelation()
1200 : {
1201 2 : ResetAllMembers();
1202 2 : }
1203 :
1204 : /**********************************************************************
1205 : * TABRelation::ResetAllMembers()
1206 : *
1207 : * Reset all class members.
1208 : **********************************************************************/
1209 4 : void TABRelation::ResetAllMembers()
1210 : {
1211 4 : m_poMainTable = nullptr;
1212 4 : CPLFree(m_pszMainFieldName);
1213 4 : m_pszMainFieldName = nullptr;
1214 4 : m_nMainFieldNo = -1;
1215 :
1216 4 : m_poRelTable = nullptr;
1217 4 : CPLFree(m_pszRelFieldName);
1218 4 : m_pszRelFieldName = nullptr;
1219 4 : m_nRelFieldNo = -1;
1220 4 : m_nRelFieldIndexNo = -1;
1221 :
1222 4 : m_nUniqueRecordNo = 0;
1223 :
1224 : // No need to close m_poRelINDFileRef since we only got a ref. to it
1225 4 : m_poRelINDFileRef = nullptr;
1226 :
1227 4 : CPLFree(m_panMainTableFieldMap);
1228 4 : m_panMainTableFieldMap = nullptr;
1229 4 : CPLFree(m_panRelTableFieldMap);
1230 4 : m_panRelTableFieldMap = nullptr;
1231 :
1232 : /*-----------------------------------------------------------------
1233 : * Note: we have to check the reference count before deleting m_poDefn
1234 : *----------------------------------------------------------------*/
1235 4 : if (m_poDefn && m_poDefn->Dereference() == 0)
1236 0 : delete m_poDefn;
1237 4 : m_poDefn = nullptr;
1238 4 : }
1239 :
1240 : /**********************************************************************
1241 : * TABRelation::Init()
1242 : *
1243 : * Set the details of the relation: the main and related tables, the fields
1244 : * through which they will be connected, and the list of fields to select.
1245 : * After this call, we are ready to read data records.
1246 : *
1247 : * For write access, Init() is called with pszMain/RelFieldName and
1248 : * **papszSelectedFields passed as NULL. They will have to be set through
1249 : * other methods before a first feature can be written.
1250 : *
1251 : * A new OGRFeatureDefn is also built for the combined tables.
1252 : *
1253 : * Returns 0 on success, or -1 or error.
1254 : **********************************************************************/
1255 2 : int TABRelation::Init(const char *pszViewName, TABFile *poMainTable,
1256 : TABFile *poRelTable, const char *pszMainFieldName,
1257 : const char *pszRelFieldName, char **papszSelectedFields)
1258 : {
1259 2 : if (poMainTable == nullptr || poRelTable == nullptr)
1260 0 : return -1;
1261 :
1262 : // We'll need the feature Defn later...
1263 2 : OGRFeatureDefn *poMainDefn = poMainTable->GetLayerDefn();
1264 2 : OGRFeatureDefn *poRelDefn = poRelTable->GetLayerDefn();
1265 :
1266 : /*-----------------------------------------------------------------
1267 : * Keep info for later use about source tables, etc.
1268 : *----------------------------------------------------------------*/
1269 2 : ResetAllMembers();
1270 :
1271 2 : m_poMainTable = poMainTable;
1272 2 : if (pszMainFieldName)
1273 : {
1274 2 : m_pszMainFieldName = CPLStrdup(pszMainFieldName);
1275 2 : m_nMainFieldNo = poMainDefn->GetFieldIndex(pszMainFieldName);
1276 : }
1277 :
1278 2 : m_poRelTable = poRelTable;
1279 2 : if (pszRelFieldName)
1280 : {
1281 2 : m_pszRelFieldName = CPLStrdup(pszRelFieldName);
1282 2 : m_nRelFieldNo = poRelDefn->GetFieldIndex(pszRelFieldName);
1283 2 : m_nRelFieldIndexNo = poRelTable->GetFieldIndexNumber(m_nRelFieldNo);
1284 2 : m_poRelINDFileRef = poRelTable->GetINDFileRef();
1285 :
1286 2 : if (m_nRelFieldIndexNo >= 0 && m_poRelINDFileRef == nullptr)
1287 : {
1288 0 : CPLError(CE_Failure, CPLE_FileIO,
1289 : "Field %s is indexed but the .IND file is missing.",
1290 : pszRelFieldName);
1291 0 : return -1;
1292 : }
1293 : }
1294 :
1295 : /*-----------------------------------------------------------------
1296 : * Init field maps. For each field in each table, a -1 means that
1297 : * the field is not selected, and a value >=0 is the index of the
1298 : * field in the view's FeatureDefn
1299 : *----------------------------------------------------------------*/
1300 2 : const int numFields1 = poMainDefn ? poMainDefn->GetFieldCount() : 0;
1301 2 : const int numFields2 = poRelDefn ? poRelDefn->GetFieldCount() : 0;
1302 :
1303 2 : m_panMainTableFieldMap =
1304 2 : static_cast<int *>(CPLMalloc((numFields1 + 1) * sizeof(int)));
1305 6 : for (int i = 0; i < numFields1; i++)
1306 4 : m_panMainTableFieldMap[i] = -1;
1307 2 : m_panRelTableFieldMap =
1308 2 : static_cast<int *>(CPLMalloc((numFields2 + 1) * sizeof(int)));
1309 6 : for (int i = 0; i < numFields2; i++)
1310 4 : m_panRelTableFieldMap[i] = -1;
1311 :
1312 : /*-----------------------------------------------------------------
1313 : * If selectedFields = "*" then select all fields from both tables
1314 : *----------------------------------------------------------------*/
1315 2 : papszSelectedFields = CSLDuplicate(papszSelectedFields);
1316 2 : if (papszSelectedFields != nullptr && papszSelectedFields[0] != nullptr &&
1317 2 : papszSelectedFields[1] == nullptr && EQUAL(papszSelectedFields[0], "*"))
1318 : {
1319 1 : CSLDestroy(papszSelectedFields);
1320 1 : papszSelectedFields = nullptr;
1321 :
1322 3 : for (int i = 0; i < numFields1; i++)
1323 : {
1324 2 : OGRFieldDefn *poFieldDefn = poMainDefn->GetFieldDefn(i);
1325 :
1326 : papszSelectedFields =
1327 2 : CSLAddString(papszSelectedFields, poFieldDefn->GetNameRef());
1328 : }
1329 :
1330 3 : for (int i = 0; i < numFields2; i++)
1331 : {
1332 2 : OGRFieldDefn *poFieldDefn = poRelDefn->GetFieldDefn(i);
1333 :
1334 2 : if (CSLFindString(papszSelectedFields, poFieldDefn->GetNameRef()) !=
1335 : -1)
1336 1 : continue; // Avoid duplicate field name in view
1337 :
1338 : papszSelectedFields =
1339 1 : CSLAddString(papszSelectedFields, poFieldDefn->GetNameRef());
1340 : }
1341 : }
1342 :
1343 : /*-----------------------------------------------------------------
1344 : * Create new FeatureDefn and copy selected fields definitions
1345 : * while updating the appropriate field maps.
1346 : *----------------------------------------------------------------*/
1347 2 : OGRFieldDefn *poFieldDefn = nullptr;
1348 :
1349 2 : m_poDefn = new OGRFeatureDefn(pszViewName);
1350 : // Ref count defaults to 0... set it to 1
1351 2 : m_poDefn->Reference();
1352 :
1353 7 : for (int i = 0;
1354 7 : papszSelectedFields != nullptr && papszSelectedFields[i] != nullptr;
1355 : i++)
1356 : {
1357 : int nIndex;
1358 10 : if (poMainDefn &&
1359 5 : (nIndex = poMainDefn->GetFieldIndex(papszSelectedFields[i])) >= 0)
1360 : {
1361 : /* Field from the main table
1362 : */
1363 3 : poFieldDefn = poMainDefn->GetFieldDefn(nIndex);
1364 3 : m_poDefn->AddFieldDefn(poFieldDefn);
1365 3 : m_panMainTableFieldMap[nIndex] = m_poDefn->GetFieldCount() - 1;
1366 : }
1367 4 : else if (poRelDefn && (nIndex = poRelDefn->GetFieldIndex(
1368 2 : papszSelectedFields[i])) >= 0)
1369 : {
1370 : /* Field from the related table
1371 : */
1372 2 : poFieldDefn = poRelDefn->GetFieldDefn(nIndex);
1373 2 : m_poDefn->AddFieldDefn(poFieldDefn);
1374 2 : m_panRelTableFieldMap[nIndex] = m_poDefn->GetFieldCount() - 1;
1375 : }
1376 : else
1377 : {
1378 : // Hummm... field does not exist... likely an unsupported feature!
1379 : // At least send a warning and ignore the field.
1380 0 : CPLError(CE_Warning, CPLE_IllegalArg,
1381 : "Selected Field %s not found in source tables %s and %s",
1382 0 : papszSelectedFields[i],
1383 0 : poMainDefn ? poMainDefn->GetName() : "(null)",
1384 0 : poRelDefn ? poRelDefn->GetName() : "(null)");
1385 : }
1386 : }
1387 2 : CSLDestroy(papszSelectedFields);
1388 2 : return 0;
1389 : }
1390 :
1391 : /**********************************************************************
1392 : * TABRelation::CreateRelFields()
1393 : *
1394 : * For write access, create the integer fields in each table that will
1395 : * link them, and setup everything to be ready to write the first feature.
1396 : *
1397 : * This function should be called just before writing the first feature.
1398 : *
1399 : * Returns 0 on success, or -1 or error.
1400 : **********************************************************************/
1401 0 : int TABRelation::CreateRelFields()
1402 : {
1403 : /*-----------------------------------------------------------------
1404 : * Create the field in each table.
1405 : * The default name is "MI_refnum" but if a field with the same name
1406 : * already exists then we'll try to generate a unique name.
1407 : *----------------------------------------------------------------*/
1408 0 : m_pszMainFieldName = CPLStrdup("MI_Refnum ");
1409 0 : const size_t nLen = strlen(m_pszMainFieldName) + 1;
1410 0 : strcpy(m_pszMainFieldName, "MI_Refnum");
1411 0 : int i = 1;
1412 0 : while (m_poDefn->GetFieldIndex(m_pszMainFieldName) >= 0)
1413 : {
1414 0 : snprintf(m_pszMainFieldName, nLen, "MI_Refnum_%d", i++);
1415 : }
1416 0 : m_pszRelFieldName = CPLStrdup(m_pszMainFieldName);
1417 :
1418 0 : m_nMainFieldNo = m_nRelFieldNo = -1;
1419 0 : if (m_poMainTable->AddFieldNative(m_pszMainFieldName, TABFInteger, 0, 0) ==
1420 : 0)
1421 0 : m_nMainFieldNo = m_poMainTable->GetLayerDefn()->GetFieldCount() - 1;
1422 :
1423 0 : if (m_poRelTable->AddFieldNative(m_pszRelFieldName, TABFInteger, 0, 0) == 0)
1424 0 : m_nRelFieldNo = m_poRelTable->GetLayerDefn()->GetFieldCount() - 1;
1425 :
1426 0 : if (m_nMainFieldNo == -1 || m_nRelFieldNo == -1)
1427 0 : return -1;
1428 :
1429 0 : if (m_poMainTable->SetFieldIndexed(m_nMainFieldNo) == -1)
1430 0 : return -1;
1431 :
1432 0 : if ((m_nRelFieldIndexNo = m_poRelTable->SetFieldIndexed(m_nRelFieldNo)) ==
1433 : -1)
1434 0 : return -1;
1435 :
1436 0 : m_poRelINDFileRef = m_poRelTable->GetINDFileRef();
1437 :
1438 : /*-----------------------------------------------------------------
1439 : * Update field maps
1440 : *----------------------------------------------------------------*/
1441 0 : OGRFeatureDefn *poMainDefn = m_poMainTable->GetLayerDefn();
1442 0 : OGRFeatureDefn *poRelDefn = m_poRelTable->GetLayerDefn();
1443 :
1444 0 : m_panMainTableFieldMap = static_cast<int *>(CPLRealloc(
1445 0 : m_panMainTableFieldMap, poMainDefn->GetFieldCount() * sizeof(int)));
1446 0 : m_panMainTableFieldMap[poMainDefn->GetFieldCount() - 1] = -1;
1447 :
1448 0 : m_panRelTableFieldMap = static_cast<int *>(CPLRealloc(
1449 0 : m_panRelTableFieldMap, poRelDefn->GetFieldCount() * sizeof(int)));
1450 0 : m_panRelTableFieldMap[poRelDefn->GetFieldCount() - 1] = -1;
1451 :
1452 : /*-----------------------------------------------------------------
1453 : * Make sure the first unique field (in poRelTable) is indexed since
1454 : * it is the one against which we will try to match records.
1455 : *----------------------------------------------------------------*/
1456 0 : if (m_poRelTable->SetFieldIndexed(0) == -1)
1457 0 : return -1;
1458 :
1459 0 : return 0;
1460 : }
1461 :
1462 : /**********************************************************************
1463 : * TABRelation::GetFeature()
1464 : *
1465 : * Fill and return a TABFeature object for the specified feature id.
1466 : *
1467 : * The returned pointer is a new TABFeature that will have to be freed
1468 : * by the caller.
1469 : *
1470 : * Returns NULL if the specified feature id does not exist of if an
1471 : * error happened. In any case, CPLError() will have been called to
1472 : * report the reason of the failure.
1473 : *
1474 : * __TODO__ The current implementation fetches the features from each table
1475 : * and creates a 3rd feature to merge them. There would be room for
1476 : * optimization, at least by avoiding the duplication of the geometry
1477 : * which can be big sometimes... but this would imply changes at the
1478 : * lower-level in the lib. and we won't go there yet.
1479 : **********************************************************************/
1480 2 : TABFeature *TABRelation::GetFeature(int nFeatureId)
1481 : {
1482 : /*-----------------------------------------------------------------
1483 : * Make sure init() has been called
1484 : *----------------------------------------------------------------*/
1485 2 : if (m_poMainTable == nullptr || m_poRelTable == nullptr)
1486 : {
1487 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1488 : "GetFeatureRef() failed: object not initialized yet!");
1489 0 : return nullptr;
1490 : }
1491 :
1492 : /*-----------------------------------------------------------------
1493 : * Read main feature and create a new one of the right type
1494 : *----------------------------------------------------------------*/
1495 2 : TABFeature *poMainFeature = m_poMainTable->GetFeatureRef(nFeatureId);
1496 2 : if (poMainFeature == nullptr)
1497 : {
1498 : // Feature cannot be read from main table...
1499 : // an error has already been reported.
1500 0 : return nullptr;
1501 : }
1502 :
1503 2 : TABFeature *poCurFeature = poMainFeature->CloneTABFeature(m_poDefn);
1504 :
1505 : /*-----------------------------------------------------------------
1506 : * Keep track of FID and copy the geometry
1507 : *----------------------------------------------------------------*/
1508 2 : poCurFeature->SetFID(nFeatureId);
1509 :
1510 2 : if (poCurFeature->GetFeatureClass() != TABFCNoGeomFeature)
1511 : {
1512 2 : OGRGeometry *poGeom = poMainFeature->GetGeometryRef();
1513 2 : poCurFeature->SetGeometry(poGeom);
1514 : }
1515 :
1516 : /*-----------------------------------------------------------------
1517 : * Fetch feature from related table
1518 : *
1519 : * __TODO__ Right now we support only many-to-1 relationships, but
1520 : * it might be possible to have several related entries
1521 : * for a single key, and in this case we should return
1522 : * one new feature for each of them.
1523 : *----------------------------------------------------------------*/
1524 2 : TABFeature *poRelFeature = nullptr;
1525 2 : if (m_poRelINDFileRef)
1526 : {
1527 : GByte *pKey =
1528 2 : BuildFieldKey(poMainFeature, m_nMainFieldNo,
1529 2 : m_poMainTable->GetNativeFieldType(m_nMainFieldNo),
1530 : m_nRelFieldIndexNo);
1531 : int nRelFeatureId =
1532 2 : m_poRelINDFileRef->FindFirst(m_nRelFieldIndexNo, pKey);
1533 :
1534 2 : if (nRelFeatureId > 0)
1535 2 : poRelFeature = m_poRelTable->GetFeatureRef(nRelFeatureId);
1536 : }
1537 :
1538 : /*-----------------------------------------------------------------
1539 : * Copy fields from poMainFeature
1540 : *----------------------------------------------------------------*/
1541 6 : for (int i = 0; i < poMainFeature->GetFieldCount(); i++)
1542 : {
1543 4 : if (m_panMainTableFieldMap[i] != -1)
1544 : {
1545 3 : poCurFeature->SetField(m_panMainTableFieldMap[i],
1546 3 : poMainFeature->GetRawFieldRef(i));
1547 : }
1548 : }
1549 :
1550 : /*-----------------------------------------------------------------
1551 : * Copy fields from poRelFeature...
1552 : *
1553 : * NOTE: For now, if no corresponding feature is found in RelTable
1554 : * then we will just leave the corresponding fields unset.
1555 : *----------------------------------------------------------------*/
1556 6 : for (int i = 0; poRelFeature && i < poRelFeature->GetFieldCount(); i++)
1557 : {
1558 4 : if (m_panRelTableFieldMap[i] != -1)
1559 : {
1560 2 : poCurFeature->SetField(m_panRelTableFieldMap[i],
1561 2 : poRelFeature->GetRawFieldRef(i));
1562 : }
1563 : }
1564 :
1565 2 : return poCurFeature;
1566 : }
1567 :
1568 : /**********************************************************************
1569 : * TABRelation::BuildFieldKey()
1570 : *
1571 : * Return the index key for the specified field in poFeature.
1572 : * Simply maps the call to the proper method in the TABINDFile class.
1573 : *
1574 : * Returns a reference to a TABINDFile internal buffer that should not
1575 : * be freed by the caller.
1576 : **********************************************************************/
1577 2 : GByte *TABRelation::BuildFieldKey(TABFeature *poFeature, int nFieldNo,
1578 : TABFieldType eType, int nIndexNo)
1579 : {
1580 2 : GByte *pKey = nullptr;
1581 :
1582 2 : switch (eType)
1583 : {
1584 0 : case TABFChar:
1585 0 : pKey = m_poRelINDFileRef->BuildKey(
1586 : nIndexNo, poFeature->GetFieldAsString(nFieldNo));
1587 0 : break;
1588 :
1589 0 : case TABFDecimal:
1590 : case TABFFloat:
1591 0 : pKey = m_poRelINDFileRef->BuildKey(
1592 : nIndexNo, poFeature->GetFieldAsDouble(nFieldNo));
1593 0 : break;
1594 :
1595 : // __TODO__ DateTime fields are 8 bytes long, not supported yet by
1596 : // the indexing code (see bug #1844).
1597 0 : case TABFDateTime:
1598 0 : CPLError(
1599 : CE_Failure, CPLE_NotSupported,
1600 : "TABRelation on field of type DateTime not supported yet.");
1601 0 : break;
1602 :
1603 2 : case TABFInteger:
1604 : case TABFSmallInt:
1605 : case TABFDate:
1606 : case TABFTime:
1607 : case TABFLogical:
1608 : default:
1609 2 : pKey = m_poRelINDFileRef->BuildKey(
1610 : nIndexNo, poFeature->GetFieldAsInteger(nFieldNo));
1611 2 : break;
1612 : }
1613 :
1614 2 : return pKey;
1615 : }
1616 :
1617 : /**********************************************************************
1618 : * TABRelation::GetNativeFieldType()
1619 : *
1620 : * Returns the native MapInfo field type for the specified field.
1621 : *
1622 : * Returns TABFUnknown if file is not opened, or if specified field index is
1623 : * invalid.
1624 : *
1625 : * Note that field ids are positive and start at 0.
1626 : **********************************************************************/
1627 0 : TABFieldType TABRelation::GetNativeFieldType(int nFieldId)
1628 : {
1629 : int i, numFields;
1630 :
1631 0 : if (m_poMainTable == nullptr || m_poRelTable == nullptr ||
1632 0 : m_panMainTableFieldMap == nullptr || m_panRelTableFieldMap == nullptr)
1633 0 : return TABFUnknown;
1634 :
1635 : /*-----------------------------------------------------------------
1636 : * Look for nFieldId in the field maps and call the corresponding
1637 : * TAB file's GetNativeFieldType()
1638 : *----------------------------------------------------------------*/
1639 0 : numFields = m_poMainTable->GetLayerDefn()->GetFieldCount();
1640 0 : for (i = 0; i < numFields; i++)
1641 : {
1642 0 : if (m_panMainTableFieldMap[i] == nFieldId)
1643 : {
1644 0 : return m_poMainTable->GetNativeFieldType(i);
1645 : }
1646 : }
1647 :
1648 0 : numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
1649 0 : for (i = 0; i < numFields; i++)
1650 : {
1651 0 : if (m_panRelTableFieldMap[i] == nFieldId)
1652 : {
1653 0 : return m_poRelTable->GetNativeFieldType(i);
1654 : }
1655 : }
1656 :
1657 0 : return TABFUnknown;
1658 : }
1659 :
1660 : /**********************************************************************
1661 : * TABRelation::AddFieldNative()
1662 : *
1663 : * Create a new field using a native mapinfo data type... this is an
1664 : * alternative to defining fields through the OGR interface.
1665 : * This function should be called after creating a new dataset, but before
1666 : * writing the first feature.
1667 : *
1668 : * This function will build/update the OGRFeatureDefn that will have to be
1669 : * used when writing features to this dataset.
1670 : *
1671 : * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn().
1672 : *
1673 : * Returns 0 on success, -1 on error.
1674 : **********************************************************************/
1675 0 : int TABRelation::AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
1676 : int nWidth /*=0*/, int nPrecision /*=0*/,
1677 : GBool bIndexed /*=FALSE*/,
1678 : GBool bUnique /*=FALSE*/, int bApproxOK)
1679 : {
1680 0 : if (m_poMainTable == nullptr || m_poRelTable == nullptr ||
1681 0 : m_panMainTableFieldMap == nullptr || m_panRelTableFieldMap == nullptr)
1682 0 : return -1;
1683 :
1684 0 : if (!bUnique)
1685 : {
1686 : /*-------------------------------------------------------------
1687 : * Add field to poMainTable and to m_poDefn
1688 : *------------------------------------------------------------*/
1689 0 : if (m_poMainTable->AddFieldNative(pszName, eMapInfoType, nWidth,
1690 : nPrecision, bIndexed, bUnique,
1691 0 : bApproxOK) != 0)
1692 0 : return -1;
1693 :
1694 0 : OGRFeatureDefn *poMainDefn = m_poMainTable->GetLayerDefn();
1695 :
1696 0 : m_panMainTableFieldMap = static_cast<int *>(CPLRealloc(
1697 0 : m_panMainTableFieldMap, poMainDefn->GetFieldCount() * sizeof(int)));
1698 :
1699 0 : m_poDefn->AddFieldDefn(
1700 0 : poMainDefn->GetFieldDefn(poMainDefn->GetFieldCount() - 1));
1701 :
1702 0 : m_panMainTableFieldMap[poMainDefn->GetFieldCount() - 1] =
1703 0 : m_poDefn->GetFieldCount() - 1;
1704 : }
1705 : else
1706 : {
1707 : /*-------------------------------------------------------------
1708 : * Add field to poRelTable and to m_poDefn
1709 : *------------------------------------------------------------*/
1710 0 : if (m_poRelTable->AddFieldNative(pszName, eMapInfoType, nWidth,
1711 : nPrecision, bIndexed, bUnique,
1712 0 : bApproxOK) != 0)
1713 0 : return -1;
1714 :
1715 0 : OGRFeatureDefn *poRelDefn = m_poRelTable->GetLayerDefn();
1716 :
1717 0 : m_panRelTableFieldMap = static_cast<int *>(CPLRealloc(
1718 0 : m_panRelTableFieldMap, poRelDefn->GetFieldCount() * sizeof(int)));
1719 :
1720 0 : m_poDefn->AddFieldDefn(
1721 0 : poRelDefn->GetFieldDefn(poRelDefn->GetFieldCount() - 1));
1722 :
1723 0 : m_panRelTableFieldMap[poRelDefn->GetFieldCount() - 1] =
1724 0 : m_poDefn->GetFieldCount() - 1;
1725 :
1726 : // The first field in this table must be indexed.
1727 0 : if (poRelDefn->GetFieldCount() == 1)
1728 0 : m_poRelTable->SetFieldIndexed(0);
1729 : }
1730 :
1731 0 : return 0;
1732 : }
1733 :
1734 : /**********************************************************************
1735 : * TABRelation::IsFieldIndexed()
1736 : *
1737 : * Returns TRUE is specified field is indexed.
1738 : *
1739 : * Note that field ids are positive and start at 0.
1740 : **********************************************************************/
1741 0 : GBool TABRelation::IsFieldIndexed(int nFieldId)
1742 : {
1743 : int i, numFields;
1744 :
1745 0 : if (m_poMainTable == nullptr || m_poRelTable == nullptr ||
1746 0 : m_panMainTableFieldMap == nullptr || m_panRelTableFieldMap == nullptr)
1747 0 : return FALSE;
1748 :
1749 : /*-----------------------------------------------------------------
1750 : * Look for nFieldId in the field maps and call the corresponding
1751 : * TAB file's GetNativeFieldType()
1752 : *----------------------------------------------------------------*/
1753 0 : numFields = m_poMainTable->GetLayerDefn()->GetFieldCount();
1754 0 : for (i = 0; i < numFields; i++)
1755 : {
1756 0 : if (m_panMainTableFieldMap[i] == nFieldId)
1757 : {
1758 0 : return m_poMainTable->IsFieldIndexed(i);
1759 : }
1760 : }
1761 :
1762 0 : numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
1763 0 : for (i = 0; i < numFields; i++)
1764 : {
1765 0 : if (m_panRelTableFieldMap[i] == nFieldId)
1766 : {
1767 0 : return m_poRelTable->IsFieldIndexed(i);
1768 : }
1769 : }
1770 :
1771 0 : return FALSE;
1772 : }
1773 :
1774 : /**********************************************************************
1775 : * TABRelation::SetFieldIndexed()
1776 : *
1777 : * Request that the specified field be indexed. This will create the .IND
1778 : * file, etc.
1779 : *
1780 : * Note that field ids are positive and start at 0.
1781 : *
1782 : * Returns 0 on success, -1 on error.
1783 : **********************************************************************/
1784 0 : int TABRelation::SetFieldIndexed(int nFieldId)
1785 : {
1786 : int i, numFields;
1787 :
1788 0 : if (m_poMainTable == nullptr || m_poRelTable == nullptr ||
1789 0 : m_panMainTableFieldMap == nullptr || m_panRelTableFieldMap == nullptr)
1790 0 : return -1;
1791 :
1792 : /*-----------------------------------------------------------------
1793 : * Look for nFieldId in the field maps and call the corresponding
1794 : * TAB file's GetNativeFieldType()
1795 : *----------------------------------------------------------------*/
1796 0 : numFields = m_poMainTable->GetLayerDefn()->GetFieldCount();
1797 0 : for (i = 0; i < numFields; i++)
1798 : {
1799 0 : if (m_panMainTableFieldMap[i] == nFieldId)
1800 : {
1801 0 : return m_poMainTable->SetFieldIndexed(i);
1802 : }
1803 : }
1804 :
1805 0 : numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
1806 0 : for (i = 0; i < numFields; i++)
1807 : {
1808 0 : if (m_panRelTableFieldMap[i] == nFieldId)
1809 : {
1810 0 : return m_poRelTable->SetFieldIndexed(i);
1811 : }
1812 : }
1813 :
1814 0 : return -1;
1815 : }
1816 :
1817 : /**********************************************************************
1818 : * TABRelation::IsFieldUnique()
1819 : *
1820 : * Returns TRUE is specified field is part of the unique table (poRelTable).
1821 : *
1822 : * Note that field ids are positive and start at 0.
1823 : **********************************************************************/
1824 0 : GBool TABRelation::IsFieldUnique(int nFieldId)
1825 : {
1826 : int i, numFields;
1827 :
1828 0 : if (m_poMainTable == nullptr || m_poRelTable == nullptr ||
1829 0 : m_panMainTableFieldMap == nullptr || m_panRelTableFieldMap == nullptr)
1830 0 : return FALSE;
1831 :
1832 : /*-----------------------------------------------------------------
1833 : * Look for nFieldId in the poRelTable field map
1834 : *----------------------------------------------------------------*/
1835 0 : numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
1836 0 : for (i = 0; i < numFields; i++)
1837 : {
1838 0 : if (m_panRelTableFieldMap[i] == nFieldId)
1839 : {
1840 0 : return TRUE; // If it is here then it is unique!
1841 : }
1842 : }
1843 :
1844 0 : return FALSE;
1845 : }
1846 :
1847 : /**********************************************************************
1848 : * TABRelation::WriteFeature()
1849 : *
1850 : * Write a feature to this dataset.
1851 : *
1852 : * For now only sequential writes are supported (i.e. with nFeatureId=-1)
1853 : * but eventually we should be able to do random access by specifying
1854 : * a value through nFeatureId.
1855 : *
1856 : * Returns the new featureId (> 0) on success, or -1 if an
1857 : * error happened in which case, CPLError() will have been called to
1858 : * report the reason of the failure.
1859 : **********************************************************************/
1860 0 : int TABRelation::WriteFeature(TABFeature *poFeature, int nFeatureId /*=-1*/)
1861 : {
1862 0 : TABFeature *poMainFeature = nullptr;
1863 :
1864 0 : if (nFeatureId != -1)
1865 : {
1866 0 : CPLError(CE_Failure, CPLE_NotSupported,
1867 : "WriteFeature(): random access not implemented yet.");
1868 0 : return -1;
1869 : }
1870 :
1871 0 : CPLAssert(m_poMainTable && m_poRelTable);
1872 :
1873 : // We'll need the feature Defn later...
1874 0 : OGRFeatureDefn *poMainDefn = m_poMainTable->GetLayerDefn();
1875 0 : OGRFeatureDefn *poRelDefn = m_poRelTable->GetLayerDefn();
1876 :
1877 : /*-----------------------------------------------------------------
1878 : * Create one feature for each table
1879 : * Copy the geometry only to the feature from the main table
1880 : *----------------------------------------------------------------*/
1881 0 : poMainFeature = poFeature->CloneTABFeature(poMainDefn);
1882 :
1883 0 : if (poFeature->GetFeatureClass() != TABFCNoGeomFeature)
1884 : {
1885 0 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1886 0 : poMainFeature->SetGeometry(poGeom);
1887 : }
1888 :
1889 : /*-----------------------------------------------------------------
1890 : * Copy fields to poMainFeature
1891 : *----------------------------------------------------------------*/
1892 0 : for (int i = 0; i < poMainDefn->GetFieldCount(); i++)
1893 : {
1894 0 : if (m_panMainTableFieldMap[i] != -1)
1895 : {
1896 0 : poMainFeature->SetField(
1897 0 : i, poFeature->GetRawFieldRef(m_panMainTableFieldMap[i]));
1898 : }
1899 : }
1900 :
1901 : /*-----------------------------------------------------------------
1902 : * Look for a record id for the unique fields, and write a new
1903 : * record if necessary
1904 : *----------------------------------------------------------------*/
1905 0 : int nRecordNo = 0;
1906 0 : int nUniqueIndexNo = -1;
1907 0 : if (m_panMainTableFieldMap[0] != -1)
1908 0 : nUniqueIndexNo = m_poRelTable->GetFieldIndexNumber(0);
1909 :
1910 0 : if (nUniqueIndexNo > 0)
1911 : {
1912 0 : GByte *pKey = BuildFieldKey(
1913 0 : poFeature, 0, m_poRelTable->GetNativeFieldType(0), nUniqueIndexNo);
1914 :
1915 0 : if ((nRecordNo = m_poRelINDFileRef->FindFirst(nUniqueIndexNo, pKey)) ==
1916 : -1)
1917 0 : return -1;
1918 :
1919 0 : if (nRecordNo == 0)
1920 : {
1921 : /*---------------------------------------------------------
1922 : * No record in poRelTable yet for this unique value...
1923 : * add one now...
1924 : *--------------------------------------------------------*/
1925 0 : TABFeature *poRelFeature = new TABFeature(poRelDefn);
1926 :
1927 0 : for (int i = 0; i < poRelDefn->GetFieldCount(); i++)
1928 : {
1929 0 : if (m_panRelTableFieldMap[i] != -1)
1930 : {
1931 0 : poRelFeature->SetField(
1932 0 : i, poFeature->GetRawFieldRef(m_panRelTableFieldMap[i]));
1933 : }
1934 : }
1935 :
1936 0 : nRecordNo = ++m_nUniqueRecordNo;
1937 :
1938 0 : poRelFeature->SetField(m_nRelFieldNo, nRecordNo);
1939 :
1940 0 : if (m_poRelTable->CreateFeature(poRelFeature) == OGRERR_NONE)
1941 0 : return -1;
1942 :
1943 0 : delete poRelFeature;
1944 : }
1945 : }
1946 :
1947 : /*-----------------------------------------------------------------
1948 : * Write poMainFeature to the main table
1949 : *----------------------------------------------------------------*/
1950 0 : poMainFeature->SetField(m_nMainFieldNo, nRecordNo);
1951 :
1952 0 : if (m_poMainTable->CreateFeature(poMainFeature) != OGRERR_NONE)
1953 0 : nFeatureId = static_cast<int>(poMainFeature->GetFID());
1954 : else
1955 0 : nFeatureId = -1;
1956 :
1957 0 : delete poMainFeature;
1958 :
1959 0 : return nFeatureId;
1960 : }
1961 :
1962 : /**********************************************************************
1963 : * TABFile::SetFeatureDefn()
1964 : *
1965 : * NOT FULLY IMPLEMENTED YET...
1966 : *
1967 : * Returns 0 on success, -1 on error.
1968 : **********************************************************************/
1969 0 : int TABRelation::SetFeatureDefn(
1970 : OGRFeatureDefn *poFeatureDefn,
1971 : CPL_UNUSED TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */)
1972 : {
1973 0 : if (m_poDefn && m_poDefn->GetFieldCount() > 0)
1974 : {
1975 0 : CPLAssert(m_poDefn == nullptr);
1976 0 : return -1;
1977 : }
1978 :
1979 : /*-----------------------------------------------------------------
1980 : * Keep a reference to the OGRFeatureDefn... we'll have to take the
1981 : * reference count into account when we are done with it.
1982 : *----------------------------------------------------------------*/
1983 0 : if (m_poDefn && m_poDefn->Dereference() == 0)
1984 0 : delete m_poDefn;
1985 :
1986 0 : m_poDefn = poFeatureDefn;
1987 0 : m_poDefn->Reference();
1988 :
1989 0 : return 0;
1990 : }
|