1 /******************************************************************************
2  *
3  * Project:  OpenGIS Simple Features Reference Implementation
4  * Purpose:  Implements interface to MapInfo .ID files used as attribute
5  *           indexes.
6  * Author:   Frank Warmerdam, warmerdam@pobox.com
7  *
8  ******************************************************************************
9  * Copyright (c) 2003, Frank Warmerdam
10  * Copyright (c) 2008-2010, Even Rouault <even dot rouault at spatialys.com>
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  ****************************************************************************/
30 
31 #include "ogr_attrind.h"
32 #include "mitab/mitab_priv.h"
33 #include "cpl_minixml.h"
34 
35 CPL_CVSID("$Id: ogr_miattrind.cpp b1c9c12ad373e40b955162b45d704070d4ebf7b0 2019-06-19 16:50:15 +0200 Even Rouault $")
36 
37 /************************************************************************/
38 /*                            OGRMIAttrIndex                            */
39 /*                                                                      */
40 /*      MapInfo .ID implementation of access to one fields              */
41 /*      indexing.                                                       */
42 /************************************************************************/
43 
44 class OGRMILayerAttrIndex;
45 
46 class OGRMIAttrIndex : public OGRAttrIndex
47 {
48     CPL_DISALLOW_COPY_ASSIGN(OGRMIAttrIndex)
49 
50 public:
51     int         iIndex;
52     TABINDFile  *poINDFile;
53     OGRMILayerAttrIndex *poLIndex;
54     OGRFieldDefn *poFldDefn;
55 
56     int         iField;
57 
58                 OGRMIAttrIndex( OGRMILayerAttrIndex *, int iIndex, int iField);
59                ~OGRMIAttrIndex();
60 
61     GByte      *BuildKey( OGRField *psKey );
62     GIntBig     GetFirstMatch( OGRField *psKey ) override;
63     GIntBig    *GetAllMatches( OGRField *psKey ) override;
64     GIntBig    *GetAllMatches( OGRField *psKey, GIntBig* panFIDList, int* nFIDCount, int* nLength ) override;
65 
66     OGRErr      AddEntry( OGRField *psKey, GIntBig nFID ) override;
67     OGRErr      RemoveEntry( OGRField *psKey, GIntBig nFID ) override;
68 
69     OGRErr      Clear() override;
70 };
71 
72 /************************************************************************/
73 /* ==================================================================== */
74 /*                         OGRMILayerAttrIndex                          */
75 /*                                                                      */
76 /*      MapInfo .ID specific implementation of a layer attribute        */
77 /*      index.                                                          */
78 /* ==================================================================== */
79 /************************************************************************/
80 
81 class OGRMILayerAttrIndex final: public OGRLayerAttrIndex
82 {
83     CPL_DISALLOW_COPY_ASSIGN(OGRMILayerAttrIndex)
84 
85 public:
86     TABINDFile  *poINDFile;
87 
88     int         nIndexCount;
89     OGRMIAttrIndex **papoIndexList;
90 
91     char        *pszMetadataFilename;
92     char        *pszMIINDFilename;
93 
94     int         bINDAsReadOnly;
95     int         bUnlinkINDFile;
96 
97                 OGRMILayerAttrIndex();
98     virtual     ~OGRMILayerAttrIndex();
99 
100     /* base class virtual methods */
101     OGRErr      Initialize( const char *pszIndexPath, OGRLayer * ) override;
102     OGRErr      CreateIndex( int iField ) override;
103     OGRErr      DropIndex( int iField ) override;
104     OGRErr      IndexAllFeatures( int iField = -1 ) override;
105 
106     OGRErr      AddToIndex( OGRFeature *poFeature, int iField = -1 ) override;
107     OGRErr      RemoveFromIndex( OGRFeature *poFeature ) override;
108 
109     OGRAttrIndex *GetFieldIndex( int iField ) override;
110 
111     /* custom to OGRMILayerAttrIndex */
112     OGRErr      SaveConfigToXML();
113     OGRErr      LoadConfigFromXML();
114     OGRErr      LoadConfigFromXML(const char* pszRawXML);
115     void        AddAttrInd( int iField, int iINDIndex );
116 
GetLayer()117     OGRLayer   *GetLayer() { return poLayer; }
118 };
119 
120 /************************************************************************/
121 /*                        OGRMILayerAttrIndex()                         */
122 /************************************************************************/
123 
OGRMILayerAttrIndex()124 OGRMILayerAttrIndex::OGRMILayerAttrIndex() :
125     poINDFile(nullptr),
126     nIndexCount(0),
127     papoIndexList(nullptr),
128     pszMetadataFilename(nullptr),
129     pszMIINDFilename(nullptr),
130     bINDAsReadOnly(TRUE),
131     bUnlinkINDFile(FALSE)
132 {}
133 
134 /************************************************************************/
135 /*                        ~OGRMILayerAttrIndex()                        */
136 /************************************************************************/
137 
~OGRMILayerAttrIndex()138 OGRMILayerAttrIndex::~OGRMILayerAttrIndex()
139 
140 {
141     if( poINDFile != nullptr )
142     {
143         poINDFile->Close();
144         delete poINDFile;
145         poINDFile = nullptr;
146     }
147 
148     if (bUnlinkINDFile)
149         VSIUnlink( pszMIINDFilename );
150 
151     for( int i = 0; i < nIndexCount; i++ )
152         delete papoIndexList[i];
153     CPLFree( papoIndexList );
154 
155     CPLFree( pszMIINDFilename );
156     CPLFree( pszMetadataFilename );
157 }
158 
159 /************************************************************************/
160 /*                             Initialize()                             */
161 /************************************************************************/
162 
Initialize(const char * pszIndexPathIn,OGRLayer * poLayerIn)163 OGRErr OGRMILayerAttrIndex::Initialize( const char *pszIndexPathIn,
164                                         OGRLayer *poLayerIn )
165 
166 {
167     if( poLayerIn == poLayer )
168         return OGRERR_NONE;
169 
170 /* -------------------------------------------------------------------- */
171 /*      Capture input information and form static pathnames.            */
172 /* -------------------------------------------------------------------- */
173     poLayer = poLayerIn;
174 
175     pszIndexPath = CPLStrdup( pszIndexPathIn );
176 
177     /* try to process the XML string directly */
178     if (STARTS_WITH_CI(pszIndexPathIn, "<OGRMILayerAttrIndex>"))
179         return LoadConfigFromXML(pszIndexPathIn);
180 
181     pszMetadataFilename = CPLStrdup(
182         CPLResetExtension( pszIndexPathIn, "idm" ) );
183 
184     pszMIINDFilename = CPLStrdup(CPLResetExtension( pszIndexPathIn, "ind" ));
185 
186 /* -------------------------------------------------------------------- */
187 /*      If a metadata file already exists, load it.                     */
188 /* -------------------------------------------------------------------- */
189     OGRErr eErr;
190     VSIStatBuf sStat;
191 
192     if( VSIStat( pszMetadataFilename, &sStat ) == 0 )
193     {
194         eErr = LoadConfigFromXML();
195         if( eErr != OGRERR_NONE )
196             return eErr;
197     }
198 
199     return OGRERR_NONE;
200 }
201 
202 /************************************************************************/
203 /*                         LoadConfigFromXML()                          */
204 /************************************************************************/
205 
LoadConfigFromXML(const char * pszRawXML)206 OGRErr OGRMILayerAttrIndex::LoadConfigFromXML(const char* pszRawXML)
207 
208 {
209 /* -------------------------------------------------------------------- */
210 /*      Parse the XML.                                                  */
211 /* -------------------------------------------------------------------- */
212     CPLXMLNode *psRoot = CPLParseXMLString( pszRawXML );
213 
214     if( psRoot == nullptr )
215         return OGRERR_FAILURE;
216 
217 /* -------------------------------------------------------------------- */
218 /*      Open the index file.                                            */
219 /* -------------------------------------------------------------------- */
220     poINDFile = new TABINDFile();
221 
222     if (pszMIINDFilename == nullptr)
223         pszMIINDFilename = CPLStrdup(CPLGetXMLValue(psRoot,"MIIDFilename",""));
224 
225     if( pszMIINDFilename == nullptr )
226         return OGRERR_FAILURE;
227 
228     /* NOTE: Replaced r+ with r according to explanation in Ticket #1620.
229      * This change has to be observed if it doesn't cause any
230      * problems in future. (mloskot)
231      */
232     if( poINDFile->Open( pszMIINDFilename, "r" ) != 0 )
233     {
234         CPLDestroyXMLNode( psRoot );
235         CPLError( CE_Failure, CPLE_OpenFailed,
236                   "Failed to open index file %s.",
237                   pszMIINDFilename );
238         return OGRERR_FAILURE;
239     }
240 /* -------------------------------------------------------------------- */
241 /*      Process each attrindex.                                         */
242 /* -------------------------------------------------------------------- */
243     for( CPLXMLNode *psAttrIndex = psRoot->psChild;
244          psAttrIndex != nullptr;
245          psAttrIndex = psAttrIndex->psNext )
246     {
247         if( psAttrIndex->eType != CXT_Element
248             || !EQUAL(psAttrIndex->pszValue,"OGRMIAttrIndex") )
249             continue;
250 
251         int iField = atoi(CPLGetXMLValue(psAttrIndex,"FieldIndex","-1"));
252         int iIndexIndex = atoi(CPLGetXMLValue(psAttrIndex,"IndexIndex","-1"));
253 
254         if( iField == -1 || iIndexIndex == -1 )
255         {
256             CPLError( CE_Warning, CPLE_AppDefined,
257                       "Skipping corrupt OGRMIAttrIndex entry." );
258             continue;
259         }
260 
261         AddAttrInd( iField, iIndexIndex );
262     }
263 
264     CPLDestroyXMLNode( psRoot );
265 
266     CPLDebug( "OGR", "Restored %d field indexes for layer %s from %s on %s.",
267               nIndexCount, poLayer->GetLayerDefn()->GetName(),
268               pszMetadataFilename ? pszMetadataFilename : "--unknown--",
269               pszMIINDFilename );
270 
271     return OGRERR_NONE;
272 }
273 
LoadConfigFromXML()274 OGRErr OGRMILayerAttrIndex::LoadConfigFromXML()
275 {
276     CPLAssert( poINDFile == nullptr );
277 
278 /* -------------------------------------------------------------------- */
279 /*      Read the XML file.                                              */
280 /* -------------------------------------------------------------------- */
281     VSILFILE *fp = VSIFOpenL( pszMetadataFilename, "rb" );
282     if( fp == nullptr )
283         return OGRERR_FAILURE;
284 
285     if( VSIFSeekL( fp, 0, SEEK_END ) != 0 )
286     {
287         VSIFCloseL(fp);
288         return OGRERR_FAILURE;
289     }
290     const vsi_l_offset nXMLSize = VSIFTellL( fp );
291     if( nXMLSize > 10 * 1024 * 1024 ||
292         VSIFSeekL( fp, 0, SEEK_SET ) != 0 )
293     {
294         VSIFCloseL(fp);
295         return OGRERR_FAILURE;
296     }
297 
298     char *pszRawXML = static_cast<char *>(CPLMalloc(static_cast<size_t>(nXMLSize)+1));
299     pszRawXML[nXMLSize] = '\0';
300     if( VSIFReadL( pszRawXML, static_cast<size_t>(nXMLSize), 1, fp ) != 1 )
301     {
302         VSIFCloseL(fp);
303         return OGRERR_FAILURE;
304     }
305 
306     VSIFCloseL( fp );
307 
308     OGRErr eErr = LoadConfigFromXML(pszRawXML);
309     CPLFree(pszRawXML);
310 
311     return eErr;
312 }
313 
314 /************************************************************************/
315 /*                          SaveConfigToXML()                           */
316 /************************************************************************/
317 
SaveConfigToXML()318 OGRErr OGRMILayerAttrIndex::SaveConfigToXML()
319 
320 {
321     if( nIndexCount == 0 )
322         return OGRERR_NONE;
323 
324 /* -------------------------------------------------------------------- */
325 /*      Create the XML tree corresponding to this layer.                */
326 /* -------------------------------------------------------------------- */
327     CPLXMLNode *psRoot =
328         CPLCreateXMLNode( nullptr, CXT_Element, "OGRMILayerAttrIndex" );
329 
330     CPLCreateXMLElementAndValue( psRoot, "MIIDFilename",
331                                  CPLGetFilename( pszMIINDFilename ) );
332 
333     for( int i = 0; i < nIndexCount; i++ )
334     {
335         OGRMIAttrIndex *poAI = papoIndexList[i];
336         CPLXMLNode *psIndex =
337             CPLCreateXMLNode( psRoot, CXT_Element, "OGRMIAttrIndex" );
338 
339         CPLCreateXMLElementAndValue( psIndex, "FieldIndex",
340                                      CPLSPrintf( "%d", poAI->iField ) );
341 
342         CPLCreateXMLElementAndValue( psIndex, "FieldName",
343                                      poLayer->GetLayerDefn()->GetFieldDefn(poAI->iField)->GetNameRef() );
344 
345         CPLCreateXMLElementAndValue( psIndex, "IndexIndex",
346                                      CPLSPrintf( "%d", poAI->iIndex ) );
347     }
348 
349 /* -------------------------------------------------------------------- */
350 /*      Save it.                                                        */
351 /* -------------------------------------------------------------------- */
352     char *pszRawXML = CPLSerializeXMLTree( psRoot );
353 
354     CPLDestroyXMLNode( psRoot );
355 
356     FILE *fp = VSIFOpen( pszMetadataFilename, "wb" );
357     if( fp == nullptr )
358     {
359         CPLError( CE_Failure, CPLE_OpenFailed,
360                   "Failed to pen `%s' for write.",
361                   pszMetadataFilename );
362         CPLFree( pszRawXML );
363         return OGRERR_FAILURE;
364     }
365 
366     OGRErr eErr = (VSIFWrite( pszRawXML, strlen(pszRawXML), 1, fp ) == 1) ? OGRERR_NONE : OGRERR_FAILURE;
367     VSIFClose( fp );
368 
369     CPLFree( pszRawXML );
370 
371     return eErr;
372 }
373 
374 /************************************************************************/
375 /*                          IndexAllFeatures()                          */
376 /************************************************************************/
377 
IndexAllFeatures(int iField)378 OGRErr OGRMILayerAttrIndex::IndexAllFeatures( int iField )
379 
380 {
381     poLayer->ResetReading();
382 
383     OGRFeature *poFeature = nullptr;
384     while( (poFeature = poLayer->GetNextFeature()) != nullptr )
385     {
386         const OGRErr eErr = AddToIndex( poFeature, iField );
387 
388         delete poFeature;
389 
390         if( eErr != OGRERR_NONE )
391             return eErr;
392     }
393 
394     poLayer->ResetReading();
395 
396     return OGRERR_NONE;
397 }
398 
399 /************************************************************************/
400 /*                            CreateIndex()                             */
401 /*                                                                      */
402 /*      Create an index corresponding to the indicated field, but do    */
403 /*      not populate it.  Use IndexAllFeatures() for that.              */
404 /************************************************************************/
405 
CreateIndex(int iField)406 OGRErr OGRMILayerAttrIndex::CreateIndex( int iField )
407 
408 {
409 /* -------------------------------------------------------------------- */
410 /*      Do we have an open .ID file yet?  If not, create it now.        */
411 /* -------------------------------------------------------------------- */
412     if( poINDFile == nullptr )
413     {
414         poINDFile = new TABINDFile();
415         if( poINDFile->Open( pszMIINDFilename, "w+" ) != 0 )
416         {
417             delete poINDFile;
418             poINDFile = nullptr;
419 
420             CPLError( CE_Failure, CPLE_OpenFailed,
421                       "Failed to create %s.",
422                       pszMIINDFilename );
423             return OGRERR_FAILURE;
424         }
425     }
426     else if (bINDAsReadOnly)
427     {
428         poINDFile->Close();
429         if( poINDFile->Open( pszMIINDFilename, "r+" ) != 0 )
430         {
431             CPLError( CE_Failure, CPLE_OpenFailed,
432                       "Failed to open %s as write-only.",
433                       pszMIINDFilename );
434 
435             if( poINDFile->Open( pszMIINDFilename, "r" ) != 0 )
436             {
437                 CPLError( CE_Failure, CPLE_OpenFailed,
438                       "Cannot re-open %s as read-only.",
439                       pszMIINDFilename );
440                 delete poINDFile;
441                 poINDFile = nullptr;
442             }
443 
444             return OGRERR_FAILURE;
445         }
446         else
447         {
448             bINDAsReadOnly = FALSE;
449         }
450     }
451 
452 /* -------------------------------------------------------------------- */
453 /*      Do we have this field indexed already?                          */
454 /* -------------------------------------------------------------------- */
455     OGRFieldDefn *poFldDefn=poLayer->GetLayerDefn()->GetFieldDefn(iField);
456 
457     for( int i = 0; i < nIndexCount; i++ )
458     {
459         if( papoIndexList[i]->iField == iField )
460         {
461             CPLError( CE_Failure, CPLE_AppDefined,
462                       "It seems we already have an index for field %d/%s\n"
463                       "of layer %s.",
464                       iField, poFldDefn->GetNameRef(),
465                       poLayer->GetLayerDefn()->GetName() );
466             return OGRERR_FAILURE;
467         }
468     }
469 
470 /* -------------------------------------------------------------------- */
471 /*      What is the corresponding field type in TAB?  Note that we      */
472 /*      don't allow indexing of any of the list types.                  */
473 /* -------------------------------------------------------------------- */
474     TABFieldType eTABFT;
475     int           nFieldWidth = 0;
476 
477     switch( poFldDefn->GetType() )
478     {
479       case OFTInteger:
480         eTABFT = TABFInteger;
481         break;
482 
483       case OFTReal:
484         eTABFT = TABFFloat;
485         break;
486 
487       case OFTString:
488         eTABFT = TABFChar;
489         if( poFldDefn->GetWidth() > 0 )
490             nFieldWidth = poFldDefn->GetWidth();
491         else
492             nFieldWidth = 64;
493         break;
494 
495       default:
496         CPLError( CE_Failure, CPLE_AppDefined,
497                   "Indexing not support for the field type of field %s.",
498                   poFldDefn->GetNameRef() );
499         return OGRERR_FAILURE;
500     }
501 
502 /* -------------------------------------------------------------------- */
503 /*      Create the index.                                               */
504 /* -------------------------------------------------------------------- */
505     const int iINDIndex = poINDFile->CreateIndex( eTABFT, nFieldWidth );
506 
507     // CreateIndex() reports its own errors.
508     if( iINDIndex < 0 )
509         return OGRERR_FAILURE;
510 
511     AddAttrInd( iField, iINDIndex );
512 
513     bUnlinkINDFile = FALSE;
514 
515 /* -------------------------------------------------------------------- */
516 /*      Save the new configuration.                                     */
517 /* -------------------------------------------------------------------- */
518     return SaveConfigToXML();
519 }
520 
521 /************************************************************************/
522 /*                             DropIndex()                              */
523 /*                                                                      */
524 /*      For now we don't have any capability to remove index data       */
525 /*      from the MapInfo index file, so we just limit ourselves to      */
526 /*      ignoring it from now on.                                        */
527 /************************************************************************/
528 
DropIndex(int iField)529 OGRErr OGRMILayerAttrIndex::DropIndex( int iField )
530 
531 {
532 /* -------------------------------------------------------------------- */
533 /*      Do we have this field indexed already?                          */
534 /* -------------------------------------------------------------------- */
535     OGRFieldDefn *poFldDefn=poLayer->GetLayerDefn()->GetFieldDefn(iField);
536 
537     int i = 0;
538     for( ; i < nIndexCount; i++ )
539     {
540         if( papoIndexList[i]->iField == iField )
541             break;
542     }
543 
544     if( i == nIndexCount )
545     {
546         CPLError( CE_Failure, CPLE_AppDefined,
547                   "DROP INDEX on field (%s) that doesn't have an index.",
548                   poFldDefn->GetNameRef() );
549         return OGRERR_FAILURE;
550     }
551 
552 /* -------------------------------------------------------------------- */
553 /*      Remove from the list.                                           */
554 /* -------------------------------------------------------------------- */
555     OGRMIAttrIndex *poAI = papoIndexList[i];
556 
557     memmove( papoIndexList + i, papoIndexList + i + 1,
558              sizeof(void*) * (nIndexCount - i - 1) );
559 
560     delete poAI;
561 
562     nIndexCount--;
563 
564 /* -------------------------------------------------------------------- */
565 /*      Save the new configuration, or if there is nothing left try     */
566 /*      to clean up the index files.                                    */
567 /* -------------------------------------------------------------------- */
568 
569     if( nIndexCount > 0 )
570         return SaveConfigToXML();
571     else
572     {
573         bUnlinkINDFile = TRUE;
574         VSIUnlink( pszMetadataFilename );
575 
576         return OGRERR_NONE;
577     }
578 }
579 
580 /************************************************************************/
581 /*                             AddAttrInd()                             */
582 /************************************************************************/
583 
AddAttrInd(int iField,int iINDIndex)584 void OGRMILayerAttrIndex::AddAttrInd( int iField, int iINDIndex )
585 
586 {
587     OGRMIAttrIndex *poAttrInd = new OGRMIAttrIndex( this, iINDIndex, iField);
588 
589     nIndexCount++;
590     papoIndexList = static_cast<OGRMIAttrIndex **>(
591         CPLRealloc(papoIndexList, sizeof(void*) * nIndexCount));
592 
593     papoIndexList[nIndexCount-1] = poAttrInd;
594 }
595 
596 /************************************************************************/
597 /*                         GetFieldAttrIndex()                          */
598 /************************************************************************/
599 
GetFieldIndex(int iField)600 OGRAttrIndex *OGRMILayerAttrIndex::GetFieldIndex( int iField )
601 
602 {
603     for( int i = 0; i < nIndexCount; i++ )
604     {
605         if( papoIndexList[i]->iField == iField )
606             return papoIndexList[i];
607     }
608 
609     return nullptr;
610 }
611 
612 /************************************************************************/
613 /*                             AddToIndex()                             */
614 /************************************************************************/
615 
AddToIndex(OGRFeature * poFeature,int iTargetField)616 OGRErr OGRMILayerAttrIndex::AddToIndex( OGRFeature *poFeature,
617                                         int iTargetField )
618 
619 {
620     OGRErr eErr = OGRERR_NONE;
621 
622     if( poFeature->GetFID() == OGRNullFID )
623     {
624         CPLError( CE_Failure, CPLE_AppDefined,
625                   "Attempt to index feature with no FID." );
626         return OGRERR_FAILURE;
627     }
628 
629     for( int i = 0; i < nIndexCount && eErr == OGRERR_NONE; i++ )
630     {
631         int iField = papoIndexList[i]->iField;
632 
633         if( iTargetField != -1 && iTargetField != iField )
634             continue;
635 
636         if( !poFeature->IsFieldSetAndNotNull( iField ) )
637             continue;
638 
639         eErr =
640             papoIndexList[i]->AddEntry( poFeature->GetRawFieldRef( iField ),
641                                         poFeature->GetFID() );
642     }
643 
644     return eErr;
645 }
646 
647 /************************************************************************/
648 /*                          RemoveFromIndex()                           */
649 /************************************************************************/
650 
RemoveFromIndex(OGRFeature *)651 OGRErr OGRMILayerAttrIndex::RemoveFromIndex( OGRFeature * /*poFeature*/ )
652 
653 {
654     return OGRERR_UNSUPPORTED_OPERATION;
655 }
656 
657 /************************************************************************/
658 /*                     OGRCreateDefaultLayerIndex()                     */
659 /************************************************************************/
660 
OGRCreateDefaultLayerIndex()661 OGRLayerAttrIndex *OGRCreateDefaultLayerIndex()
662 
663 {
664     return new OGRMILayerAttrIndex();
665 }
666 
667 /************************************************************************/
668 /* ==================================================================== */
669 /*                            OGRMIAttrIndex                            */
670 /* ==================================================================== */
671 /************************************************************************/
672 
673 /* class declared at top of file */
674 
675 /************************************************************************/
676 /*                           OGRMIAttrIndex()                           */
677 /************************************************************************/
678 
OGRMIAttrIndex(OGRMILayerAttrIndex * poLayerIndex,int iIndexIn,int iFieldIn)679 OGRMIAttrIndex::OGRMIAttrIndex( OGRMILayerAttrIndex *poLayerIndex,
680                                 int iIndexIn, int iFieldIn ) :
681     iIndex(iIndexIn),
682     poINDFile(poLayerIndex->poINDFile),
683     poLIndex(poLayerIndex),
684     poFldDefn(poLayerIndex->GetLayer()->GetLayerDefn()->GetFieldDefn(iFieldIn)),
685     iField(iFieldIn)
686 {}
687 
688 /************************************************************************/
689 /*                          ~OGRMIAttrIndex()                           */
690 /************************************************************************/
691 
~OGRMIAttrIndex()692 OGRMIAttrIndex::~OGRMIAttrIndex()
693 {
694 }
695 
696 /************************************************************************/
697 /*                              AddEntry()                              */
698 /************************************************************************/
699 
AddEntry(OGRField * psKey,GIntBig nFID)700 OGRErr OGRMIAttrIndex::AddEntry( OGRField *psKey, GIntBig nFID )
701 
702 {
703     if( psKey == nullptr )
704         return OGRERR_FAILURE;
705 
706     if( nFID >= INT_MAX )
707         return OGRERR_FAILURE;
708 
709     GByte *pabyKey = BuildKey( psKey );
710 
711     if( pabyKey == nullptr )
712         return OGRERR_FAILURE;
713 
714     if( poINDFile->AddEntry( iIndex, pabyKey, static_cast<int>(nFID)+1 ) != 0 )
715         return OGRERR_FAILURE;
716     else
717         return OGRERR_NONE;
718 }
719 
720 /************************************************************************/
721 /*                            RemoveEntry()                             */
722 /************************************************************************/
723 
RemoveEntry(OGRField *,GIntBig)724 OGRErr OGRMIAttrIndex::RemoveEntry( OGRField * /*psKey*/, GIntBig /*nFID*/ )
725 
726 {
727     return OGRERR_UNSUPPORTED_OPERATION;
728 }
729 
730 /************************************************************************/
731 /*                              BuildKey()                              */
732 /************************************************************************/
733 
BuildKey(OGRField * psKey)734 GByte *OGRMIAttrIndex::BuildKey( OGRField *psKey )
735 
736 {
737     GByte* ret = nullptr;
738     switch( poFldDefn->GetType() )
739     {
740       case OFTInteger:
741         ret = poINDFile->BuildKey( iIndex, psKey->Integer );
742         break;
743 
744       case OFTInteger64:
745       {
746         if( !CPL_INT64_FITS_ON_INT32(psKey->Integer64) )
747         {
748             CPLError(CE_Warning, CPLE_NotSupported,
749                      "64bit integer value passed to OGRMIAttrIndex::BuildKey()");
750         }
751         ret = poINDFile->BuildKey( iIndex, static_cast<int>(psKey->Integer64) );
752         break;
753       }
754 
755       case OFTReal:
756         ret = poINDFile->BuildKey( iIndex, psKey->Real );
757         break;
758 
759       case OFTString:
760         ret = poINDFile->BuildKey( iIndex, psKey->String );
761         break;
762 
763       default:
764         CPLAssert( false );
765         break;
766     }
767     return ret;
768 }
769 
770 /************************************************************************/
771 /*                           GetFirstMatch()                            */
772 /************************************************************************/
773 
GetFirstMatch(OGRField * psKey)774 GIntBig OGRMIAttrIndex::GetFirstMatch( OGRField *psKey )
775 
776 {
777     GByte *pabyKey = BuildKey( psKey );
778     const GIntBig nFID = poINDFile->FindFirst( iIndex, pabyKey );
779     if( nFID < 1 )
780         return OGRNullFID;
781     else
782         return nFID - 1;
783 }
784 
785 /************************************************************************/
786 /*                           GetAllMatches()                            */
787 /************************************************************************/
788 
GetAllMatches(OGRField * psKey,GIntBig * panFIDList,int * nFIDCount,int * nLength)789 GIntBig *OGRMIAttrIndex::GetAllMatches( OGRField *psKey, GIntBig* panFIDList, int* nFIDCount, int* nLength )
790 {
791     GByte *pabyKey = BuildKey( psKey );
792 
793     if (panFIDList == nullptr)
794     {
795         panFIDList = static_cast<GIntBig *>(CPLMalloc(sizeof(GIntBig) * 2));
796         *nFIDCount = 0;
797         *nLength = 2;
798     }
799 
800     GIntBig nFID = poINDFile->FindFirst( iIndex, pabyKey );
801     while( nFID > 0 )
802     {
803         if( *nFIDCount >= *nLength-1 )
804         {
805             *nLength = (*nLength) * 2 + 10;
806             panFIDList = static_cast<GIntBig *>(CPLRealloc(panFIDList, sizeof(GIntBig)* (*nLength)));
807         }
808         panFIDList[(*nFIDCount)++] = nFID - 1;
809 
810         nFID = poINDFile->FindNext( iIndex, pabyKey );
811     }
812 
813     panFIDList[*nFIDCount] = OGRNullFID;
814 
815     return panFIDList;
816 }
817 
GetAllMatches(OGRField * psKey)818 GIntBig *OGRMIAttrIndex::GetAllMatches( OGRField *psKey )
819 {
820     int nFIDCount, nLength;
821     return GetAllMatches( psKey, nullptr, &nFIDCount, &nLength );
822 }
823 
824 /************************************************************************/
825 /*                               Clear()                                */
826 /************************************************************************/
827 
Clear()828 OGRErr OGRMIAttrIndex::Clear()
829 
830 {
831     return OGRERR_UNSUPPORTED_OPERATION;
832 }
833