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