EVOLUTION-MANAGER
Edit File: filegdbindex.cpp
/****************************************************************************** * $Id: filegdbindex.cpp 27751 2014-09-28 16:33:48Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Implements reading of FileGDB indexes * Author: Even Rouault, <even dot rouault at mines-dash paris dot org> * ****************************************************************************** * Copyright (c) 2014, Even Rouault <even dot rouault at mines-paris dot org> * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************/ #include "filegdbtable_priv.h" #include "cpl_string.h" #include "cpl_time.h" #include <algorithm> CPL_CVSID("$Id"); namespace OpenFileGDB { /************************************************************************/ /* FileGDBOGRDateToDoubleDate() */ /************************************************************************/ static int FileGDBOGRDateToDoubleDate( const OGRField* psField, double *pdfVal ) { struct tm brokendowntime; brokendowntime.tm_year = psField->Date.Year - 1900; brokendowntime.tm_mon = psField->Date.Month - 1; brokendowntime.tm_mday = psField->Date.Day; brokendowntime.tm_hour = psField->Date.Hour; brokendowntime.tm_min = psField->Date.Minute; brokendowntime.tm_sec = psField->Date.Second; GIntBig nTime = CPLYMDHMSToUnixTime(&brokendowntime); *pdfVal = nTime / 3600. / 24 + 25569; return TRUE; } /************************************************************************/ /* FileGDBTrivialIterator */ /************************************************************************/ class FileGDBTrivialIterator : public FileGDBIterator { FileGDBIterator *poParentIter; FileGDBTable *poTable; int iRow; public: FileGDBTrivialIterator(FileGDBIterator *poParentIter); virtual ~FileGDBTrivialIterator() { delete poParentIter; } virtual FileGDBTable *GetTable() { return poTable; } virtual void Reset() { iRow = 0; poParentIter->Reset(); } virtual int GetNextRowSortedByFID(); virtual int GetRowCount() { return poTable->GetTotalRecordCount(); } virtual int GetNextRowSortedByValue() { return poParentIter->GetNextRowSortedByValue(); } virtual const OGRField* GetMinValue(int& eOutType) { return poParentIter->GetMinValue(eOutType); } virtual const OGRField* GetMaxValue(int& eOutType) { return poParentIter->GetMaxValue(eOutType); } virtual int GetMinMaxSumCount(double& dfMin, double& dfMax, double& dfSum, int& nCount) { return poParentIter->GetMinMaxSumCount(dfMin, dfMax, dfSum, nCount); } }; /************************************************************************/ /* FileGDBNotIterator */ /************************************************************************/ class FileGDBNotIterator : public FileGDBIterator { FileGDBIterator *poIterBase; FileGDBTable *poTable; int iRow; int iNextRowBase; int bNoHoles; public: FileGDBNotIterator(FileGDBIterator* poIterBase); virtual ~FileGDBNotIterator(); virtual FileGDBTable *GetTable() { return poTable; } virtual void Reset(); virtual int GetNextRowSortedByFID(); virtual int GetRowCount(); }; /************************************************************************/ /* FileGDBAndIterator */ /************************************************************************/ class FileGDBAndIterator : public FileGDBIterator { FileGDBIterator *poIter1; FileGDBIterator *poIter2; int iNextRow1; int iNextRow2; public: FileGDBAndIterator(FileGDBIterator* poIter1, FileGDBIterator* poIter2); virtual ~FileGDBAndIterator(); virtual FileGDBTable *GetTable() { return poIter1->GetTable(); } virtual void Reset(); virtual int GetNextRowSortedByFID(); }; /************************************************************************/ /* FileGDBOrIterator */ /************************************************************************/ class FileGDBOrIterator : public FileGDBIterator { FileGDBIterator *poIter1; FileGDBIterator *poIter2; int bIteratorAreExclusive; int iNextRow1; int iNextRow2; int bHasJustReset; public: FileGDBOrIterator(FileGDBIterator* poIter1, FileGDBIterator* poIter2, int bIteratorAreExclusive = FALSE); virtual ~FileGDBOrIterator(); virtual FileGDBTable *GetTable() { return poIter1->GetTable(); } virtual void Reset(); virtual int GetNextRowSortedByFID(); virtual int GetRowCount(); }; /************************************************************************/ /* FileGDBIndexIterator */ /************************************************************************/ #define MAX_DEPTH 3 #define UUID_LEN_AS_STRING 38 #define MAX_CAR_COUNT_STR 80 #define MAX_UTF8_LEN_STR (4 * MAX_CAR_COUNT_STR) #define FGDB_PAGE_SIZE 4096 class FileGDBIndexIterator : public FileGDBIterator { FileGDBTable *poParent; int bAscending; VSILFILE *fpCurIdx; FileGDBFieldType eFieldType; GUInt32 nMaxPerPages; GUInt32 nOffsetFirstValInPage; GUInt32 nValueCountInIdx; GUInt32 nIndexDepth; FileGDBSQLOp eOp; OGRField sValue; int iFirstPageIdx[MAX_DEPTH], iLastPageIdx[MAX_DEPTH], iCurPageIdx[MAX_DEPTH]; GUInt32 nSubPagesCount[MAX_DEPTH]; GUInt32 nLastPageAccessed[MAX_DEPTH]; int iCurFeatureInPage, nFeaturesInPage; int bEvaluateToFALSE; int bEOF; int iSorted; int nSortedCount; int *panSortedRows; int SortRows(); GUInt16 asUTF16Str[MAX_CAR_COUNT_STR]; int nStrLen; char szUUID[UUID_LEN_AS_STRING + 1]; GByte abyPage[MAX_DEPTH][FGDB_PAGE_SIZE]; GByte abyPageFeature[FGDB_PAGE_SIZE]; OGRField sMin, sMax; char szMin[MAX_UTF8_LEN_STR+1]; char szMax[MAX_UTF8_LEN_STR+1]; const OGRField* GetMinMaxValue(OGRField* psField, int& eOutType, int bIsMin); int ReadPageNumber(int iLevel); int LoadNextPage(int iLevel); int FindPages(int iLevel, int nPage); int LoadNextFeaturePage(); int GetNextRow(); FileGDBIndexIterator(FileGDBTable* poParent, int bAscending); int SetConstraint(int nFieldIdx, FileGDBSQLOp op, OGRFieldType eOGRFieldType, const OGRField* psValue); template <class Getter> void GetMinMaxSumCount( double& dfMin, double& dfMax, double& dfSum, int& nCount); public: virtual ~FileGDBIndexIterator(); static FileGDBIterator* Build(FileGDBTable* poParent, int nFieldIdx, int bAscending, FileGDBSQLOp op, OGRFieldType eOGRFieldType, const OGRField* psValue); virtual FileGDBTable *GetTable() { return poParent; } virtual void Reset(); virtual int GetNextRowSortedByFID(); virtual int GetRowCount(); virtual int GetNextRowSortedByValue() { return GetNextRow(); } virtual const OGRField* GetMinValue(int& eOutType); virtual const OGRField* GetMaxValue(int& eOutType); virtual int GetMinMaxSumCount(double& dfMin, double& dfMax, double& dfSum, int& nCount); }; /************************************************************************/ /* GetMinValue() */ /************************************************************************/ const OGRField* FileGDBIterator::GetMinValue(int& eOutType) { PrintError(); eOutType = -1; return NULL; } /************************************************************************/ /* GetMaxValue() */ /************************************************************************/ const OGRField* FileGDBIterator::GetMaxValue(int& eOutType) { PrintError(); eOutType = -1; return NULL; } /************************************************************************/ /* GetNextRowSortedByValue() */ /************************************************************************/ int FileGDBIterator::GetNextRowSortedByValue() { PrintError(); return -1; } /************************************************************************/ /* GetMinMaxSumCount() */ /************************************************************************/ int FileGDBIterator::GetMinMaxSumCount(double& dfMin, double& dfMax, double& dfSum, int& nCount) { PrintError(); dfMin = 0.0; dfMax = 0.0; dfSum = 0.0; nCount = 0; return FALSE; } /************************************************************************/ /* Build() */ /************************************************************************/ FileGDBIterator* FileGDBIterator::Build(FileGDBTable* poParent, int nFieldIdx, int bAscending, FileGDBSQLOp op, OGRFieldType eOGRFieldType, const OGRField* psValue) { return FileGDBIndexIterator::Build(poParent, nFieldIdx, bAscending, op, eOGRFieldType, psValue); } /************************************************************************/ /* BuildIsNotNull() */ /************************************************************************/ FileGDBIterator* FileGDBIterator::BuildIsNotNull(FileGDBTable* poParent, int nFieldIdx, int bAscending) { FileGDBIterator* poIter = Build(poParent, nFieldIdx, bAscending, FGSO_ISNOTNULL, OFTMaxType, NULL); if( poIter != NULL ) { /* Optimization */ if( poIter->GetRowCount() == poParent->GetTotalRecordCount() ) { CPLAssert(poParent->GetValidRecordCount() == poParent->GetTotalRecordCount()); poIter = new FileGDBTrivialIterator(poIter); } } return poIter; } /************************************************************************/ /* BuildNot() */ /************************************************************************/ FileGDBIterator* FileGDBIterator::BuildNot(FileGDBIterator* poIterBase) { return new FileGDBNotIterator(poIterBase); } /************************************************************************/ /* BuildAnd() */ /************************************************************************/ FileGDBIterator* FileGDBIterator::BuildAnd(FileGDBIterator* poIter1, FileGDBIterator* poIter2) { return new FileGDBAndIterator(poIter1, poIter2); } /************************************************************************/ /* BuildOr() */ /************************************************************************/ FileGDBIterator* FileGDBIterator::BuildOr(FileGDBIterator* poIter1, FileGDBIterator* poIter2, int bIteratorAreExclusive) { return new FileGDBOrIterator(poIter1, poIter2, bIteratorAreExclusive); } /************************************************************************/ /* GetRowCount() */ /************************************************************************/ int FileGDBIterator::GetRowCount() { Reset(); int nCount = 0; while( GetNextRowSortedByFID() >= 0 ) nCount ++; Reset(); return nCount; } /************************************************************************/ /* FileGDBTrivialIterator() */ /************************************************************************/ FileGDBTrivialIterator::FileGDBTrivialIterator(FileGDBIterator* poParentIter) : poParentIter(poParentIter), poTable(poParentIter->GetTable()), iRow(0) { } /************************************************************************/ /* GetNextRowSortedByFID() */ /************************************************************************/ int FileGDBTrivialIterator::GetNextRowSortedByFID() { if( iRow < poTable->GetTotalRecordCount() ) return iRow ++; else return -1; } /************************************************************************/ /* FileGDBNotIterator() */ /************************************************************************/ FileGDBNotIterator::FileGDBNotIterator(FileGDBIterator* poIterBase) : poIterBase(poIterBase), poTable(poIterBase->GetTable()), iRow(0), iNextRowBase(-1) { bNoHoles = (poTable->GetValidRecordCount() == poTable->GetTotalRecordCount()); } /************************************************************************/ /* ~FileGDBNotIterator() */ /************************************************************************/ FileGDBNotIterator::~FileGDBNotIterator() { delete poIterBase; } /************************************************************************/ /* Reset() */ /************************************************************************/ void FileGDBNotIterator::Reset() { poIterBase->Reset(); iRow = 0; iNextRowBase = -1; } /************************************************************************/ /* GetNextRowSortedByFID() */ /************************************************************************/ int FileGDBNotIterator::GetNextRowSortedByFID() { if( iNextRowBase < 0 ) { iNextRowBase = poIterBase->GetNextRowSortedByFID(); if( iNextRowBase < 0 ) iNextRowBase = poTable->GetTotalRecordCount(); } while( TRUE ) { if( iRow < iNextRowBase ) { if( bNoHoles ) return iRow ++; else if( poTable->GetOffsetInTableForRow(iRow) ) return iRow ++; else if( !poTable->HasGotError() ) iRow ++; else return -1; } else if( iRow == poTable->GetTotalRecordCount() ) return -1; else { iRow = iNextRowBase + 1; iNextRowBase = poIterBase->GetNextRowSortedByFID(); if( iNextRowBase < 0 ) iNextRowBase = poTable->GetTotalRecordCount(); } } } /************************************************************************/ /* GetRowCount() */ /************************************************************************/ int FileGDBNotIterator::GetRowCount() { return poTable->GetValidRecordCount() - poIterBase->GetRowCount(); } /************************************************************************/ /* FileGDBAndIterator() */ /************************************************************************/ FileGDBAndIterator::FileGDBAndIterator(FileGDBIterator* poIter1, FileGDBIterator* poIter2) : poIter1(poIter1), poIter2(poIter2), iNextRow1(-1), iNextRow2(-1) { CPLAssert(poIter1->GetTable() == poIter2->GetTable()); } /************************************************************************/ /* ~FileGDBAndIterator() */ /************************************************************************/ FileGDBAndIterator::~FileGDBAndIterator() { delete poIter1; delete poIter2; } /************************************************************************/ /* Reset() */ /************************************************************************/ void FileGDBAndIterator::Reset() { poIter1->Reset(); poIter2->Reset(); iNextRow1 = -1; iNextRow2 = -1; } /************************************************************************/ /* GetNextRowSortedByFID() */ /************************************************************************/ int FileGDBAndIterator::GetNextRowSortedByFID() { if( iNextRow1 == iNextRow2 ) { iNextRow1 = poIter1->GetNextRowSortedByFID(); iNextRow2 = poIter2->GetNextRowSortedByFID(); if( iNextRow1 < 0 || iNextRow2 < 0 ) { return -1; } } while( TRUE ) { if( iNextRow1 < iNextRow2 ) { iNextRow1 = poIter1->GetNextRowSortedByFID(); if( iNextRow1 < 0 ) return -1; } else if( iNextRow2 < iNextRow1 ) { iNextRow2 = poIter2->GetNextRowSortedByFID(); if( iNextRow2 < 0 ) return -1; } else return iNextRow1; } } /************************************************************************/ /* FileGDBOrIterator() */ /************************************************************************/ FileGDBOrIterator::FileGDBOrIterator(FileGDBIterator* poIter1, FileGDBIterator* poIter2, int bIteratorAreExclusive) : poIter1(poIter1), poIter2(poIter2), bIteratorAreExclusive(bIteratorAreExclusive), iNextRow1(-1), iNextRow2(-1), bHasJustReset(TRUE) { CPLAssert(poIter1->GetTable() == poIter2->GetTable()); } /************************************************************************/ /* ~FileGDBOrIterator() */ /************************************************************************/ FileGDBOrIterator::~FileGDBOrIterator() { delete poIter1; delete poIter2; } /************************************************************************/ /* Reset() */ /************************************************************************/ void FileGDBOrIterator::Reset() { poIter1->Reset(); poIter2->Reset(); iNextRow1 = -1; iNextRow2 = -1; bHasJustReset = TRUE; } /************************************************************************/ /* GetNextRowSortedByFID() */ /************************************************************************/ int FileGDBOrIterator::GetNextRowSortedByFID() { if( bHasJustReset ) { bHasJustReset = FALSE; iNextRow1 = poIter1->GetNextRowSortedByFID(); iNextRow2 = poIter2->GetNextRowSortedByFID(); } if( iNextRow1 < 0 ) { int iVal = iNextRow2; iNextRow2 = poIter2->GetNextRowSortedByFID(); return iVal; } if( iNextRow2 < 0 || iNextRow1 < iNextRow2 ) { int iVal = iNextRow1; iNextRow1 = poIter1->GetNextRowSortedByFID(); return iVal; } if( iNextRow2 < iNextRow1 ) { int iVal = iNextRow2; iNextRow2 = poIter2->GetNextRowSortedByFID(); return iVal; } if( bIteratorAreExclusive ) PrintError(); int iVal = iNextRow1; iNextRow1 = poIter1->GetNextRowSortedByFID(); iNextRow2 = poIter2->GetNextRowSortedByFID(); return iVal; } /************************************************************************/ /* GetRowCount() */ /************************************************************************/ int FileGDBOrIterator::GetRowCount() { if( bIteratorAreExclusive ) return poIter1->GetRowCount() + poIter2->GetRowCount(); else return FileGDBIterator::GetRowCount(); } /************************************************************************/ /* FileGDBIndexIterator() */ /************************************************************************/ FileGDBIndexIterator::FileGDBIndexIterator(FileGDBTable* poParent, int bAscending) : poParent(poParent), bAscending(bAscending), fpCurIdx(NULL), eFieldType(FGFT_UNDEFINED), nMaxPerPages(0), nOffsetFirstValInPage(0), nValueCountInIdx(0), nIndexDepth(0), eOp(FGSO_ISNOTNULL), iCurFeatureInPage(-1), nFeaturesInPage(0), bEvaluateToFALSE(FALSE), bEOF(FALSE), iSorted(0), nSortedCount(-1), panSortedRows(NULL), nStrLen(0) { memset(iFirstPageIdx, 0xFF, MAX_DEPTH * sizeof(int)); memset(iLastPageIdx, 0xFF, MAX_DEPTH * sizeof(int)); memset(iCurPageIdx, 0xFF, MAX_DEPTH * sizeof(int)); memset(nSubPagesCount, 0, MAX_DEPTH * sizeof(int)); memset(nLastPageAccessed, 0, MAX_DEPTH * sizeof(int)); memset(&sValue, 0, sizeof(sValue)); } /************************************************************************/ /* ~FileGDBIndexIterator() */ /************************************************************************/ FileGDBIndexIterator::~FileGDBIndexIterator() { if( fpCurIdx ) VSIFCloseL(fpCurIdx); fpCurIdx = NULL; VSIFree(panSortedRows); } /************************************************************************/ /* Build() */ /************************************************************************/ FileGDBIterator* FileGDBIndexIterator::Build( FileGDBTable* poParent, int nFieldIdx, int bAscending, FileGDBSQLOp op, OGRFieldType eOGRFieldType, const OGRField* psValue ) { FileGDBIndexIterator* poIndexIterator = new FileGDBIndexIterator(poParent, bAscending); if( poIndexIterator->SetConstraint(nFieldIdx, op, eOGRFieldType, psValue) ) { return poIndexIterator; } delete poIndexIterator; return NULL; } /************************************************************************/ /* FileGDBSQLOpToStr() */ /************************************************************************/ static const char* FileGDBSQLOpToStr(FileGDBSQLOp op) { switch( op ) { case FGSO_ISNOTNULL : return "IS NOT NULL"; case FGSO_LT: return "<";; case FGSO_LE: return "<="; case FGSO_EQ: return "="; case FGSO_GE: return ">="; case FGSO_GT: return ">"; } return "unknown_op"; } /************************************************************************/ /* FileGDBValueToStr() */ /************************************************************************/ static const char* FileGDBValueToStr(OGRFieldType eOGRFieldType, const OGRField* psValue) { if( psValue == NULL ) return ""; switch( eOGRFieldType ) { case OFTInteger: return CPLSPrintf("%d", psValue->Integer); case OFTReal: return CPLSPrintf("%.18g", psValue->Real); case OFTString: return psValue->String; case OFTDateTime: return CPLSPrintf("%04d/%02d/%02d %02d:%02d:%02d", psValue->Date.Year, psValue->Date.Month, psValue->Date.Day, psValue->Date.Hour, psValue->Date.Minute, psValue->Date.Second); case OFTDate: return CPLSPrintf("%04d/%02d/%02d", psValue->Date.Year, psValue->Date.Month, psValue->Date.Day); case OFTTime: return CPLSPrintf("%02d:%02d:%02d", psValue->Date.Hour, psValue->Date.Minute, psValue->Date.Second); default: break; } return ""; } /************************************************************************/ /* SetConstraint() */ /************************************************************************/ int FileGDBIndexIterator::SetConstraint(int nFieldIdx, FileGDBSQLOp op, OGRFieldType eOGRFieldType, const OGRField* psValue) { const int errorRetValue = FALSE; CPLAssert(fpCurIdx == NULL); returnErrorIf(nFieldIdx < 0 || nFieldIdx >= poParent->GetFieldCount() ); FileGDBField* poField = poParent->GetField(nFieldIdx); returnErrorIf(!(poField->HasIndex()) ); eFieldType = poField->GetType(); eOp = op; returnErrorIf(eFieldType != FGFT_INT16 && eFieldType != FGFT_INT32 && eFieldType != FGFT_FLOAT32 && eFieldType != FGFT_FLOAT64 && eFieldType != FGFT_STRING && eFieldType != FGFT_DATETIME && eFieldType != FGFT_UUID_1 && eFieldType != FGFT_UUID_2 ); const char* pszAtxName = CPLFormFilename(CPLGetPath(poParent->GetFilename().c_str()), CPLGetBasename(poParent->GetFilename().c_str()), CPLSPrintf("%s.atx", poField->GetIndex()->GetIndexName().c_str())); fpCurIdx = VSIFOpenL( pszAtxName, "rb" ); returnErrorIf(fpCurIdx == NULL ); VSIFSeekL(fpCurIdx, 0, SEEK_END); vsi_l_offset nFileSize = VSIFTellL(fpCurIdx); returnErrorIf(nFileSize < FGDB_PAGE_SIZE + 22 ); VSIFSeekL(fpCurIdx, nFileSize - 22, SEEK_SET); GByte abyTrailer[22]; returnErrorIf(VSIFReadL( abyTrailer, 22, 1, fpCurIdx ) != 1 ); nMaxPerPages = (FGDB_PAGE_SIZE - 12) / (4 + abyTrailer[0]); nOffsetFirstValInPage = 12 + nMaxPerPages * 4; GUInt32 nMagic1 = GetUInt32(abyTrailer + 2, 0); returnErrorIf(nMagic1 != 1 ); nIndexDepth = GetUInt32(abyTrailer + 6, 0); /* CPLDebug("OpenFileGDB", "nIndexDepth = %u", nIndexDepth); */ returnErrorIf(!(nIndexDepth >= 1 && nIndexDepth <= MAX_DEPTH + 1) ); nValueCountInIdx = GetUInt32(abyTrailer + 10, 0); /* CPLDebug("OpenFileGDB", "nValueCountInIdx = %u", nValueCountInIdx); */ /* negative like in sample_clcV15_esri_v10.gdb/a00000005.FDO_UUID.atx */ if( (int)nValueCountInIdx < 0 ) return FALSE; /* QGIS_TEST_101.gdb/a00000006.FDO_UUID.atx */ if( nValueCountInIdx == 0 ) { VSIFSeekL(fpCurIdx, 4, SEEK_SET); GByte abyBuffer[4]; returnErrorIf(VSIFReadL( abyBuffer, 4, 1, fpCurIdx ) != 1 ); nValueCountInIdx = GetUInt32(abyBuffer, 0); } /* PreNIS.gdb/a00000006.FDO_UUID.atx has depth 2 and the value of */ /* nValueCountInIdx is 11 which is not the number of non-null values */ else if( nValueCountInIdx < nMaxPerPages && nIndexDepth > 1 ) return FALSE; returnErrorIf(nValueCountInIdx > (GUInt32)poParent->GetValidRecordCount() ); switch( eFieldType ) { case FGFT_INT16: returnErrorIf(abyTrailer[0] != sizeof(GUInt16)); if( eOp != FGSO_ISNOTNULL ) { returnErrorIf(eOGRFieldType != OFTInteger); sValue.Integer = psValue->Integer; } break; case FGFT_INT32: returnErrorIf(abyTrailer[0] != sizeof(GUInt32)); if( eOp != FGSO_ISNOTNULL ) { returnErrorIf(eOGRFieldType != OFTInteger); sValue.Integer = psValue->Integer; } break; case FGFT_FLOAT32: returnErrorIf(abyTrailer[0] != sizeof(float)); if( eOp != FGSO_ISNOTNULL ) { returnErrorIf(eOGRFieldType != OFTReal); sValue.Real = psValue->Real; } break; case FGFT_FLOAT64: returnErrorIf(abyTrailer[0] != sizeof(double)); if( eOp != FGSO_ISNOTNULL ) { returnErrorIf(eOGRFieldType != OFTReal); sValue.Real = psValue->Real; } break; case FGFT_STRING: { returnErrorIf((abyTrailer[0] % 2) != 0); returnErrorIf(abyTrailer[0] == 0); returnErrorIf(abyTrailer[0] > 2 * MAX_CAR_COUNT_STR); nStrLen = abyTrailer[0] / 2; if( eOp != FGSO_ISNOTNULL ) { returnErrorIf(eOGRFieldType != OFTString); wchar_t *pWide = CPLRecodeToWChar( psValue->String, CPL_ENC_UTF8, CPL_ENC_UCS2 ); returnErrorIf(pWide == NULL); int nCount = 0; while( pWide[nCount] != 0 ) { returnErrorAndCleanupIf(nCount == nStrLen, CPLFree(pWide)); asUTF16Str[nCount] = pWide[nCount]; nCount ++; } while( nCount < nStrLen ) { asUTF16Str[nCount] = 32; /* space character */ nCount ++; } CPLFree(pWide); } break; } case FGFT_DATETIME: { returnErrorIf( abyTrailer[0] != sizeof(double)); if( eOp != FGSO_ISNOTNULL ) { returnErrorIf(eOGRFieldType != OFTReal && eOGRFieldType != OFTDateTime && eOGRFieldType != OFTDate && eOGRFieldType != OFTTime); if( eOGRFieldType == OFTReal ) sValue.Real = psValue->Real; else FileGDBOGRDateToDoubleDate(psValue, &(sValue.Real)); } break; } case FGFT_UUID_1: case FGFT_UUID_2: { returnErrorIf(abyTrailer[0] != UUID_LEN_AS_STRING); if( eOp != FGSO_ISNOTNULL ) { returnErrorIf(eOGRFieldType != OFTString); memset(szUUID, 0, UUID_LEN_AS_STRING + 1); strncpy(szUUID, psValue->String, UUID_LEN_AS_STRING); bEvaluateToFALSE = (eOp == FGSO_EQ && strlen(psValue->String) != UUID_LEN_AS_STRING); } break; } default: CPLAssert(FALSE); break; } if( nValueCountInIdx > 0 ) { if( nIndexDepth == 1 ) { iFirstPageIdx[0] = iLastPageIdx[0] = 0; } else { returnErrorIf(!FindPages(0, 1) ); } } CPLDebug("OpenFileGDB", "Using index on field %s (%s %s)", poField->GetName().c_str(), FileGDBSQLOpToStr(eOp), FileGDBValueToStr(eOGRFieldType, psValue)); Reset(); return TRUE; } /************************************************************************/ /* FileGDBUTF16StrCompare() */ /************************************************************************/ static int FileGDBUTF16StrCompare(const GUInt16* pasFirst, const GUInt16* pasSecond, int nStrLen) { for(int i=0;i<nStrLen;i++) { if( pasFirst[i] < pasSecond[i] ) return -1; if( pasFirst[i] > pasSecond[i] ) return 1; } return 0; } /************************************************************************/ /* COMPARE() */ /************************************************************************/ #define COMPARE(a,b) (((a)<(b)) ? -1 : ((a)==(b)) ? 0 : 1) /************************************************************************/ /* FindPages() */ /************************************************************************/ int FileGDBIndexIterator::FindPages(int iLevel, int nPage) { const int errorRetValue = FALSE; VSIFSeekL(fpCurIdx, (nPage - 1) * FGDB_PAGE_SIZE, SEEK_SET); returnErrorIf(VSIFReadL( abyPage[iLevel], FGDB_PAGE_SIZE, 1, fpCurIdx ) != 1 ); nSubPagesCount[iLevel] = GetUInt32(abyPage[iLevel] + 4, 0); returnErrorIf(nSubPagesCount[iLevel] == 0 || nSubPagesCount[iLevel] > nMaxPerPages); if( nIndexDepth == 2 ) returnErrorIf(nValueCountInIdx > nMaxPerPages * (nSubPagesCount[0] + 1)); if( eOp == FGSO_ISNOTNULL ) { iFirstPageIdx[iLevel] = 0; iLastPageIdx[iLevel] = nSubPagesCount[iLevel]; return TRUE; } GUInt32 i; #ifdef DEBUG_INDEX_CONSISTENCY double dfLastMax = 0.0; int nLastMax = 0; GUInt16 asLastMax[MAX_CAR_COUNT_STR] = { 0 }; char szLastMaxUUID[UUID_LEN_AS_STRING + 1] = { 0 }; #endif iFirstPageIdx[iLevel] = iLastPageIdx[iLevel] = -1; for( i = 0; i < nSubPagesCount[iLevel]; i ++ ) { int nComp; switch( eFieldType ) { case FGFT_INT16: { GInt16 nVal = GetInt16(abyPage[iLevel] + nOffsetFirstValInPage, i); #ifdef DEBUG_INDEX_CONSISTENCY returnErrorIf(i > 0 && nVal < nLastMax); nLastMax = nVal; #endif nComp = COMPARE(sValue.Integer, nVal); break; } case FGFT_INT32: { GInt32 nVal = GetInt32(abyPage[iLevel] + nOffsetFirstValInPage, i); #ifdef DEBUG_INDEX_CONSISTENCY returnErrorIf(i > 0 && nVal < nLastMax); nLastMax = nVal; #endif nComp = COMPARE(sValue.Integer, nVal); break; } case FGFT_FLOAT32: { float fVal = GetFloat32(abyPage[iLevel] + nOffsetFirstValInPage, i); #ifdef DEBUG_INDEX_CONSISTENCY returnErrorIf(i > 0 && fVal < dfLastMax); dfLastMax = fVal; #endif nComp = COMPARE(sValue.Real, fVal); break; } case FGFT_FLOAT64: case FGFT_DATETIME: { double dfVal = GetFloat64(abyPage[iLevel] + nOffsetFirstValInPage, i); #ifdef DEBUG_INDEX_CONSISTENCY returnErrorIf(i > 0 && dfVal < dfLastMax); dfLastMax = dfVal; #endif nComp = COMPARE(sValue.Real, dfVal); break; } case FGFT_STRING: { GUInt16* pasMax; #ifdef CPL_MSB GUInt16 asMax[MAX_CAR_COUNT_STR]; pasMax = asMax; memcpy(asMax, abyPage[iLevel] + nOffsetFirstValInPage + nStrLen * sizeof(GUInt16) * i, nStrLen * sizeof(GUInt16)); for(int j=0;j<nStrLen;j++) CPL_LSBPTR16(&asMax[j]); #else pasMax = (GUInt16*)(abyPage[iLevel] + nOffsetFirstValInPage + nStrLen * sizeof(GUInt16) * i); #endif #ifdef DEBUG_INDEX_CONSISTENCY returnErrorIf(i > 0 && FileGDBUTF16StrCompare(pasMax, asLastMax, nStrLen) < 0); memcpy(asLastMax, pasMax, nStrLen * 2); #endif nComp = FileGDBUTF16StrCompare(asUTF16Str, pasMax, nStrLen); break; } case FGFT_UUID_1: case FGFT_UUID_2: { const char* psNonzMaxUUID = (char*)(abyPage[iLevel] + nOffsetFirstValInPage + UUID_LEN_AS_STRING * i); #ifdef DEBUG_INDEX_CONSISTENCY returnErrorIf(i > 0 && memcmp(psNonzMaxUUID, szLastMaxUUID, UUID_LEN_AS_STRING) < 0); memcpy(szLastMaxUUID, psNonzMaxUUID, UUID_LEN_AS_STRING); #endif nComp = memcmp(szUUID, psNonzMaxUUID, UUID_LEN_AS_STRING); break; } default: CPLAssert(FALSE); nComp = 0; break; } int bStop = FALSE; switch( eOp ) { /* dfVal = 1 2 2 3 3 4 */ /* sValue.Real = 3 */ /* nComp = (sValue.Real < dfVal) ? -1 : (sValue.Real == dfVal) ? 0 : 1; */ case FGSO_LT: case FGSO_LE: if( iFirstPageIdx[iLevel] < 0 ) { iFirstPageIdx[iLevel] = iLastPageIdx[iLevel] = (int)i; } else { iLastPageIdx[iLevel] = (int)i; if( nComp < 0 ) { bStop = TRUE; } } break; case FGSO_EQ: if( iFirstPageIdx[iLevel] < 0 ) { if( nComp <= 0 ) iFirstPageIdx[iLevel] = iLastPageIdx[iLevel] = (int)i; } else { if( nComp == 0 ) iLastPageIdx[iLevel] = (int)i; else bStop = TRUE; } break; case FGSO_GE: if( iFirstPageIdx[iLevel] < 0 ) { if( nComp <= 0 ) { iFirstPageIdx[iLevel] = (int)i; iLastPageIdx[iLevel] = nSubPagesCount[iLevel]; bStop = TRUE; } } break; case FGSO_GT: if( iFirstPageIdx[iLevel] < 0 ) { if( nComp < 0 ) { iFirstPageIdx[iLevel] = (int)i; iLastPageIdx[iLevel] = nSubPagesCount[iLevel]; bStop = TRUE; } } break; default: CPLAssert(FALSE); break; } if( bStop ) break; } if( iFirstPageIdx[iLevel] < 0 ) { iFirstPageIdx[iLevel] = iLastPageIdx[iLevel] = nSubPagesCount[iLevel]; } else if( iLastPageIdx[iLevel] < (int)nSubPagesCount[iLevel] ) { iLastPageIdx[iLevel] ++; } return TRUE; } /************************************************************************/ /* Reset() */ /************************************************************************/ void FileGDBIndexIterator::Reset() { iCurPageIdx[0] = (bAscending) ? iFirstPageIdx[0] -1 : iLastPageIdx[0] + 1; memset(iFirstPageIdx + 1, 0xFF, (MAX_DEPTH - 1) * sizeof(int)); memset(iLastPageIdx + 1, 0xFF, (MAX_DEPTH - 1) * sizeof(int)); memset(iCurPageIdx + 1, 0xFF, (MAX_DEPTH - 1) * sizeof(int)); memset(nLastPageAccessed, 0, MAX_DEPTH * sizeof(int)); iCurFeatureInPage = 0; nFeaturesInPage = 0; iSorted = 0; bEOF = ( nValueCountInIdx == 0 || bEvaluateToFALSE ); } /************************************************************************/ /* ReadPageNumber() */ /************************************************************************/ int FileGDBIndexIterator::ReadPageNumber(int iLevel) { const int errorRetValue = 0; GUInt32 nPage = GetUInt32(abyPage[iLevel] + 8, iCurPageIdx[iLevel]); if( nPage == nLastPageAccessed[iLevel] ) { if( !LoadNextPage(iLevel) ) return 0; nPage = GetUInt32(abyPage[iLevel] + 8, iCurPageIdx[iLevel]); } nLastPageAccessed[iLevel] = nPage; returnErrorIf(nPage < 2); return nPage; } /************************************************************************/ /* LoadNextPage() */ /************************************************************************/ int FileGDBIndexIterator::LoadNextPage(int iLevel) { const int errorRetValue = FALSE; if( (bAscending && iCurPageIdx[iLevel] == iLastPageIdx[iLevel]) || (!bAscending && iCurPageIdx[iLevel] == iFirstPageIdx[iLevel]) ) { if( iLevel == 0 || !LoadNextPage(iLevel - 1) ) return FALSE; GUInt32 nPage = ReadPageNumber(iLevel-1); returnErrorIf(!FindPages(iLevel, nPage) ); iCurPageIdx[iLevel] = (bAscending) ? iFirstPageIdx[iLevel] : iLastPageIdx[iLevel]; } else { if( bAscending ) iCurPageIdx[iLevel] ++; else iCurPageIdx[iLevel] --; } return TRUE; } /************************************************************************/ /* LoadNextFeaturePage() */ /************************************************************************/ int FileGDBIndexIterator::LoadNextFeaturePage() { const int errorRetValue = FALSE; GUInt32 nPage; if( nIndexDepth == 1 ) { if( iCurPageIdx[0] == iLastPageIdx[0] ) { return FALSE; } if( bAscending ) iCurPageIdx[0] ++; else iCurPageIdx[0] --; nPage = 1; } else { if( !LoadNextPage( nIndexDepth - 2 ) ) { return FALSE; } nPage = ReadPageNumber(nIndexDepth - 2); returnErrorIf(nPage < 2); } VSIFSeekL(fpCurIdx, (nPage - 1) * FGDB_PAGE_SIZE, SEEK_SET); returnErrorIf(VSIFReadL( abyPageFeature, FGDB_PAGE_SIZE, 1, fpCurIdx ) != 1); GUInt32 nFeatures = GetUInt32(abyPageFeature + 4, 0); returnErrorIf(nFeatures > nMaxPerPages); nFeaturesInPage = (int)nFeatures; iCurFeatureInPage = (bAscending) ? 0 : nFeaturesInPage - 1; if( nFeatures == 0 ) return FALSE; return TRUE; } /************************************************************************/ /* GetNextRow() */ /************************************************************************/ int FileGDBIndexIterator::GetNextRow() { const int errorRetValue = -1; if( bEOF ) return -1; while( TRUE ) { if( iCurFeatureInPage >= nFeaturesInPage || iCurFeatureInPage < 0 ) { if( !LoadNextFeaturePage() ) { bEOF = TRUE; return -1; } } int bMatch; if( eOp == FGSO_ISNOTNULL ) { bMatch = TRUE; } else { int nComp; bMatch = FALSE; switch( eFieldType ) { case FGFT_INT16: { GInt16 nVal = GetInt16(abyPageFeature + nOffsetFirstValInPage, iCurFeatureInPage); nComp = COMPARE(sValue.Integer, nVal); break; } case FGFT_INT32: { GInt32 nVal = GetInt32(abyPageFeature + nOffsetFirstValInPage, iCurFeatureInPage); nComp = COMPARE(sValue.Integer, nVal); break; } case FGFT_FLOAT32: { float fVal = GetFloat32(abyPageFeature + nOffsetFirstValInPage, iCurFeatureInPage); nComp = COMPARE(sValue.Real, fVal); break; } case FGFT_FLOAT64: case FGFT_DATETIME: { double dfVal = GetFloat64(abyPageFeature + nOffsetFirstValInPage, iCurFeatureInPage); nComp = COMPARE(sValue.Real, dfVal); break; } case FGFT_STRING: { #ifdef CPL_MSB GUInt16 asVal[MAX_CAR_COUNT_STR]; memcpy(asVal, abyPageFeature + nOffsetFirstValInPage + nStrLen * 2 * iCurFeatureInPage, nStrLen * 2); for(int j=0;j<nStrLen;j++) CPL_LSBPTR16(&asVal[j]); nComp = FileGDBUTF16StrCompare(asUTF16Str, asVal, nStrLen); #else nComp = FileGDBUTF16StrCompare(asUTF16Str, (GUInt16*)(abyPageFeature + nOffsetFirstValInPage + nStrLen * 2 * iCurFeatureInPage), nStrLen); #endif break; } case FGFT_UUID_1: case FGFT_UUID_2: { nComp = memcmp(szUUID, abyPageFeature + nOffsetFirstValInPage + UUID_LEN_AS_STRING *iCurFeatureInPage, UUID_LEN_AS_STRING); break; } default: CPLAssert(FALSE); nComp = 0; break; } switch( eOp ) { case FGSO_LT: if( nComp <= 0 && bAscending ) { bEOF = TRUE; return -1; } bMatch = TRUE; break; case FGSO_LE: if( nComp < 0 && bAscending ) { bEOF = TRUE; return -1; } bMatch = TRUE; break; case FGSO_EQ: if( nComp < 0 && bAscending ) { bEOF = TRUE; return -1; } bMatch = ( nComp == 0 ); break; case FGSO_GE: bMatch = ( nComp <= 0 ); break; case FGSO_GT: bMatch = ( nComp < 0 ); break; default: CPLAssert(FALSE); break; } } if( bMatch ) { GUInt32 nFID = GetUInt32(abyPageFeature + 12, iCurFeatureInPage); if( bAscending ) iCurFeatureInPage ++; else iCurFeatureInPage --; returnErrorAndCleanupIf(nFID < 1 || nFID > (GUInt32)poParent->GetTotalRecordCount(), bEOF = TRUE); return (int) (nFID - 1); } else { if( bAscending ) iCurFeatureInPage ++; else iCurFeatureInPage --; } } } /************************************************************************/ /* SortRows() */ /************************************************************************/ int FileGDBIndexIterator::SortRows() { nSortedCount = 0; iSorted = 0; int nSortedAlloc = 0; Reset(); while( TRUE ) { int nRow = GetNextRow(); if( nRow < 0 ) break; if( nSortedCount == nSortedAlloc ) { int nNewSortedAlloc = 4 * nSortedAlloc / 3 + 16; int* panNewSortedRows = (int*)VSIRealloc(panSortedRows, sizeof(int) * nNewSortedAlloc); if( panNewSortedRows == NULL ) { nSortedCount = 0; return FALSE; } nSortedAlloc = nNewSortedAlloc; panSortedRows = panNewSortedRows; } panSortedRows[nSortedCount ++] = nRow; } if( nSortedCount == 0 ) return FALSE; std::sort(panSortedRows, panSortedRows + nSortedCount); if( eOp == FGSO_ISNOTNULL && (int)nValueCountInIdx != nSortedCount ) PrintError(); return TRUE; } /************************************************************************/ /* GetNextRowSortedByFID() */ /************************************************************************/ int FileGDBIndexIterator::GetNextRowSortedByFID() { if( eOp == FGSO_EQ ) return GetNextRow(); if( iSorted < nSortedCount ) return panSortedRows[iSorted ++]; if( nSortedCount < 0 ) { if( !SortRows() ) return -1; return panSortedRows[iSorted ++]; } else { return -1; } } /************************************************************************/ /* GetRowCount() */ /************************************************************************/ int FileGDBIndexIterator::GetRowCount() { if( eOp == FGSO_ISNOTNULL ) return (int)nValueCountInIdx; if( nSortedCount >= 0 ) return nSortedCount; int nRowCount = 0; int bSaveAscending = bAscending; bAscending = TRUE; /* for a tiny bit of more efficiency */ Reset(); while( GetNextRow() >= 0 ) nRowCount ++; bAscending = bSaveAscending; Reset(); return nRowCount; } /************************************************************************/ /* GetMinMaxValue() */ /************************************************************************/ const OGRField* FileGDBIndexIterator::GetMinMaxValue(OGRField* psField, int& eOutType, int bIsMin) { const OGRField* errorRetValue = NULL; eOutType = -1; if( nValueCountInIdx == 0 ) return NULL; GByte abyPage[FGDB_PAGE_SIZE]; GUInt32 nPage = 1; for( GUInt32 iLevel = 0; iLevel < nIndexDepth - 1; iLevel ++ ) { VSIFSeekL(fpCurIdx, (nPage - 1) * FGDB_PAGE_SIZE, SEEK_SET); returnErrorIf(VSIFReadL( abyPage, FGDB_PAGE_SIZE, 1, fpCurIdx ) != 1 ); GUInt32 nSubPagesCount = GetUInt32(abyPage + 4, 0); returnErrorIf(nSubPagesCount == 0 || nSubPagesCount > nMaxPerPages); if( bIsMin ) nPage = GetUInt32(abyPage + 8, 0); else nPage = GetUInt32(abyPage + 8, nSubPagesCount); returnErrorIf(nPage < 2 ); } VSIFSeekL(fpCurIdx, (nPage - 1) * FGDB_PAGE_SIZE, SEEK_SET); returnErrorIf(VSIFReadL( abyPage, FGDB_PAGE_SIZE, 1, fpCurIdx ) != 1); GUInt32 nFeatures = GetUInt32(abyPage + 4, 0); returnErrorIf(nFeatures < 1 || nFeatures > nMaxPerPages); int iFeature = (bIsMin) ? 0 : nFeatures-1; switch( eFieldType ) { case FGFT_INT16: { GInt16 nVal = GetInt16(abyPage + nOffsetFirstValInPage, iFeature); psField->Integer = nVal; eOutType = OFTInteger; return psField; } case FGFT_INT32: { GInt32 nVal = GetInt32(abyPage + nOffsetFirstValInPage, iFeature); psField->Integer = nVal; eOutType = OFTInteger; return psField; } case FGFT_FLOAT32: { float fVal = GetFloat32(abyPage + nOffsetFirstValInPage, iFeature); psField->Real = fVal; eOutType = OFTReal; return psField; } case FGFT_FLOAT64: { double dfVal = GetFloat64(abyPage + nOffsetFirstValInPage, iFeature); psField->Real = dfVal; eOutType = OFTReal; return psField; } case FGFT_DATETIME: { double dfVal = GetFloat64(abyPage + nOffsetFirstValInPage, iFeature); FileGDBDoubleDateToOGRDate(dfVal, psField); eOutType = OFTDateTime; return psField; } case FGFT_STRING: { wchar_t awsVal[MAX_CAR_COUNT_STR+1]; for(int j=0;j<nStrLen;j++) { GUInt16 nCh = GetUInt16(abyPage + nOffsetFirstValInPage + nStrLen * sizeof(GUInt16) * iFeature, j); awsVal[j] = nCh; } awsVal[nStrLen] = 0; char* pszOut = CPLRecodeFromWChar(awsVal, CPL_ENC_UCS2, CPL_ENC_UTF8); returnErrorIf(pszOut == NULL ); returnErrorAndCleanupIf(strlen(pszOut) > MAX_UTF8_LEN_STR, VSIFree(pszOut) ); strcpy(psField->String, pszOut); CPLFree(pszOut); eOutType = OFTString; return psField; } case FGFT_UUID_1: case FGFT_UUID_2: { memcpy(psField->String, abyPage + nOffsetFirstValInPage + UUID_LEN_AS_STRING *iFeature, UUID_LEN_AS_STRING); psField->String[UUID_LEN_AS_STRING] = 0; eOutType = OFTString; return psField; } default: CPLAssert(FALSE); break; } return NULL; } /************************************************************************/ /* GetMinValue() */ /************************************************************************/ const OGRField* FileGDBIndexIterator::GetMinValue(int& eOutType) { if( eOp != FGSO_ISNOTNULL ) return FileGDBIterator::GetMinValue(eOutType); if( eFieldType == FGFT_STRING || eFieldType == FGFT_UUID_1 || eFieldType == FGFT_UUID_2 ) sMin.String = szMin; return GetMinMaxValue(&sMin, eOutType, TRUE); } /************************************************************************/ /* GetMaxValue() */ /************************************************************************/ const OGRField* FileGDBIndexIterator::GetMaxValue(int& eOutType) { if( eOp != FGSO_ISNOTNULL ) return FileGDBIterator::GetMinValue(eOutType); if( eFieldType == FGFT_STRING || eFieldType == FGFT_UUID_1 || eFieldType == FGFT_UUID_2 ) sMax.String = szMax; return GetMinMaxValue(&sMax, eOutType, FALSE); } /************************************************************************/ /* GetMinMaxSumCount() */ /************************************************************************/ struct Int16Getter { public: static double GetAsDouble(const GByte* pBaseAddr, int iOffset) { return GetInt16(pBaseAddr, iOffset); } }; struct Int32Getter { public: static double GetAsDouble(const GByte* pBaseAddr, int iOffset) { return GetInt32(pBaseAddr, iOffset); } }; struct Float32Getter { public: static double GetAsDouble(const GByte* pBaseAddr, int iOffset) { return GetFloat32(pBaseAddr, iOffset); } }; struct Float64Getter { public: static double GetAsDouble(const GByte* pBaseAddr, int iOffset) { return GetFloat64(pBaseAddr, iOffset); } }; template <class Getter> void FileGDBIndexIterator::GetMinMaxSumCount( double& dfMin, double& dfMax, double& dfSum, int& nCount) { int nLocalCount = 0; double dfLocalSum = 0.0; double dfVal = 0.0; while( TRUE ) { if( iCurFeatureInPage >= nFeaturesInPage ) { if( !LoadNextFeaturePage() ) { break; } } dfVal = Getter::GetAsDouble(abyPageFeature + nOffsetFirstValInPage, iCurFeatureInPage); dfLocalSum += dfVal; if( nLocalCount == 0 ) dfMin = dfVal; nLocalCount ++; iCurFeatureInPage ++; } dfSum = dfLocalSum; nCount = nLocalCount; dfMax = dfVal; } int FileGDBIndexIterator::GetMinMaxSumCount(double& dfMin, double& dfMax, double& dfSum, int& nCount) { const int errorRetValue = FALSE; dfMin = 0.0; dfMax = 0.0; dfSum = 0.0; nCount = 0; returnErrorIf(eOp != FGSO_ISNOTNULL ); returnErrorIf(eFieldType != FGFT_INT16 && eFieldType != FGFT_INT32 && eFieldType != FGFT_FLOAT32 && eFieldType != FGFT_FLOAT64 && eFieldType != FGFT_DATETIME ); int bSaveAscending = bAscending; bAscending = TRUE; Reset(); switch( eFieldType ) { case FGFT_INT16: { GetMinMaxSumCount<Int16Getter>(dfMin, dfMax, dfSum, nCount); break; } case FGFT_INT32: { GetMinMaxSumCount<Int32Getter>(dfMin, dfMax, dfSum, nCount); break; } case FGFT_FLOAT32: { GetMinMaxSumCount<Float32Getter>(dfMin, dfMax, dfSum, nCount); break; } case FGFT_FLOAT64: case FGFT_DATETIME: { GetMinMaxSumCount<Float64Getter>(dfMin, dfMax, dfSum, nCount); break; } default: CPLAssert(FALSE); break; } bAscending = bSaveAscending; Reset(); return TRUE; } }; /* namespace OpenFileGDB */