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