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