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 3 : 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 3 : m_poRelation(nullptr), m_bRelFieldsCreated(FALSE)
53 : {
54 3 : }
55 :
56 : /**********************************************************************
57 : * TABView::~TABView()
58 : *
59 : * Destructor.
60 : **********************************************************************/
61 6 : TABView::~TABView()
62 : {
63 3 : TABView::Close();
64 6 : }
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 1 : void TABView::ResetReading()
76 : {
77 1 : if (m_nMainTableIndex != -1)
78 1 : m_papoTABFiles[m_nMainTableIndex]->ResetReading();
79 1 : }
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 3 : int TABView::Open(const char *pszFname, TABAccess eAccess,
99 : GBool bTestOpenNoError /*= FALSE*/,
100 : const char *pszCharset /* = NULL */)
101 : {
102 3 : char nStatus = 0;
103 :
104 3 : 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 3 : if (eAccess == TABRead)
115 : {
116 3 : m_eAccessMode = TABRead;
117 3 : 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 3 : return nStatus;
134 : }
135 :
136 : /**********************************************************************
137 : * TABView::OpenForRead()
138 : *
139 : * Open for reading
140 : *
141 : * Returns 0 on success, -1 on error.
142 : **********************************************************************/
143 3 : int TABView::OpenForRead(const char *pszFname,
144 : GBool bTestOpenNoError /*= FALSE*/)
145 : {
146 3 : char *pszPath = nullptr;
147 3 : int nFnameLen = 0;
148 :
149 3 : m_eAccessMode = TABRead;
150 :
151 : /*-----------------------------------------------------------------
152 : * Read main .TAB (text) file
153 : *----------------------------------------------------------------*/
154 3 : 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 3 : 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 3 : m_papszTABFile = TAB_CSLLoad(m_pszFname);
170 3 : 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 3 : GBool bCreateViewFound = FALSE;
188 21 : for (int i = 0; !bCreateViewFound && m_papszTABFile[i]; i++)
189 : {
190 18 : const char *pszStr = m_papszTABFile[i];
191 18 : while (*pszStr != '\0' && isspace(static_cast<unsigned char>(*pszStr)))
192 0 : pszStr++;
193 18 : if (STARTS_WITH_CI(pszStr, "create view"))
194 3 : bCreateViewFound = TRUE;
195 : }
196 :
197 3 : 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 3 : pszPath = CPLStrdup(m_pszFname);
219 3 : nFnameLen = static_cast<int>(strlen(pszPath));
220 86 : for (; nFnameLen > 0; nFnameLen--)
221 : {
222 86 : if (pszPath[nFnameLen - 1] == '/' || pszPath[nFnameLen - 1] == '\\')
223 : {
224 : break;
225 : }
226 83 : pszPath[nFnameLen - 1] = '\0';
227 : }
228 :
229 : /*-----------------------------------------------------------------
230 : * Extract the useful info from the TAB header
231 : *----------------------------------------------------------------*/
232 3 : 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 3 : CPLFree(pszPath);
240 3 : 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 3 : 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 3 : m_papoTABFiles =
262 3 : static_cast<TABFile **>(CPLCalloc(m_numTABFiles, sizeof(TABFile *)));
263 :
264 9 : for (int iFile = 0; iFile < m_numTABFiles; iFile++)
265 : {
266 : #ifndef _WIN32
267 6 : TABAdjustFilenameExtension(m_papszTABFnames[iFile]);
268 : #endif
269 :
270 6 : m_papoTABFiles[iFile] = new TABFile(m_poDS);
271 :
272 6 : if (m_papoTABFiles[iFile]->Open(m_papszTABFnames[iFile], m_eAccessMode,
273 6 : 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 3 : m_poRelation = new TABRelation;
288 :
289 3 : CPLAssert(m_nMainTableIndex == 0);
290 3 : CPLAssert(CSLCount(m_papszWhereClause) == 5);
291 3 : char *pszTableName = TABGetBasename(m_pszFname);
292 6 : if (m_poRelation->Init(pszTableName, m_papoTABFiles[0], m_papoTABFiles[1],
293 3 : m_papszWhereClause[4], m_papszWhereClause[2],
294 3 : 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 3 : CPLFree(pszTableName);
302 :
303 3 : 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 3 : int TABView::ParseTABFile(const char *pszDatasetPath,
423 : GBool bTestOpenNoError /*=FALSE*/)
424 : {
425 : int iLine, numLines;
426 3 : char **papszTok = nullptr;
427 3 : GBool bInsideTableDef = FALSE;
428 :
429 3 : 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 3 : numLines = CSLCount(m_papszTABFile);
437 :
438 30 : for (iLine = 0; iLine < numLines; iLine++)
439 : {
440 : /*-------------------------------------------------------------
441 : * Tokenize the next .TAB line, and check first keyword
442 : *------------------------------------------------------------*/
443 27 : CSLDestroy(papszTok);
444 27 : papszTok = CSLTokenizeStringComplex(m_papszTABFile[iLine], " \t(),;",
445 : TRUE, FALSE);
446 27 : if (CSLCount(papszTok) < 2)
447 6 : continue; // All interesting lines have at least 2 tokens
448 :
449 21 : if (EQUAL(papszTok[0], "!version"))
450 : {
451 3 : CPLFree(m_pszVersion);
452 3 : m_pszVersion = CPLStrdup(papszTok[1]);
453 : }
454 18 : else if (EQUAL(papszTok[0], "!charset"))
455 : {
456 0 : CPLFree(m_pszCharset);
457 0 : m_pszCharset = CPLStrdup(papszTok[1]);
458 : }
459 24 : else if (EQUAL(papszTok[0], "open") && EQUAL(papszTok[1], "table") &&
460 6 : CSLCount(papszTok) >= 3)
461 : {
462 : // Source table name may be either "filename" or "filename.tab"
463 6 : int nLen = static_cast<int>(strlen(papszTok[2]));
464 6 : if (nLen > 4 && EQUAL(papszTok[2] + nLen - 4, ".tab"))
465 0 : papszTok[2][nLen - 4] = '\0';
466 :
467 6 : m_papszTABFnames = CSLAppendPrintf(m_papszTABFnames, "%s%s.tab",
468 6 : pszDatasetPath, papszTok[2]);
469 : }
470 12 : else if (EQUAL(papszTok[0], "create") && EQUAL(papszTok[1], "view"))
471 : {
472 3 : bInsideTableDef = TRUE;
473 : }
474 9 : else if (bInsideTableDef && (EQUAL(papszTok[0], "Select")))
475 : {
476 : /*---------------------------------------------------------
477 : * We found the list of table fields (comma-delimited list)
478 : *--------------------------------------------------------*/
479 8 : for (int iTok = 1; papszTok[iTok] != nullptr; iTok++)
480 5 : m_papszFieldNames =
481 8 : CSLAddString(m_papszFieldNames, papszTok[iTok]);
482 : }
483 6 : 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 3 : CSLDestroy(m_papszWhereClause);
493 6 : m_papszWhereClause = CSLTokenizeStringComplex(
494 3 : 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 3 : 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 3 : 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 3 : m_nMainTableIndex = 0;
528 :
529 : /*-----------------------------------------------------------------
530 : * Make sure all required class members are set
531 : *----------------------------------------------------------------*/
532 3 : m_numTABFiles = CSLCount(m_papszTABFnames);
533 :
534 3 : if (m_pszCharset == nullptr)
535 3 : m_pszCharset = CPLStrdup("Neutral");
536 3 : if (m_pszVersion == nullptr)
537 0 : m_pszVersion = CPLStrdup("100");
538 :
539 3 : 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 3 : 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 3 : 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 : const OGRFeatureDefn *poDefn = GetLayerDefn();
593 0 : for (int iField = 0; iField < poDefn->GetFieldCount(); iField++)
594 : {
595 0 : const 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 3 : int TABView::Close()
636 : {
637 : // In write access, the main .TAB file has not been written yet.
638 3 : if (m_eAccessMode == TABWrite && m_poRelation)
639 0 : WriteTABFile();
640 :
641 9 : for (int i = 0; m_papoTABFiles && i < m_numTABFiles; i++)
642 : {
643 6 : if (m_papoTABFiles[i])
644 6 : delete m_papoTABFiles[i]; // Automatically closes.
645 : }
646 3 : CPLFree(m_papoTABFiles);
647 3 : m_papoTABFiles = nullptr;
648 3 : 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 3 : 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 3 : CPLFree(m_pszFname);
672 3 : m_pszFname = nullptr;
673 :
674 3 : CSLDestroy(m_papszTABFile);
675 3 : m_papszTABFile = nullptr;
676 :
677 3 : CPLFree(m_pszVersion);
678 3 : m_pszVersion = nullptr;
679 3 : CPLFree(m_pszCharset);
680 3 : m_pszCharset = nullptr;
681 :
682 3 : CSLDestroy(m_papszTABFnames);
683 3 : m_papszTABFnames = nullptr;
684 :
685 3 : CSLDestroy(m_papszFieldNames);
686 3 : m_papszFieldNames = nullptr;
687 3 : CSLDestroy(m_papszWhereClause);
688 3 : m_papszWhereClause = nullptr;
689 :
690 3 : m_nMainTableIndex = -1;
691 :
692 3 : if (m_poRelation)
693 3 : delete m_poRelation;
694 3 : m_poRelation = nullptr;
695 :
696 3 : m_bRelFieldsCreated = FALSE;
697 :
698 3 : 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 3 : GIntBig TABView::GetNextFeatureId(GIntBig nPrevId)
748 : {
749 3 : if (m_nMainTableIndex != -1)
750 3 : 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 3 : TABFeature *TABView::GetFeatureRef(GIntBig nFeatureId)
770 : {
771 :
772 : /*-----------------------------------------------------------------
773 : * Make sure file is open.
774 : *----------------------------------------------------------------*/
775 3 : 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 3 : if (!CPL_INT64_FITS_ON_INT32(nFeatureId))
783 0 : return nullptr;
784 :
785 3 : if (m_poCurFeature)
786 : {
787 0 : delete m_poCurFeature;
788 0 : m_poCurFeature = nullptr;
789 : }
790 :
791 3 : m_poCurFeature = m_poRelation->GetFeature(static_cast<int>(nFeatureId));
792 3 : m_nCurFeatureId = nFeatureId;
793 3 : if (m_poCurFeature)
794 : {
795 2 : m_poCurFeature->SetFID(m_nCurFeatureId);
796 : }
797 3 : 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() const
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 5 : const OGRFeatureDefn *TABView::GetLayerDefn() const
858 : {
859 5 : if (m_poRelation)
860 5 : 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 : const OGRSpatialReference *TABView::GetSpatialRef() const
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) const
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 3 : 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 3 : m_poDefn(nullptr)
1194 : {
1195 3 : }
1196 :
1197 : /**********************************************************************
1198 : * TABRelation::~TABRelation()
1199 : *
1200 : * Destructor.
1201 : **********************************************************************/
1202 6 : TABRelation::~TABRelation()
1203 : {
1204 3 : ResetAllMembers();
1205 3 : }
1206 :
1207 : /**********************************************************************
1208 : * TABRelation::ResetAllMembers()
1209 : *
1210 : * Reset all class members.
1211 : **********************************************************************/
1212 6 : void TABRelation::ResetAllMembers()
1213 : {
1214 6 : m_poMainTable = nullptr;
1215 6 : CPLFree(m_pszMainFieldName);
1216 6 : m_pszMainFieldName = nullptr;
1217 6 : m_nMainFieldNo = -1;
1218 :
1219 6 : m_poRelTable = nullptr;
1220 6 : CPLFree(m_pszRelFieldName);
1221 6 : m_pszRelFieldName = nullptr;
1222 6 : m_nRelFieldNo = -1;
1223 6 : m_nRelFieldIndexNo = -1;
1224 :
1225 6 : m_nUniqueRecordNo = 0;
1226 :
1227 : // No need to close m_poRelINDFileRef since we only got a ref. to it
1228 6 : m_poRelINDFileRef = nullptr;
1229 :
1230 6 : CPLFree(m_panMainTableFieldMap);
1231 6 : m_panMainTableFieldMap = nullptr;
1232 6 : CPLFree(m_panRelTableFieldMap);
1233 6 : m_panRelTableFieldMap = nullptr;
1234 :
1235 : /*-----------------------------------------------------------------
1236 : * Note: we have to check the reference count before deleting m_poDefn
1237 : *----------------------------------------------------------------*/
1238 6 : if (m_poDefn)
1239 3 : m_poDefn->Release();
1240 6 : m_poDefn = nullptr;
1241 6 : }
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 3 : int TABRelation::Init(const char *pszViewName, TABFile *poMainTable,
1259 : TABFile *poRelTable, const char *pszMainFieldName,
1260 : const char *pszRelFieldName, char **papszSelectedFields)
1261 : {
1262 3 : if (poMainTable == nullptr || poRelTable == nullptr)
1263 0 : return -1;
1264 :
1265 : // We'll need the feature Defn later...
1266 3 : const OGRFeatureDefn *poMainDefn = poMainTable->GetLayerDefn();
1267 3 : const OGRFeatureDefn *poRelDefn = poRelTable->GetLayerDefn();
1268 :
1269 : /*-----------------------------------------------------------------
1270 : * Keep info for later use about source tables, etc.
1271 : *----------------------------------------------------------------*/
1272 3 : ResetAllMembers();
1273 :
1274 3 : m_poMainTable = poMainTable;
1275 3 : if (pszMainFieldName)
1276 : {
1277 3 : m_pszMainFieldName = CPLStrdup(pszMainFieldName);
1278 3 : m_nMainFieldNo = poMainDefn->GetFieldIndex(pszMainFieldName);
1279 : }
1280 :
1281 3 : m_poRelTable = poRelTable;
1282 3 : if (pszRelFieldName)
1283 : {
1284 3 : m_pszRelFieldName = CPLStrdup(pszRelFieldName);
1285 3 : m_nRelFieldNo = poRelDefn->GetFieldIndex(pszRelFieldName);
1286 3 : m_nRelFieldIndexNo = poRelTable->GetFieldIndexNumber(m_nRelFieldNo);
1287 3 : m_poRelINDFileRef = poRelTable->GetINDFileRef();
1288 :
1289 3 : 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 3 : const int numFields1 = poMainDefn ? poMainDefn->GetFieldCount() : 0;
1304 3 : const int numFields2 = poRelDefn ? poRelDefn->GetFieldCount() : 0;
1305 :
1306 3 : m_panMainTableFieldMap =
1307 3 : static_cast<int *>(CPLMalloc((numFields1 + 1) * sizeof(int)));
1308 9 : for (int i = 0; i < numFields1; i++)
1309 6 : m_panMainTableFieldMap[i] = -1;
1310 3 : m_panRelTableFieldMap =
1311 3 : static_cast<int *>(CPLMalloc((numFields2 + 1) * sizeof(int)));
1312 9 : for (int i = 0; i < numFields2; i++)
1313 6 : m_panRelTableFieldMap[i] = -1;
1314 :
1315 : /*-----------------------------------------------------------------
1316 : * If selectedFields = "*" then select all fields from both tables
1317 : *----------------------------------------------------------------*/
1318 3 : papszSelectedFields = CSLDuplicate(papszSelectedFields);
1319 3 : if (papszSelectedFields != nullptr && papszSelectedFields[0] != nullptr &&
1320 3 : 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 : const 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 : const 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 3 : const OGRFieldDefn *poFieldDefn = nullptr;
1351 :
1352 3 : m_poDefn = new OGRFeatureDefn(pszViewName);
1353 : // Ref count defaults to 0... set it to 1
1354 3 : m_poDefn->Reference();
1355 :
1356 10 : for (int i = 0;
1357 10 : papszSelectedFields != nullptr && papszSelectedFields[i] != nullptr;
1358 : i++)
1359 : {
1360 : int nIndex;
1361 14 : if (poMainDefn &&
1362 7 : (nIndex = poMainDefn->GetFieldIndex(papszSelectedFields[i])) >= 0)
1363 : {
1364 : /* Field from the main table
1365 : */
1366 4 : poFieldDefn = poMainDefn->GetFieldDefn(nIndex);
1367 4 : m_poDefn->AddFieldDefn(poFieldDefn);
1368 4 : m_panMainTableFieldMap[nIndex] = m_poDefn->GetFieldCount() - 1;
1369 : }
1370 6 : else if (poRelDefn && (nIndex = poRelDefn->GetFieldIndex(
1371 3 : papszSelectedFields[i])) >= 0)
1372 : {
1373 : /* Field from the related table
1374 : */
1375 3 : poFieldDefn = poRelDefn->GetFieldDefn(nIndex);
1376 3 : m_poDefn->AddFieldDefn(poFieldDefn);
1377 3 : 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 3 : CSLDestroy(papszSelectedFields);
1391 3 : 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 : const OGRFeatureDefn *poMainDefn = m_poMainTable->GetLayerDefn();
1445 0 : const 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 3 : TABFeature *TABRelation::GetFeature(int nFeatureId)
1484 : {
1485 : /*-----------------------------------------------------------------
1486 : * Make sure init() has been called
1487 : *----------------------------------------------------------------*/
1488 3 : 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 3 : TABFeature *poMainFeature = m_poMainTable->GetFeatureRef(nFeatureId);
1499 3 : 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 : auto poCurFeature =
1507 6 : std::unique_ptr<TABFeature>(poMainFeature->CloneTABFeature(m_poDefn));
1508 :
1509 : /*-----------------------------------------------------------------
1510 : * Keep track of FID and copy the geometry
1511 : *----------------------------------------------------------------*/
1512 3 : poCurFeature->SetFID(nFeatureId);
1513 :
1514 3 : if (poCurFeature->GetFeatureClass() != TABFCNoGeomFeature)
1515 : {
1516 3 : OGRGeometry *poGeom = poMainFeature->GetGeometryRef();
1517 3 : poCurFeature->SetGeometry(poGeom);
1518 : }
1519 :
1520 : /*-----------------------------------------------------------------
1521 : * Fetch feature from related table
1522 : *
1523 : * __TODO__ Right now we support only many-to-1 relationships, but
1524 : * it might be possible to have several related entries
1525 : * for a single key, and in this case we should return
1526 : * one new feature for each of them.
1527 : *----------------------------------------------------------------*/
1528 3 : TABFeature *poRelFeature = nullptr;
1529 3 : if (m_poRelINDFileRef)
1530 : {
1531 : GByte *pKey =
1532 3 : BuildFieldKey(poMainFeature, m_nMainFieldNo,
1533 3 : m_poMainTable->GetNativeFieldType(m_nMainFieldNo),
1534 : m_nRelFieldIndexNo);
1535 3 : if (!pKey)
1536 1 : return nullptr;
1537 :
1538 : int nRelFeatureId =
1539 2 : m_poRelINDFileRef->FindFirst(m_nRelFieldIndexNo, pKey);
1540 :
1541 2 : if (nRelFeatureId > 0)
1542 2 : poRelFeature = m_poRelTable->GetFeatureRef(nRelFeatureId);
1543 : }
1544 :
1545 : /*-----------------------------------------------------------------
1546 : * Copy fields from poMainFeature
1547 : *----------------------------------------------------------------*/
1548 6 : for (int i = 0; i < poMainFeature->GetFieldCount(); i++)
1549 : {
1550 4 : if (m_panMainTableFieldMap[i] != -1)
1551 : {
1552 6 : poCurFeature->SetField(m_panMainTableFieldMap[i],
1553 3 : poMainFeature->GetRawFieldRef(i));
1554 : }
1555 : }
1556 :
1557 : /*-----------------------------------------------------------------
1558 : * Copy fields from poRelFeature...
1559 : *
1560 : * NOTE: For now, if no corresponding feature is found in RelTable
1561 : * then we will just leave the corresponding fields unset.
1562 : *----------------------------------------------------------------*/
1563 6 : for (int i = 0; poRelFeature && i < poRelFeature->GetFieldCount(); i++)
1564 : {
1565 4 : if (m_panRelTableFieldMap[i] != -1)
1566 : {
1567 4 : poCurFeature->SetField(m_panRelTableFieldMap[i],
1568 2 : poRelFeature->GetRawFieldRef(i));
1569 : }
1570 : }
1571 :
1572 2 : return poCurFeature.release();
1573 : }
1574 :
1575 : /**********************************************************************
1576 : * TABRelation::BuildFieldKey()
1577 : *
1578 : * Return the index key for the specified field in poFeature.
1579 : * Simply maps the call to the proper method in the TABINDFile class.
1580 : *
1581 : * Returns a reference to a TABINDFile internal buffer that should not
1582 : * be freed by the caller.
1583 : **********************************************************************/
1584 3 : GByte *TABRelation::BuildFieldKey(TABFeature *poFeature, int nFieldNo,
1585 : TABFieldType eType, int nIndexNo)
1586 : {
1587 3 : GByte *pKey = nullptr;
1588 :
1589 3 : switch (eType)
1590 : {
1591 0 : case TABFChar:
1592 0 : pKey = m_poRelINDFileRef->BuildKey(
1593 : nIndexNo, poFeature->GetFieldAsString(nFieldNo));
1594 0 : break;
1595 :
1596 1 : case TABFDecimal:
1597 : case TABFFloat:
1598 1 : pKey = m_poRelINDFileRef->BuildKey(
1599 : nIndexNo, poFeature->GetFieldAsDouble(nFieldNo));
1600 1 : break;
1601 :
1602 : // __TODO__ DateTime fields are 8 bytes long, not supported yet by
1603 : // the indexing code (see bug #1844).
1604 0 : case TABFDateTime:
1605 0 : CPLError(
1606 : CE_Failure, CPLE_NotSupported,
1607 : "TABRelation on field of type DateTime not supported yet.");
1608 0 : break;
1609 :
1610 2 : case TABFInteger:
1611 : case TABFSmallInt:
1612 : case TABFDate:
1613 : case TABFTime:
1614 : case TABFLogical:
1615 : default:
1616 2 : pKey = m_poRelINDFileRef->BuildKey(
1617 : nIndexNo, poFeature->GetFieldAsInteger(nFieldNo));
1618 2 : break;
1619 : }
1620 :
1621 3 : return pKey;
1622 : }
1623 :
1624 : /**********************************************************************
1625 : * TABRelation::GetNativeFieldType()
1626 : *
1627 : * Returns the native MapInfo field type for the specified field.
1628 : *
1629 : * Returns TABFUnknown if file is not opened, or if specified field index is
1630 : * invalid.
1631 : *
1632 : * Note that field ids are positive and start at 0.
1633 : **********************************************************************/
1634 0 : TABFieldType TABRelation::GetNativeFieldType(int nFieldId)
1635 : {
1636 : int i, numFields;
1637 :
1638 0 : if (m_poMainTable == nullptr || m_poRelTable == nullptr ||
1639 0 : m_panMainTableFieldMap == nullptr || m_panRelTableFieldMap == nullptr)
1640 0 : return TABFUnknown;
1641 :
1642 : /*-----------------------------------------------------------------
1643 : * Look for nFieldId in the field maps and call the corresponding
1644 : * TAB file's GetNativeFieldType()
1645 : *----------------------------------------------------------------*/
1646 0 : numFields = m_poMainTable->GetLayerDefn()->GetFieldCount();
1647 0 : for (i = 0; i < numFields; i++)
1648 : {
1649 0 : if (m_panMainTableFieldMap[i] == nFieldId)
1650 : {
1651 0 : return m_poMainTable->GetNativeFieldType(i);
1652 : }
1653 : }
1654 :
1655 0 : numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
1656 0 : for (i = 0; i < numFields; i++)
1657 : {
1658 0 : if (m_panRelTableFieldMap[i] == nFieldId)
1659 : {
1660 0 : return m_poRelTable->GetNativeFieldType(i);
1661 : }
1662 : }
1663 :
1664 0 : return TABFUnknown;
1665 : }
1666 :
1667 : /**********************************************************************
1668 : * TABRelation::AddFieldNative()
1669 : *
1670 : * Create a new field using a native mapinfo data type... this is an
1671 : * alternative to defining fields through the OGR interface.
1672 : * This function should be called after creating a new dataset, but before
1673 : * writing the first feature.
1674 : *
1675 : * This function will build/update the OGRFeatureDefn that will have to be
1676 : * used when writing features to this dataset.
1677 : *
1678 : * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn().
1679 : *
1680 : * Returns 0 on success, -1 on error.
1681 : **********************************************************************/
1682 0 : int TABRelation::AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
1683 : int nWidth /*=0*/, int nPrecision /*=0*/,
1684 : GBool bIndexed /*=FALSE*/,
1685 : GBool bUnique /*=FALSE*/, int bApproxOK)
1686 : {
1687 0 : if (m_poMainTable == nullptr || m_poRelTable == nullptr ||
1688 0 : m_panMainTableFieldMap == nullptr || m_panRelTableFieldMap == nullptr)
1689 0 : return -1;
1690 :
1691 0 : if (!bUnique)
1692 : {
1693 : /*-------------------------------------------------------------
1694 : * Add field to poMainTable and to m_poDefn
1695 : *------------------------------------------------------------*/
1696 0 : if (m_poMainTable->AddFieldNative(pszName, eMapInfoType, nWidth,
1697 : nPrecision, bIndexed, bUnique,
1698 0 : bApproxOK) != 0)
1699 0 : return -1;
1700 :
1701 0 : const OGRFeatureDefn *poMainDefn = m_poMainTable->GetLayerDefn();
1702 :
1703 0 : m_panMainTableFieldMap = static_cast<int *>(CPLRealloc(
1704 0 : m_panMainTableFieldMap, poMainDefn->GetFieldCount() * sizeof(int)));
1705 :
1706 0 : m_poDefn->AddFieldDefn(
1707 0 : poMainDefn->GetFieldDefn(poMainDefn->GetFieldCount() - 1));
1708 :
1709 0 : m_panMainTableFieldMap[poMainDefn->GetFieldCount() - 1] =
1710 0 : m_poDefn->GetFieldCount() - 1;
1711 : }
1712 : else
1713 : {
1714 : /*-------------------------------------------------------------
1715 : * Add field to poRelTable and to m_poDefn
1716 : *------------------------------------------------------------*/
1717 0 : if (m_poRelTable->AddFieldNative(pszName, eMapInfoType, nWidth,
1718 : nPrecision, bIndexed, bUnique,
1719 0 : bApproxOK) != 0)
1720 0 : return -1;
1721 :
1722 0 : const OGRFeatureDefn *poRelDefn = m_poRelTable->GetLayerDefn();
1723 :
1724 0 : m_panRelTableFieldMap = static_cast<int *>(CPLRealloc(
1725 0 : m_panRelTableFieldMap, poRelDefn->GetFieldCount() * sizeof(int)));
1726 :
1727 0 : m_poDefn->AddFieldDefn(
1728 0 : poRelDefn->GetFieldDefn(poRelDefn->GetFieldCount() - 1));
1729 :
1730 0 : m_panRelTableFieldMap[poRelDefn->GetFieldCount() - 1] =
1731 0 : m_poDefn->GetFieldCount() - 1;
1732 :
1733 : // The first field in this table must be indexed.
1734 0 : if (poRelDefn->GetFieldCount() == 1)
1735 0 : m_poRelTable->SetFieldIndexed(0);
1736 : }
1737 :
1738 0 : return 0;
1739 : }
1740 :
1741 : /**********************************************************************
1742 : * TABRelation::IsFieldIndexed()
1743 : *
1744 : * Returns TRUE is specified field is indexed.
1745 : *
1746 : * Note that field ids are positive and start at 0.
1747 : **********************************************************************/
1748 0 : GBool TABRelation::IsFieldIndexed(int nFieldId)
1749 : {
1750 : int i, numFields;
1751 :
1752 0 : if (m_poMainTable == nullptr || m_poRelTable == nullptr ||
1753 0 : m_panMainTableFieldMap == nullptr || m_panRelTableFieldMap == nullptr)
1754 0 : return FALSE;
1755 :
1756 : /*-----------------------------------------------------------------
1757 : * Look for nFieldId in the field maps and call the corresponding
1758 : * TAB file's GetNativeFieldType()
1759 : *----------------------------------------------------------------*/
1760 0 : numFields = m_poMainTable->GetLayerDefn()->GetFieldCount();
1761 0 : for (i = 0; i < numFields; i++)
1762 : {
1763 0 : if (m_panMainTableFieldMap[i] == nFieldId)
1764 : {
1765 0 : return m_poMainTable->IsFieldIndexed(i);
1766 : }
1767 : }
1768 :
1769 0 : numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
1770 0 : for (i = 0; i < numFields; i++)
1771 : {
1772 0 : if (m_panRelTableFieldMap[i] == nFieldId)
1773 : {
1774 0 : return m_poRelTable->IsFieldIndexed(i);
1775 : }
1776 : }
1777 :
1778 0 : return FALSE;
1779 : }
1780 :
1781 : /**********************************************************************
1782 : * TABRelation::SetFieldIndexed()
1783 : *
1784 : * Request that the specified field be indexed. This will create the .IND
1785 : * file, etc.
1786 : *
1787 : * Note that field ids are positive and start at 0.
1788 : *
1789 : * Returns 0 on success, -1 on error.
1790 : **********************************************************************/
1791 0 : int TABRelation::SetFieldIndexed(int nFieldId)
1792 : {
1793 : int i, numFields;
1794 :
1795 0 : if (m_poMainTable == nullptr || m_poRelTable == nullptr ||
1796 0 : m_panMainTableFieldMap == nullptr || m_panRelTableFieldMap == nullptr)
1797 0 : return -1;
1798 :
1799 : /*-----------------------------------------------------------------
1800 : * Look for nFieldId in the field maps and call the corresponding
1801 : * TAB file's GetNativeFieldType()
1802 : *----------------------------------------------------------------*/
1803 0 : numFields = m_poMainTable->GetLayerDefn()->GetFieldCount();
1804 0 : for (i = 0; i < numFields; i++)
1805 : {
1806 0 : if (m_panMainTableFieldMap[i] == nFieldId)
1807 : {
1808 0 : return m_poMainTable->SetFieldIndexed(i);
1809 : }
1810 : }
1811 :
1812 0 : numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
1813 0 : for (i = 0; i < numFields; i++)
1814 : {
1815 0 : if (m_panRelTableFieldMap[i] == nFieldId)
1816 : {
1817 0 : return m_poRelTable->SetFieldIndexed(i);
1818 : }
1819 : }
1820 :
1821 0 : return -1;
1822 : }
1823 :
1824 : /**********************************************************************
1825 : * TABRelation::IsFieldUnique()
1826 : *
1827 : * Returns TRUE is specified field is part of the unique table (poRelTable).
1828 : *
1829 : * Note that field ids are positive and start at 0.
1830 : **********************************************************************/
1831 0 : GBool TABRelation::IsFieldUnique(int nFieldId)
1832 : {
1833 : int i, numFields;
1834 :
1835 0 : if (m_poMainTable == nullptr || m_poRelTable == nullptr ||
1836 0 : m_panMainTableFieldMap == nullptr || m_panRelTableFieldMap == nullptr)
1837 0 : return FALSE;
1838 :
1839 : /*-----------------------------------------------------------------
1840 : * Look for nFieldId in the poRelTable field map
1841 : *----------------------------------------------------------------*/
1842 0 : numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
1843 0 : for (i = 0; i < numFields; i++)
1844 : {
1845 0 : if (m_panRelTableFieldMap[i] == nFieldId)
1846 : {
1847 0 : return TRUE; // If it is here then it is unique!
1848 : }
1849 : }
1850 :
1851 0 : return FALSE;
1852 : }
1853 :
1854 : /**********************************************************************
1855 : * TABRelation::WriteFeature()
1856 : *
1857 : * Write a feature to this dataset.
1858 : *
1859 : * For now only sequential writes are supported (i.e. with nFeatureId=-1)
1860 : * but eventually we should be able to do random access by specifying
1861 : * a value through nFeatureId.
1862 : *
1863 : * Returns the new featureId (> 0) on success, or -1 if an
1864 : * error happened in which case, CPLError() will have been called to
1865 : * report the reason of the failure.
1866 : **********************************************************************/
1867 0 : int TABRelation::WriteFeature(TABFeature *poFeature, int nFeatureId /*=-1*/)
1868 : {
1869 0 : TABFeature *poMainFeature = nullptr;
1870 :
1871 0 : if (nFeatureId != -1)
1872 : {
1873 0 : CPLError(CE_Failure, CPLE_NotSupported,
1874 : "WriteFeature(): random access not implemented yet.");
1875 0 : return -1;
1876 : }
1877 :
1878 0 : CPLAssert(m_poMainTable && m_poRelTable);
1879 :
1880 : // We'll need the feature Defn later...
1881 0 : const OGRFeatureDefn *poMainDefn = m_poMainTable->GetLayerDefn();
1882 0 : const OGRFeatureDefn *poRelDefn = m_poRelTable->GetLayerDefn();
1883 :
1884 : /*-----------------------------------------------------------------
1885 : * Create one feature for each table
1886 : * Copy the geometry only to the feature from the main table
1887 : *----------------------------------------------------------------*/
1888 0 : poMainFeature = poFeature->CloneTABFeature(poMainDefn);
1889 :
1890 0 : if (poFeature->GetFeatureClass() != TABFCNoGeomFeature)
1891 : {
1892 0 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1893 0 : poMainFeature->SetGeometry(poGeom);
1894 : }
1895 :
1896 : /*-----------------------------------------------------------------
1897 : * Copy fields to poMainFeature
1898 : *----------------------------------------------------------------*/
1899 0 : for (int i = 0; i < poMainDefn->GetFieldCount(); i++)
1900 : {
1901 0 : if (m_panMainTableFieldMap[i] != -1)
1902 : {
1903 0 : poMainFeature->SetField(
1904 0 : i, poFeature->GetRawFieldRef(m_panMainTableFieldMap[i]));
1905 : }
1906 : }
1907 :
1908 : /*-----------------------------------------------------------------
1909 : * Look for a record id for the unique fields, and write a new
1910 : * record if necessary
1911 : *----------------------------------------------------------------*/
1912 0 : int nRecordNo = 0;
1913 0 : int nUniqueIndexNo = -1;
1914 0 : if (m_panMainTableFieldMap[0] != -1)
1915 0 : nUniqueIndexNo = m_poRelTable->GetFieldIndexNumber(0);
1916 :
1917 0 : if (nUniqueIndexNo > 0)
1918 : {
1919 0 : GByte *pKey = BuildFieldKey(
1920 0 : poFeature, 0, m_poRelTable->GetNativeFieldType(0), nUniqueIndexNo);
1921 :
1922 0 : if (!pKey || (nRecordNo = m_poRelINDFileRef->FindFirst(nUniqueIndexNo,
1923 : pKey)) == -1)
1924 0 : return -1;
1925 :
1926 0 : if (nRecordNo == 0)
1927 : {
1928 : /*---------------------------------------------------------
1929 : * No record in poRelTable yet for this unique value...
1930 : * add one now...
1931 : *--------------------------------------------------------*/
1932 0 : TABFeature *poRelFeature = new TABFeature(poRelDefn);
1933 :
1934 0 : for (int i = 0; i < poRelDefn->GetFieldCount(); i++)
1935 : {
1936 0 : if (m_panRelTableFieldMap[i] != -1)
1937 : {
1938 0 : poRelFeature->SetField(
1939 0 : i, poFeature->GetRawFieldRef(m_panRelTableFieldMap[i]));
1940 : }
1941 : }
1942 :
1943 0 : nRecordNo = ++m_nUniqueRecordNo;
1944 :
1945 0 : poRelFeature->SetField(m_nRelFieldNo, nRecordNo);
1946 :
1947 0 : if (m_poRelTable->CreateFeature(poRelFeature) == OGRERR_NONE)
1948 0 : return -1;
1949 :
1950 0 : delete poRelFeature;
1951 : }
1952 : }
1953 :
1954 : /*-----------------------------------------------------------------
1955 : * Write poMainFeature to the main table
1956 : *----------------------------------------------------------------*/
1957 0 : poMainFeature->SetField(m_nMainFieldNo, nRecordNo);
1958 :
1959 0 : if (m_poMainTable->CreateFeature(poMainFeature) != OGRERR_NONE)
1960 0 : nFeatureId = static_cast<int>(poMainFeature->GetFID());
1961 : else
1962 0 : nFeatureId = -1;
1963 :
1964 0 : delete poMainFeature;
1965 :
1966 0 : return nFeatureId;
1967 : }
1968 :
1969 : /**********************************************************************
1970 : * TABFile::SetFeatureDefn()
1971 : *
1972 : * NOT FULLY IMPLEMENTED YET...
1973 : *
1974 : * Returns 0 on success, -1 on error.
1975 : **********************************************************************/
1976 0 : int TABRelation::SetFeatureDefn(
1977 : OGRFeatureDefn *poFeatureDefn,
1978 : CPL_UNUSED TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */)
1979 : {
1980 0 : if (m_poDefn && m_poDefn->GetFieldCount() > 0)
1981 : {
1982 0 : CPLAssert(m_poDefn == nullptr);
1983 0 : return -1;
1984 : }
1985 :
1986 : /*-----------------------------------------------------------------
1987 : * Keep a reference to the OGRFeatureDefn... we'll have to take the
1988 : * reference count into account when we are done with it.
1989 : *----------------------------------------------------------------*/
1990 0 : if (m_poDefn)
1991 0 : m_poDefn->Release();
1992 0 : m_poDefn = poFeatureDefn;
1993 0 : m_poDefn->Reference();
1994 :
1995 0 : return 0;
1996 : }
|