4803 lines
168 KiB
C++
4803 lines
168 KiB
C++
/******************************************************************************
|
|
*
|
|
* Project: OpenGIS Simple Features Reference Implementation
|
|
* Purpose: Formal test harness for OGRLayer implementations.
|
|
* Author: Frank Warmerdam, warmerdam@pobox.com
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 1999, Frank Warmerdam
|
|
* Copyright (c) 2009-2014, Even Rouault <even dot rouault at spatialys.com>
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
****************************************************************************/
|
|
|
|
#include "cpl_conv.h"
|
|
#include "cpl_multiproc.h"
|
|
#include "gdal_version.h"
|
|
#include "ogr_api.h"
|
|
#include "ogr_p.h"
|
|
#include "ogrsf_frmts.h"
|
|
#include "ogr_swq.h"
|
|
#include "ogr_recordbatch.h"
|
|
#include "commonutils.h"
|
|
|
|
#include <cinttypes>
|
|
#include <algorithm>
|
|
#include <limits>
|
|
|
|
bool bReadOnly = false;
|
|
bool bVerbose = true;
|
|
const char *pszDataSource = nullptr;
|
|
char **papszLayers = nullptr;
|
|
const char *pszSQLStatement = nullptr;
|
|
const char *pszDialect = nullptr;
|
|
int nLoops = 1;
|
|
bool bFullSpatialFilter = false;
|
|
char **papszOpenOptions = nullptr;
|
|
const char *pszDriver = nullptr;
|
|
bool bAllDrivers = false;
|
|
const char *pszLogFilename = nullptr;
|
|
char **papszDSCO = nullptr;
|
|
char **papszLCO = nullptr;
|
|
|
|
typedef struct
|
|
{
|
|
CPLJoinableThread *hThread;
|
|
int bRet;
|
|
} ThreadContext;
|
|
|
|
static void Usage();
|
|
static void ThreadFunction(void *user_data);
|
|
static void ThreadFunctionInternal(ThreadContext *psContext);
|
|
static int TestDataset(GDALDriver **ppoDriver);
|
|
static int TestCreate(GDALDriver *poDriver, int bFromAllDrivers);
|
|
static int TestOGRLayer(GDALDataset *poDS, OGRLayer *poLayer, int bIsSQLLayer);
|
|
static int TestInterleavedReading(const char *pszDataSource,
|
|
char **papszLayers);
|
|
static int TestDSErrorConditions(GDALDataset *poDS);
|
|
static int TestVirtualIO(GDALDataset *poDS);
|
|
|
|
static const char *Log(const char *pszMsg, int nLineNumber)
|
|
{
|
|
if (pszLogFilename == nullptr)
|
|
return pszMsg;
|
|
FILE *f = fopen(pszLogFilename, "at");
|
|
if (f == nullptr)
|
|
return pszMsg;
|
|
fprintf(f, "%d: %s\n", nLineNumber, pszMsg);
|
|
fclose(f);
|
|
return pszMsg;
|
|
}
|
|
|
|
#define LOG_STR(str) (Log((str), __LINE__))
|
|
#define LOG_ACTION(action) ((void)Log(#action, __LINE__), (action))
|
|
|
|
/************************************************************************/
|
|
/* DestroyFeatureAndNullify() */
|
|
/************************************************************************/
|
|
|
|
static void DestroyFeatureAndNullify(OGRFeature *&poFeature)
|
|
{
|
|
OGRFeature::DestroyFeature(poFeature);
|
|
poFeature = nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* main() */
|
|
/************************************************************************/
|
|
|
|
MAIN_START(nArgc, papszArgv)
|
|
|
|
{
|
|
EarlySetConfigOptions(nArgc, papszArgv);
|
|
|
|
OGRRegisterAll();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Processing command line arguments. */
|
|
/* -------------------------------------------------------------------- */
|
|
nArgc = OGRGeneralCmdLineProcessor(nArgc, &papszArgv, 0);
|
|
|
|
if (nArgc < 1)
|
|
exit(-nArgc);
|
|
|
|
int bRet = TRUE;
|
|
int nThreads = 1;
|
|
|
|
if (EQUAL(CPLGetConfigOption("DRIVER_WISHED", ""), "FileGDB"))
|
|
{
|
|
auto poFileGDB = GetGDALDriverManager()->GetDriverByName("FileGDB");
|
|
auto poOpenFileGDB =
|
|
GetGDALDriverManager()->GetDriverByName("OpenFileGDB");
|
|
if (poFileGDB && poOpenFileGDB)
|
|
{
|
|
GetGDALDriverManager()->DeregisterDriver(poFileGDB);
|
|
GetGDALDriverManager()->DeregisterDriver(poOpenFileGDB);
|
|
GetGDALDriverManager()->RegisterDriver(poFileGDB);
|
|
GetGDALDriverManager()->RegisterDriver(poOpenFileGDB);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Processing command line arguments. */
|
|
/* -------------------------------------------------------------------- */
|
|
for (int iArg = 1; iArg < nArgc; iArg++)
|
|
{
|
|
if (EQUAL(papszArgv[iArg], "--utility_version"))
|
|
{
|
|
printf("%s was compiled against GDAL %s and "
|
|
"is running against GDAL %s\n",
|
|
papszArgv[0], GDAL_RELEASE_NAME,
|
|
GDALVersionInfo("RELEASE_NAME"));
|
|
CSLDestroy(papszArgv);
|
|
return 0;
|
|
}
|
|
else if (EQUAL(papszArgv[iArg], "-ro"))
|
|
{
|
|
bReadOnly = true;
|
|
}
|
|
else if (EQUAL(papszArgv[iArg], "-q") ||
|
|
EQUAL(papszArgv[iArg], "-quiet"))
|
|
{
|
|
bVerbose = false;
|
|
}
|
|
else if (EQUAL(papszArgv[iArg], "-sql") && iArg + 1 < nArgc)
|
|
{
|
|
pszSQLStatement = papszArgv[++iArg];
|
|
}
|
|
else if (EQUAL(papszArgv[iArg], "-dialect") && iArg + 1 < nArgc)
|
|
{
|
|
pszDialect = papszArgv[++iArg];
|
|
}
|
|
else if (EQUAL(papszArgv[iArg], "-threads") && iArg + 1 < nArgc)
|
|
{
|
|
nThreads = atoi(papszArgv[++iArg]);
|
|
}
|
|
else if (EQUAL(papszArgv[iArg], "-loops") && iArg + 1 < nArgc)
|
|
{
|
|
nLoops = atoi(papszArgv[++iArg]);
|
|
}
|
|
else if (EQUAL(papszArgv[iArg], "-fsf"))
|
|
{
|
|
bFullSpatialFilter = true;
|
|
}
|
|
else if (EQUAL(papszArgv[iArg], "-oo") && iArg + 1 < nArgc)
|
|
{
|
|
papszOpenOptions =
|
|
CSLAddString(papszOpenOptions, papszArgv[++iArg]);
|
|
}
|
|
else if (EQUAL(papszArgv[iArg], "-dsco") && iArg + 1 < nArgc)
|
|
{
|
|
papszDSCO = CSLAddString(papszDSCO, papszArgv[++iArg]);
|
|
}
|
|
else if (EQUAL(papszArgv[iArg], "-lco") && iArg + 1 < nArgc)
|
|
{
|
|
papszLCO = CSLAddString(papszLCO, papszArgv[++iArg]);
|
|
}
|
|
else if (EQUAL(papszArgv[iArg], "-log") && iArg + 1 < nArgc)
|
|
{
|
|
pszLogFilename = papszArgv[++iArg];
|
|
}
|
|
else if (EQUAL(papszArgv[iArg], "-driver") && iArg + 1 < nArgc)
|
|
{
|
|
pszDriver = papszArgv[++iArg];
|
|
}
|
|
else if (EQUAL(papszArgv[iArg], "-all_drivers"))
|
|
{
|
|
bAllDrivers = true;
|
|
}
|
|
else if (papszArgv[iArg][0] == '-')
|
|
{
|
|
Usage();
|
|
}
|
|
else if (pszDataSource == nullptr)
|
|
{
|
|
pszDataSource = papszArgv[iArg];
|
|
}
|
|
else
|
|
{
|
|
papszLayers = CSLAddString(papszLayers, papszArgv[iArg]);
|
|
}
|
|
}
|
|
|
|
if (pszDataSource == nullptr && pszDriver == nullptr && !bAllDrivers)
|
|
Usage();
|
|
|
|
if (nThreads > 1 && !bReadOnly && pszDataSource != nullptr)
|
|
{
|
|
fprintf(
|
|
stderr,
|
|
"-threads must be used with -ro or -driver/-all_drivers option.\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (nThreads == 1)
|
|
{
|
|
ThreadContext sContext;
|
|
ThreadFunction(&sContext);
|
|
bRet = sContext.bRet;
|
|
}
|
|
else if (nThreads > 1)
|
|
{
|
|
ThreadContext *pasContext = new ThreadContext[nThreads];
|
|
for (int i = 0; i < nThreads; i++)
|
|
{
|
|
pasContext[i].hThread =
|
|
CPLCreateJoinableThread(ThreadFunction, &(pasContext[i]));
|
|
}
|
|
for (int i = 0; i < nThreads; i++)
|
|
{
|
|
CPLJoinThread(pasContext[i].hThread);
|
|
bRet &= pasContext[i].bRet;
|
|
}
|
|
delete[] pasContext;
|
|
}
|
|
|
|
OGRCleanupAll();
|
|
|
|
CSLDestroy(papszLayers);
|
|
CSLDestroy(papszArgv);
|
|
CSLDestroy(papszOpenOptions);
|
|
CSLDestroy(papszDSCO);
|
|
CSLDestroy(papszLCO);
|
|
|
|
#ifdef DBMALLOC
|
|
malloc_dump(1);
|
|
#endif
|
|
|
|
return (bRet) ? 0 : 1;
|
|
}
|
|
|
|
MAIN_END
|
|
|
|
/************************************************************************/
|
|
/* ThreadFunction() */
|
|
/************************************************************************/
|
|
|
|
static void ThreadFunction(void *user_data)
|
|
|
|
{
|
|
ThreadContext *psContext = static_cast<ThreadContext *>(user_data);
|
|
psContext->bRet = TRUE;
|
|
#ifdef __AFL_HAVE_MANUAL_CONTROL
|
|
while (__AFL_LOOP(1000))
|
|
{
|
|
#endif
|
|
for (int iLoop = 0; psContext->bRet && iLoop < nLoops; iLoop++)
|
|
{
|
|
ThreadFunctionInternal(psContext);
|
|
}
|
|
#ifdef __AFL_HAVE_MANUAL_CONTROL
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ThreadFunctionInternal() */
|
|
/************************************************************************/
|
|
|
|
static void ThreadFunctionInternal(ThreadContext *psContext)
|
|
|
|
{
|
|
int bRet = TRUE;
|
|
|
|
GDALDriver *poDriver = nullptr;
|
|
|
|
if (pszDataSource != nullptr)
|
|
{
|
|
bRet = TestDataset(&poDriver);
|
|
}
|
|
else if (pszDriver != nullptr)
|
|
{
|
|
poDriver = static_cast<GDALDriver *>(GDALGetDriverByName(pszDriver));
|
|
if (poDriver)
|
|
{
|
|
bRet &= TestCreate(poDriver, FALSE);
|
|
}
|
|
else
|
|
{
|
|
printf("ERROR: Cannot find driver %s\n", pszDriver);
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const int nCount = GDALGetDriverCount();
|
|
for (int i = 0; i < nCount; i++)
|
|
{
|
|
poDriver = static_cast<GDALDriver *>(GDALGetDriver(i));
|
|
if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR) != nullptr)
|
|
bRet &= TestCreate(poDriver, TRUE);
|
|
}
|
|
}
|
|
|
|
psContext->bRet = bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestDataset() */
|
|
/************************************************************************/
|
|
|
|
static int TestDataset(GDALDriver **ppoDriver)
|
|
{
|
|
int bRet = TRUE;
|
|
int bRetLocal;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Open data source. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
GDALDataset *poDS = static_cast<GDALDataset *>(GDALOpenEx(
|
|
pszDataSource,
|
|
(!bReadOnly ? GDAL_OF_UPDATE : GDAL_OF_READONLY) | GDAL_OF_VECTOR,
|
|
nullptr, papszOpenOptions, nullptr));
|
|
|
|
if (poDS == nullptr && !bReadOnly)
|
|
{
|
|
poDS = static_cast<GDALDataset *>(GDALOpenEx(
|
|
pszDataSource, GDAL_OF_VECTOR, nullptr, papszOpenOptions, nullptr));
|
|
if (poDS != nullptr && bVerbose)
|
|
{
|
|
printf("Had to open data source read-only.\n");
|
|
bReadOnly = true;
|
|
}
|
|
}
|
|
|
|
GDALDriver *poDriver = nullptr;
|
|
if (poDS != nullptr)
|
|
poDriver = poDS->GetDriver();
|
|
*ppoDriver = poDriver;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Report failure */
|
|
/* -------------------------------------------------------------------- */
|
|
if (poDS == nullptr)
|
|
{
|
|
OGRSFDriverRegistrar *poR = OGRSFDriverRegistrar::GetRegistrar();
|
|
|
|
printf("FAILURE:\n"
|
|
"Unable to open datasource `%s' with the following drivers.\n",
|
|
pszDataSource);
|
|
|
|
for (int iDriver = 0; iDriver < poR->GetDriverCount(); iDriver++)
|
|
{
|
|
printf(" -> %s\n", poR->GetDriver(iDriver)->GetDescription());
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Some information messages. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (bVerbose)
|
|
printf("INFO: Open of `%s' using driver `%s' successful.\n",
|
|
pszDataSource, poDriver->GetDescription());
|
|
|
|
if (bVerbose && !EQUAL(pszDataSource, poDS->GetDescription()))
|
|
{
|
|
printf("INFO: Internal data source name `%s'\n"
|
|
" different from user name `%s'.\n",
|
|
poDS->GetDescription(), pszDataSource);
|
|
}
|
|
|
|
// Check that pszDomain == nullptr doesn't crash
|
|
poDS->GetMetadata(nullptr);
|
|
poDS->GetMetadataItem("", nullptr);
|
|
|
|
if (!poDriver->GetMetadataItem(GDAL_DCAP_VECTOR))
|
|
{
|
|
printf("FAILURE: Driver does not advertise GDAL_DCAP_VECTOR!\n");
|
|
bRet = false;
|
|
}
|
|
|
|
// Test consistency of datasource capabilities and driver metadata
|
|
if (poDS->TestCapability(ODsCCreateLayer) &&
|
|
!poDriver->GetMetadataItem(GDAL_DCAP_CREATE_LAYER))
|
|
{
|
|
printf("FAILURE: Dataset advertises ODsCCreateLayer capability but "
|
|
"driver metadata does not advertise GDAL_DCAP_CREATE_LAYER!\n");
|
|
bRet = false;
|
|
}
|
|
if (!bReadOnly &&
|
|
poDriver->GetMetadataItem(GDAL_DCAP_MULTIPLE_VECTOR_LAYERS) &&
|
|
poDriver->GetMetadataItem(GDAL_DCAP_CREATE_LAYER) &&
|
|
!poDS->TestCapability(ODsCCreateLayer))
|
|
{
|
|
printf("FAILURE: Driver advertises GDAL_DCAP_CREATE_LAYER and "
|
|
"GDAL_DCAP_MULTIPLE_VECTOR_LAYERS capability but dataset does "
|
|
"not advertise ODsCCreateLayer!\n");
|
|
bRet = false;
|
|
}
|
|
|
|
if (poDS->TestCapability(ODsCDeleteLayer) &&
|
|
!poDriver->GetMetadataItem(GDAL_DCAP_DELETE_LAYER))
|
|
{
|
|
printf("FAILURE: Dataset advertises ODsCDeleteLayer capability but "
|
|
"driver metadata does not advertise GDAL_DCAP_DELETE_LAYER!\n");
|
|
bRet = false;
|
|
}
|
|
|
|
if (poDriver->GetMetadataItem(GDAL_DCAP_CREATE_FIELD) &&
|
|
!poDriver->GetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES))
|
|
{
|
|
bRet = FALSE;
|
|
printf("FAILURE: Driver metadata advertises GDAL_DCAP_CREATE_FIELD but "
|
|
"does not include GDAL_DMD_CREATIONFIELDDATATYPES!\n");
|
|
}
|
|
if (poDriver->GetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES) &&
|
|
!poDriver->GetMetadataItem(GDAL_DCAP_CREATE_FIELD))
|
|
{
|
|
bRet = FALSE;
|
|
printf(
|
|
"FAILURE: Driver metadata includes GDAL_DMD_CREATIONFIELDDATATYPES "
|
|
"but does not advertise GDAL_DCAP_CREATE_FIELD!\n");
|
|
}
|
|
if (poDriver->GetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES) &&
|
|
!poDriver->GetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES))
|
|
{
|
|
bRet = FALSE;
|
|
printf("FAILURE: Driver metadata includes "
|
|
"GDAL_DMD_CREATIONFIELDDATASUBTYPES but does not include "
|
|
"GDAL_DMD_CREATIONFIELDDATATYPES!\n");
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Process optional SQL request. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (pszSQLStatement != nullptr)
|
|
{
|
|
OGRLayer *poResultSet =
|
|
poDS->ExecuteSQL(pszSQLStatement, nullptr, pszDialect);
|
|
if (poResultSet == nullptr)
|
|
{
|
|
GDALClose(poDS);
|
|
return FALSE;
|
|
}
|
|
|
|
if (bVerbose)
|
|
{
|
|
printf("INFO: Testing layer %s.\n", poResultSet->GetName());
|
|
}
|
|
bRet = TestOGRLayer(poDS, poResultSet, TRUE);
|
|
|
|
poDS->ReleaseResultSet(poResultSet);
|
|
|
|
bRetLocal = TestDSErrorConditions(poDS);
|
|
bRet &= bRetLocal;
|
|
|
|
bRetLocal = TestVirtualIO(poDS);
|
|
bRet &= bRetLocal;
|
|
}
|
|
/* -------------------------------------------------------------------- */
|
|
/* Process each data source layer. */
|
|
/* -------------------------------------------------------------------- */
|
|
else if (papszLayers == nullptr)
|
|
{
|
|
for (int iLayer = 0; iLayer < poDS->GetLayerCount(); iLayer++)
|
|
{
|
|
OGRLayer *poLayer = poDS->GetLayer(iLayer);
|
|
|
|
if (poLayer == nullptr)
|
|
{
|
|
printf("FAILURE: Couldn't fetch advertised layer %d!\n",
|
|
iLayer);
|
|
GDALClose(poDS);
|
|
return FALSE;
|
|
}
|
|
|
|
if (bVerbose)
|
|
{
|
|
printf("INFO: Testing layer %s.\n", poLayer->GetName());
|
|
}
|
|
bRet &= TestOGRLayer(poDS, poLayer, FALSE);
|
|
}
|
|
|
|
bRetLocal = TestDSErrorConditions(poDS);
|
|
bRet &= bRetLocal;
|
|
|
|
bRetLocal = TestVirtualIO(poDS);
|
|
bRet &= bRetLocal;
|
|
|
|
if (poDS->GetLayerCount() >= 2)
|
|
{
|
|
GDALClose(poDS);
|
|
poDS = nullptr;
|
|
bRetLocal = TestInterleavedReading(pszDataSource, nullptr);
|
|
bRet &= bRetLocal;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Or process layers specified by the user */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
char **papszLayerIter = papszLayers;
|
|
while (*papszLayerIter)
|
|
{
|
|
OGRLayer *poLayer = poDS->GetLayerByName(*papszLayerIter);
|
|
|
|
if (poLayer == nullptr)
|
|
{
|
|
printf("FAILURE: Couldn't fetch requested layer %s!\n",
|
|
*papszLayerIter);
|
|
GDALClose(poDS);
|
|
return FALSE;
|
|
}
|
|
|
|
if (bVerbose)
|
|
{
|
|
printf("INFO: Testing layer %s.\n", poLayer->GetName());
|
|
}
|
|
bRet &= TestOGRLayer(poDS, poLayer, FALSE);
|
|
|
|
papszLayerIter++;
|
|
}
|
|
|
|
bRetLocal = TestDSErrorConditions(poDS);
|
|
bRet &= bRetLocal;
|
|
|
|
bRetLocal = TestVirtualIO(poDS);
|
|
bRet &= bRetLocal;
|
|
|
|
if (CSLCount(papszLayers) >= 2)
|
|
{
|
|
GDALClose(poDS);
|
|
poDS = nullptr;
|
|
bRetLocal = TestInterleavedReading(pszDataSource, papszLayers);
|
|
bRet &= bRetLocal;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Close down. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (poDS != nullptr)
|
|
GDALClose(poDS);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetWKT() */
|
|
/************************************************************************/
|
|
|
|
static const char *GetWKT(OGRwkbGeometryType eGeomType)
|
|
{
|
|
const char *pszWKT = nullptr;
|
|
if (eGeomType == wkbUnknown || eGeomType == wkbPoint)
|
|
pszWKT = "POINT (0 0)";
|
|
else if (eGeomType == wkbLineString)
|
|
pszWKT = "LINESTRING (0 0,1 1)";
|
|
else if (eGeomType == wkbPolygon)
|
|
pszWKT = "POLYGON ((0 0,0 1,1 1,1 0,0 0))";
|
|
else if (eGeomType == wkbMultiPoint)
|
|
pszWKT = "MULTIPOINT (0 0)";
|
|
else if (eGeomType == wkbMultiLineString)
|
|
pszWKT = "MULTILINESTRING ((0 0,1 1))";
|
|
else if (eGeomType == wkbMultiPolygon)
|
|
pszWKT = "MULTIPOLYGON (((0 0,0 1,1 1,1 0,0 0)))";
|
|
else if (eGeomType == wkbGeometryCollection)
|
|
pszWKT = "GEOMETRYCOLLECTION (POINT (0 0),LINESTRING (0 0,1 1),"
|
|
"POLYGON ((0 0,0 1,1 1,1 0,0 0)))";
|
|
else if (eGeomType == wkbPoint25D)
|
|
pszWKT = "POINT (0 0 10)";
|
|
else if (eGeomType == wkbLineString25D)
|
|
pszWKT = "LINESTRING (0 0 10,1 1 10)";
|
|
else if (eGeomType == wkbPolygon25D)
|
|
pszWKT = "POLYGON ((0 0 10,0 1 10,1 1 10,1 0 10,0 0 10))";
|
|
else if (eGeomType == wkbMultiPoint25D)
|
|
pszWKT = "MULTIPOINT (0 0 10)";
|
|
else if (eGeomType == wkbMultiLineString25D)
|
|
pszWKT = "MULTILINESTRING ((0 0 10,1 1 10))";
|
|
else if (eGeomType == wkbMultiPolygon25D)
|
|
pszWKT = "MULTIPOLYGON (((0 0 10,0 1 10,1 1 10,1 0 10,0 0 10)))";
|
|
else if (eGeomType == wkbGeometryCollection25D)
|
|
pszWKT =
|
|
"GEOMETRYCOLLECTION (POINT (0 0 10),LINESTRING (0 0 10,1 1 10),"
|
|
"POLYGON ((0 0 10,0 1 10,1 1 10,1 0 10,0 0 10)))";
|
|
return pszWKT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestCreateLayer() */
|
|
/************************************************************************/
|
|
|
|
static int TestCreateLayer(GDALDriver *poDriver, OGRwkbGeometryType eGeomType)
|
|
{
|
|
int bRet = TRUE;
|
|
const char *pszExt = poDriver->GetMetadataItem(GDAL_DMD_EXTENSION);
|
|
|
|
static int nCounter = 0;
|
|
CPLString osFilename =
|
|
CPLFormFilename("/vsimem", CPLSPrintf("test%d", ++nCounter), pszExt);
|
|
GDALDataset *poDS = LOG_ACTION(
|
|
poDriver->Create(osFilename, 0, 0, 0, GDT_Unknown, papszDSCO));
|
|
if (poDS == nullptr)
|
|
{
|
|
if (bVerbose)
|
|
printf("INFO: %s: Creation of %s failed.\n",
|
|
poDriver->GetDescription(), osFilename.c_str());
|
|
return bRet;
|
|
}
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
int bCreateLayerCap = LOG_ACTION(poDS->TestCapability(ODsCCreateLayer));
|
|
OGRLayer *poLayer = LOG_ACTION(poDS->CreateLayer(
|
|
CPLGetFilename(osFilename), nullptr, eGeomType, papszLCO));
|
|
CPLPopErrorHandler();
|
|
CPLString osLayerNameToTest;
|
|
OGRwkbGeometryType eExpectedGeomType = wkbUnknown;
|
|
if (poLayer != nullptr)
|
|
{
|
|
if (bCreateLayerCap == FALSE)
|
|
{
|
|
printf("ERROR: %s: TestCapability(ODsCCreateLayer) returns FALSE "
|
|
"whereas layer creation was successful.\n",
|
|
poDriver->GetDescription());
|
|
bRet = FALSE;
|
|
}
|
|
|
|
if (LOG_ACTION(poLayer->GetLayerDefn()) == nullptr)
|
|
{
|
|
printf("ERROR: %s: GetLayerDefn() returns NUL just after layer "
|
|
"creation.\n",
|
|
poDriver->GetDescription());
|
|
bRet = FALSE;
|
|
}
|
|
|
|
auto poLyrDS = LOG_ACTION(poLayer->GetDataset());
|
|
if (!poLyrDS)
|
|
{
|
|
printf("ERROR: %s: GetDataset() returns NUL just after layer "
|
|
"creation.\n",
|
|
poDriver->GetDescription());
|
|
bRet = FALSE;
|
|
}
|
|
else if (poLyrDS != poDS)
|
|
{
|
|
printf("WARNING: %s: GetDataset()->GetDescription() (=%s) != %s"
|
|
"creation.\n",
|
|
poDriver->GetDescription(), poLyrDS->GetDescription(),
|
|
poDS->GetDescription());
|
|
}
|
|
|
|
// Create fields of various types
|
|
int bCreateField = LOG_ACTION(poLayer->TestCapability(OLCCreateField));
|
|
int iFieldStr = -1;
|
|
int iFieldInt = -1;
|
|
int iFieldReal = -1;
|
|
int iFieldDate = -1;
|
|
int iFieldDateTime = -1;
|
|
int bStrFieldOK;
|
|
{
|
|
OGRFieldDefn oFieldStr("str", OFTString);
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
bStrFieldOK =
|
|
LOG_ACTION(poLayer->CreateField(&oFieldStr)) == OGRERR_NONE;
|
|
CPLPopErrorHandler();
|
|
if (bStrFieldOK && (iFieldStr = LOG_ACTION(poLayer->GetLayerDefn())
|
|
->GetFieldIndex("str")) < 0)
|
|
{
|
|
printf("ERROR: %s: CreateField(str) returned OK "
|
|
"but field was not created.\n",
|
|
poDriver->GetDescription());
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
|
|
OGRFieldDefn oFieldInt("int", OFTInteger);
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
const bool bIntFieldOK =
|
|
LOG_ACTION(poLayer->CreateField(&oFieldInt)) == OGRERR_NONE;
|
|
CPLPopErrorHandler();
|
|
if (bIntFieldOK &&
|
|
(iFieldInt = poLayer->GetLayerDefn()->GetFieldIndex("int")) < 0)
|
|
{
|
|
printf("ERROR: %s: CreateField(int) returned OK "
|
|
"but field was not created.\n",
|
|
poDriver->GetDescription());
|
|
bRet = FALSE;
|
|
}
|
|
|
|
OGRFieldDefn oFieldReal("real", OFTReal);
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
const bool bRealFieldOK =
|
|
LOG_ACTION(poLayer->CreateField(&oFieldReal)) == OGRERR_NONE;
|
|
CPLPopErrorHandler();
|
|
if (bRealFieldOK &&
|
|
(iFieldReal = poLayer->GetLayerDefn()->GetFieldIndex("real")) < 0)
|
|
{
|
|
printf("ERROR: %s: CreateField(real) returned OK but field was not "
|
|
"created.\n",
|
|
poDriver->GetDescription());
|
|
bRet = FALSE;
|
|
}
|
|
|
|
OGRFieldDefn oFieldDate("mydate", OFTDate);
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
const bool bDateFieldOK =
|
|
LOG_ACTION(poLayer->CreateField(&oFieldDate)) == OGRERR_NONE;
|
|
CPLPopErrorHandler();
|
|
if (bDateFieldOK &&
|
|
(iFieldDate = poLayer->GetLayerDefn()->GetFieldIndex("mydate")) < 0)
|
|
{
|
|
printf("ERROR: %s: CreateField(mydate) returned OK but field was "
|
|
"not created.\n",
|
|
poDriver->GetDescription());
|
|
bRet = FALSE;
|
|
}
|
|
|
|
OGRFieldDefn oFieldDateTime("datetime", OFTDateTime);
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
const bool bDateTimeFieldOK =
|
|
LOG_ACTION(poLayer->CreateField(&oFieldDateTime)) == OGRERR_NONE;
|
|
CPLPopErrorHandler();
|
|
if (bDateTimeFieldOK &&
|
|
(iFieldDateTime =
|
|
poLayer->GetLayerDefn()->GetFieldIndex("datetime")) < 0)
|
|
{
|
|
printf("ERROR: %s: CreateField(datetime) returned OK but field was "
|
|
"not created.\n",
|
|
poDriver->GetDescription());
|
|
bRet = FALSE;
|
|
}
|
|
|
|
if (bCreateField == FALSE &&
|
|
(bStrFieldOK || bIntFieldOK || bRealFieldOK || bDateFieldOK ||
|
|
bDateTimeFieldOK))
|
|
{
|
|
printf("ERROR: %s: TestCapability(OLCCreateField) returns FALSE.\n",
|
|
poDriver->GetDescription());
|
|
bRet = FALSE;
|
|
}
|
|
|
|
if (LOG_ACTION(poLayer->TestCapability(OLCSequentialWrite)) == FALSE)
|
|
{
|
|
printf("ERROR: %s: TestCapability(OLCSequentialWrite) returns "
|
|
"FALSE.\n",
|
|
poDriver->GetDescription());
|
|
bRet = FALSE;
|
|
}
|
|
|
|
/* Test creating empty feature */
|
|
OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
|
|
CPLErrorReset();
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
OGRErr eErr = LOG_ACTION(poLayer->CreateFeature(poFeature));
|
|
CPLPopErrorHandler();
|
|
if (eErr != OGRERR_NONE && CPLGetLastErrorType() == 0)
|
|
{
|
|
printf("INFO: %s: CreateFeature() at line %d failed but without "
|
|
"explicit error.\n",
|
|
poDriver->GetDescription(), __LINE__);
|
|
}
|
|
if (eErr == OGRERR_NONE && poFeature->GetFID() < 0 &&
|
|
eGeomType == wkbUnknown)
|
|
{
|
|
printf("INFO: %s: CreateFeature() at line %d succeeded "
|
|
"but failed to assign FID to feature.\n",
|
|
poDriver->GetDescription(), __LINE__);
|
|
}
|
|
delete poFeature;
|
|
|
|
/* Test creating feature with all fields set */
|
|
poFeature = new OGRFeature(poLayer->GetLayerDefn());
|
|
if (bStrFieldOK)
|
|
poFeature->SetField(iFieldStr, "foo");
|
|
if (bIntFieldOK)
|
|
poFeature->SetField(iFieldInt, 123);
|
|
if (bRealFieldOK)
|
|
poFeature->SetField(iFieldReal, 1.23);
|
|
if (bDateFieldOK)
|
|
poFeature->SetField(iFieldDate, "2014/10/20");
|
|
if (bDateTimeFieldOK)
|
|
poFeature->SetField(iFieldDateTime, "2014/10/20 12:34:56");
|
|
CPLErrorReset();
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
eErr = LOG_ACTION(poLayer->CreateFeature(poFeature));
|
|
CPLPopErrorHandler();
|
|
if (eErr != OGRERR_NONE && CPLGetLastErrorType() == 0)
|
|
{
|
|
printf("INFO: %s: CreateFeature() at line %d failed "
|
|
"but without explicit error.\n",
|
|
poDriver->GetDescription(), __LINE__);
|
|
}
|
|
delete poFeature;
|
|
|
|
/* Test creating feature with all fields set as well as geometry */
|
|
poFeature = new OGRFeature(poLayer->GetLayerDefn());
|
|
if (bStrFieldOK)
|
|
poFeature->SetField(iFieldStr, "foo");
|
|
if (bIntFieldOK)
|
|
poFeature->SetField(iFieldInt, 123);
|
|
if (bRealFieldOK)
|
|
poFeature->SetField(iFieldReal, 1.23);
|
|
if (bDateFieldOK)
|
|
poFeature->SetField(iFieldDate, "2014/10/20");
|
|
if (bDateTimeFieldOK)
|
|
poFeature->SetField(iFieldDateTime, "2014/10/20 12:34:56");
|
|
|
|
const char *pszWKT = GetWKT(eGeomType);
|
|
if (pszWKT != nullptr)
|
|
{
|
|
OGRGeometry *poGeom = nullptr;
|
|
OGRGeometryFactory::createFromWkt(pszWKT, nullptr, &poGeom);
|
|
poFeature->SetGeometryDirectly(poGeom);
|
|
}
|
|
|
|
CPLErrorReset();
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
eErr = LOG_ACTION(poLayer->CreateFeature(poFeature));
|
|
CPLPopErrorHandler();
|
|
if (eErr != OGRERR_NONE && CPLGetLastErrorType() == 0)
|
|
{
|
|
printf("INFO: %s: CreateFeature() at line %d failed "
|
|
"but without explicit error.\n",
|
|
poDriver->GetDescription(), __LINE__);
|
|
}
|
|
delete poFeature;
|
|
|
|
/* Test feature with incompatible geometry */
|
|
poFeature = new OGRFeature(poLayer->GetLayerDefn());
|
|
if (bStrFieldOK)
|
|
poFeature->SetField(iFieldStr, "foo");
|
|
if (bIntFieldOK)
|
|
poFeature->SetField(iFieldInt, 123);
|
|
if (bRealFieldOK)
|
|
poFeature->SetField(iFieldReal, 1.23);
|
|
if (bDateFieldOK)
|
|
poFeature->SetField(iFieldDate, "2014/10/20");
|
|
if (bDateTimeFieldOK)
|
|
poFeature->SetField(iFieldDateTime, "2014/10/20 12:34:56");
|
|
|
|
OGRwkbGeometryType eOtherGeomType;
|
|
if (eGeomType == wkbUnknown || eGeomType == wkbNone)
|
|
eOtherGeomType = wkbLineString;
|
|
else if (wkbFlatten(eGeomType) == eGeomType)
|
|
eOtherGeomType = static_cast<OGRwkbGeometryType>(
|
|
(static_cast<int>(eGeomType) % 7) + 1);
|
|
else
|
|
eOtherGeomType = wkbSetZ(static_cast<OGRwkbGeometryType>(
|
|
((static_cast<int>(wkbFlatten(eGeomType)) % 7) + 1)));
|
|
pszWKT = GetWKT(eOtherGeomType);
|
|
if (pszWKT != nullptr)
|
|
{
|
|
OGRGeometry *poGeom = nullptr;
|
|
OGRGeometryFactory::createFromWkt(pszWKT, nullptr, &poGeom);
|
|
poFeature->SetGeometryDirectly(poGeom);
|
|
}
|
|
|
|
CPLErrorReset();
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
eErr = LOG_ACTION(poLayer->CreateFeature(poFeature));
|
|
CPLPopErrorHandler();
|
|
if (eErr != OGRERR_NONE && CPLGetLastErrorType() == 0)
|
|
{
|
|
printf("INFO: %s: CreateFeature() at line %d failed "
|
|
"but without explicit error.\n",
|
|
poDriver->GetDescription(), __LINE__);
|
|
}
|
|
delete poFeature;
|
|
|
|
/* Test reading a feature: write-only layers might not like this */
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
delete LOG_ACTION(poLayer->GetNextFeature());
|
|
CPLPopErrorHandler();
|
|
|
|
osLayerNameToTest = poLayer->GetName();
|
|
eExpectedGeomType = poLayer->GetGeomType();
|
|
|
|
/* Some drivers don't like more than one layer per dataset */
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
const int bCreateLayerCap2 =
|
|
LOG_ACTION(poDS->TestCapability(ODsCCreateLayer));
|
|
OGRLayer *poLayer2 = LOG_ACTION(poDS->CreateLayer(
|
|
CPLSPrintf("%s2", CPLGetFilename(osFilename)), nullptr, eGeomType));
|
|
CPLPopErrorHandler();
|
|
if (poLayer2 == nullptr && bCreateLayerCap2)
|
|
{
|
|
printf("INFO: %s: Creation of second layer failed but "
|
|
"TestCapability(ODsCCreateLayer) succeeded.\n",
|
|
poDriver->GetDescription());
|
|
}
|
|
else if (!EQUAL(poDriver->GetDescription(), "CSV") &&
|
|
poLayer2 != nullptr)
|
|
{
|
|
OGRFieldDefn oFieldStr("str", OFTString);
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
LOG_ACTION(poLayer2->CreateField(&oFieldStr));
|
|
CPLPopErrorHandler();
|
|
|
|
poFeature = new OGRFeature(poLayer2->GetLayerDefn());
|
|
pszWKT = GetWKT(eGeomType);
|
|
if (pszWKT != nullptr)
|
|
{
|
|
OGRGeometry *poGeom = nullptr;
|
|
OGRGeometryFactory::createFromWkt(pszWKT, nullptr, &poGeom);
|
|
poFeature->SetGeometryDirectly(poGeom);
|
|
}
|
|
CPLErrorReset();
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
eErr = LOG_ACTION(poLayer2->CreateFeature(poFeature));
|
|
CPLPopErrorHandler();
|
|
delete poFeature;
|
|
|
|
if (eErr == OGRERR_NONE)
|
|
{
|
|
osLayerNameToTest = poLayer2->GetName();
|
|
eExpectedGeomType = poLayer2->GetGeomType();
|
|
}
|
|
}
|
|
|
|
/* Test deleting first layer */
|
|
const int bDeleteLayerCap =
|
|
LOG_ACTION(poDS->TestCapability(ODsCDeleteLayer));
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
eErr = LOG_ACTION(poDS->DeleteLayer(0));
|
|
CPLPopErrorHandler();
|
|
if (eErr == OGRERR_NONE)
|
|
{
|
|
if (!bDeleteLayerCap)
|
|
{
|
|
printf("ERROR: %s: TestCapability(ODsCDeleteLayer) "
|
|
"returns FALSE but layer deletion worked.\n",
|
|
poDriver->GetDescription());
|
|
bRet = FALSE;
|
|
}
|
|
|
|
if (LOG_ACTION(poDS->GetLayerByName(CPLGetFilename(osFilename))) !=
|
|
nullptr)
|
|
{
|
|
printf("ERROR: %s: DeleteLayer() declared success, "
|
|
"but layer can still be fetched.\n",
|
|
poDriver->GetDescription());
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bDeleteLayerCap)
|
|
{
|
|
printf("ERROR: %s: TestCapability(ODsCDeleteLayer) "
|
|
"returns TRUE but layer deletion failed.\n",
|
|
poDriver->GetDescription());
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
}
|
|
/*else
|
|
{
|
|
if( bVerbose )
|
|
printf("INFO: %s: Creation of layer with geom_type = %s failed.\n",
|
|
poDriver->GetDescription(),
|
|
OGRGeometryTypeToName(eGeomType));
|
|
}*/
|
|
LOG_ACTION(GDALClose(poDS));
|
|
|
|
if (eExpectedGeomType != wkbUnknown &&
|
|
/* Those drivers are expected not to store a layer geometry type */
|
|
!EQUAL(poDriver->GetDescription(), "KML") &&
|
|
!EQUAL(poDriver->GetDescription(), "LIBKML") &&
|
|
!EQUAL(poDriver->GetDescription(), "PDF") &&
|
|
!EQUAL(poDriver->GetDescription(), "GeoJSON") &&
|
|
!EQUAL(poDriver->GetDescription(), "JSONFG") &&
|
|
!EQUAL(poDriver->GetDescription(), "OGR_GMT") &&
|
|
!EQUAL(poDriver->GetDescription(), "PDS4") &&
|
|
!EQUAL(poDriver->GetDescription(), "FlatGeobuf"))
|
|
{
|
|
/* Reopen dataset */
|
|
poDS = LOG_ACTION(static_cast<GDALDataset *>(
|
|
GDALOpenEx(osFilename, GDAL_OF_VECTOR, nullptr, nullptr, nullptr)));
|
|
if (poDS != nullptr)
|
|
{
|
|
poLayer = LOG_ACTION(poDS->GetLayerByName(osLayerNameToTest));
|
|
if (poLayer != nullptr)
|
|
{
|
|
if (poLayer->GetGeomType() != eExpectedGeomType &&
|
|
!(eGeomType == wkbGeometryCollection25D &&
|
|
EQUAL(poDriver->GetDescription(), "OpenFileGDB")))
|
|
{
|
|
printf("ERROR: %s: GetGeomType() returns %d but %d "
|
|
"was expected (and %d originally set).\n",
|
|
poDriver->GetDescription(), poLayer->GetGeomType(),
|
|
eExpectedGeomType, eGeomType);
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
LOG_ACTION(GDALClose(poDS));
|
|
}
|
|
}
|
|
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
LOG_ACTION(poDriver->Delete(osFilename));
|
|
CPLPopErrorHandler();
|
|
VSIUnlink(osFilename);
|
|
|
|
if (poLayer != nullptr)
|
|
{
|
|
/* Test creating empty layer */
|
|
osFilename = CPLFormFilename("/vsimem",
|
|
CPLSPrintf("test%d", ++nCounter), pszExt);
|
|
poDS = LOG_ACTION(
|
|
poDriver->Create(osFilename, 0, 0, 0, GDT_Unknown, nullptr));
|
|
if (poDS != nullptr)
|
|
{
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
LOG_ACTION(poDS->CreateLayer(CPLGetFilename(osFilename), nullptr,
|
|
eGeomType));
|
|
CPLPopErrorHandler();
|
|
LOG_ACTION(GDALClose(poDS));
|
|
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
LOG_ACTION(poDriver->Delete(osFilename));
|
|
CPLPopErrorHandler();
|
|
VSIUnlink(osFilename);
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestCreate() */
|
|
/************************************************************************/
|
|
|
|
static int TestCreate(GDALDriver *poDriver, int bFromAllDrivers)
|
|
{
|
|
int bRet = TRUE;
|
|
const bool bVirtualIO =
|
|
poDriver->GetMetadataItem(GDAL_DCAP_VIRTUALIO) != nullptr;
|
|
if (poDriver->GetMetadataItem(GDAL_DCAP_CREATE) == nullptr || !bVirtualIO)
|
|
{
|
|
if (bVerbose && !bFromAllDrivers)
|
|
printf("INFO: %s: TestCreate skipped.\n",
|
|
poDriver->GetDescription());
|
|
return TRUE;
|
|
}
|
|
|
|
printf("%s\n", LOG_STR(CPLSPrintf("INFO: TestCreate(%s).",
|
|
poDriver->GetDescription())));
|
|
|
|
const char *pszExt = poDriver->GetMetadataItem(GDAL_DMD_EXTENSION);
|
|
CPLString osFilename = CPLFormFilename("/foo", "test", pszExt);
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
GDALDataset *poDS =
|
|
LOG_ACTION(poDriver->Create(osFilename, 0, 0, 0, GDT_Unknown, nullptr));
|
|
CPLPopErrorHandler();
|
|
if (poDS != nullptr)
|
|
{
|
|
/* Sometimes actual file creation is deferred */
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
OGRLayer *poLayer =
|
|
LOG_ACTION(poDS->CreateLayer("test", nullptr, wkbPoint));
|
|
CPLPopErrorHandler();
|
|
|
|
/* Or sometimes writing is deferred at dataset closing */
|
|
CPLErrorReset();
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
LOG_ACTION(GDALClose(poDS));
|
|
CPLPopErrorHandler();
|
|
if (poLayer != nullptr && CPLGetLastErrorType() == 0)
|
|
{
|
|
printf("INFO: %s: Creation of %s should have failed.\n",
|
|
poDriver->GetDescription(), osFilename.c_str());
|
|
}
|
|
}
|
|
|
|
bRet &= LOG_ACTION(TestCreateLayer(poDriver, wkbUnknown));
|
|
bRet &= LOG_ACTION(TestCreateLayer(poDriver, wkbNone));
|
|
bRet &= LOG_ACTION(TestCreateLayer(poDriver, wkbPoint));
|
|
bRet &= LOG_ACTION(TestCreateLayer(poDriver, wkbLineString));
|
|
bRet &= LOG_ACTION(TestCreateLayer(poDriver, wkbPolygon));
|
|
bRet &= LOG_ACTION(TestCreateLayer(poDriver, wkbMultiPoint));
|
|
bRet &= LOG_ACTION(TestCreateLayer(poDriver, wkbMultiLineString));
|
|
bRet &= LOG_ACTION(TestCreateLayer(poDriver, wkbMultiPolygon));
|
|
bRet &= LOG_ACTION(TestCreateLayer(poDriver, wkbGeometryCollection));
|
|
bRet &= LOG_ACTION(TestCreateLayer(poDriver, wkbPoint25D));
|
|
bRet &= LOG_ACTION(TestCreateLayer(poDriver, wkbLineString25D));
|
|
bRet &= LOG_ACTION(TestCreateLayer(poDriver, wkbPolygon25D));
|
|
bRet &= LOG_ACTION(TestCreateLayer(poDriver, wkbMultiPoint25D));
|
|
bRet &= LOG_ACTION(TestCreateLayer(poDriver, wkbMultiLineString25D));
|
|
bRet &= LOG_ACTION(TestCreateLayer(poDriver, wkbMultiPolygon25D));
|
|
bRet &= LOG_ACTION(TestCreateLayer(poDriver, wkbGeometryCollection25D));
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Usage() */
|
|
/************************************************************************/
|
|
|
|
static void Usage()
|
|
|
|
{
|
|
printf("Usage: test_ogrsf [-ro] [-q] [-threads N] [-loops M] [-fsf]\n"
|
|
" (datasource_name | [-driver driver_name] [[-dsco "
|
|
"NAME=VALUE] ...] [[-lco NAME=VALUE] ...] | -all_drivers) \n"
|
|
" [[layer1_name, layer2_name, ...] | [-sql "
|
|
"statement] [-dialect dialect]]\n"
|
|
" [[-oo NAME=VALUE] ...]\n");
|
|
printf("\n");
|
|
printf("-fsf : full spatial filter testing (slow)\n");
|
|
exit(1);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestBasic() */
|
|
/************************************************************************/
|
|
|
|
static int TestBasic(GDALDataset *poDS, OGRLayer *poLayer)
|
|
{
|
|
int bRet = TRUE;
|
|
|
|
const char *pszLayerName = LOG_ACTION(poLayer->GetName());
|
|
const OGRwkbGeometryType eGeomType = LOG_ACTION(poLayer->GetGeomType());
|
|
OGRFeatureDefn *poFDefn = LOG_ACTION(poLayer->GetLayerDefn());
|
|
|
|
if (strcmp(pszLayerName, LOG_ACTION(poFDefn->GetName())) != 0)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: poLayer->GetName() and poFDefn->GetName() differ.\n"
|
|
"poLayer->GetName() = %s\n"
|
|
"poFDefn->GetName() = %s\n",
|
|
pszLayerName, poFDefn->GetName());
|
|
}
|
|
|
|
if (strcmp(pszLayerName, LOG_ACTION(poLayer->GetDescription())) != 0)
|
|
{
|
|
bRet = FALSE;
|
|
printf(
|
|
"ERROR: poLayer->GetName() and poLayer->GetDescription() differ.\n"
|
|
"poLayer->GetName() = %s\n"
|
|
"poLayer->GetDescription() = %s\n",
|
|
pszLayerName, poLayer->GetDescription());
|
|
}
|
|
|
|
if (eGeomType != LOG_ACTION(poFDefn->GetGeomType()))
|
|
{
|
|
bRet = FALSE;
|
|
printf(
|
|
"ERROR: poLayer->GetGeomType() and poFDefn->GetGeomType() differ.\n"
|
|
"poLayer->GetGeomType() = %d\n"
|
|
"poFDefn->GetGeomType() = %d\n",
|
|
eGeomType, poFDefn->GetGeomType());
|
|
}
|
|
|
|
if (LOG_ACTION(poLayer->GetFIDColumn()) == nullptr)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: poLayer->GetFIDColumn() returned NULL.\n");
|
|
}
|
|
|
|
if (LOG_ACTION(poLayer->GetGeometryColumn()) == nullptr)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: poLayer->GetGeometryColumn() returned NULL.\n");
|
|
}
|
|
|
|
if (LOG_ACTION(poFDefn->GetGeomFieldCount()) > 0)
|
|
{
|
|
if (eGeomType != LOG_ACTION(poFDefn->GetGeomFieldDefn(0))->GetType())
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: poLayer->GetGeomType() and "
|
|
"poFDefn->GetGeomFieldDefn(0)->GetType() differ.\n"
|
|
"poLayer->GetGeomType() = %d\n"
|
|
"poFDefn->GetGeomFieldDefn(0)->GetType() = %d\n",
|
|
eGeomType, poFDefn->GetGeomFieldDefn(0)->GetType());
|
|
}
|
|
|
|
if (!EQUAL(LOG_ACTION(poLayer->GetGeometryColumn()),
|
|
poFDefn->GetGeomFieldDefn(0)->GetNameRef()))
|
|
{
|
|
if (poFDefn->GetGeomFieldCount() > 1)
|
|
bRet = FALSE;
|
|
printf("%s: poLayer->GetGeometryColumn() and "
|
|
"poFDefn->GetGeomFieldDefn(0)->GetNameRef() differ.\n"
|
|
"poLayer->GetGeometryColumn() = %s\n"
|
|
"poFDefn->GetGeomFieldDefn(0)->GetNameRef() = %s\n",
|
|
(poFDefn->GetGeomFieldCount() == 1) ? "WARNING" : "ERROR",
|
|
poLayer->GetGeometryColumn(),
|
|
poFDefn->GetGeomFieldDefn(0)->GetNameRef());
|
|
}
|
|
|
|
if (LOG_ACTION(poLayer->GetSpatialRef()) !=
|
|
LOG_ACTION(poFDefn->GetGeomFieldDefn(0)->GetSpatialRef()))
|
|
{
|
|
if (poFDefn->GetGeomFieldCount() > 1)
|
|
bRet = FALSE;
|
|
printf("%s: poLayer->GetSpatialRef() and "
|
|
"poFDefn->GetGeomFieldDefn(0)->GetSpatialRef() differ.\n"
|
|
"poLayer->GetSpatialRef() = %p\n"
|
|
"poFDefn->GetGeomFieldDefn(0)->GetSpatialRef() = %p\n",
|
|
(poFDefn->GetGeomFieldCount() == 1) ? "WARNING" : "ERROR",
|
|
poLayer->GetSpatialRef(),
|
|
poFDefn->GetGeomFieldDefn(0)->GetSpatialRef());
|
|
}
|
|
}
|
|
|
|
// Test consistency of layer capabilities and driver metadata
|
|
if (poDS)
|
|
{
|
|
GDALDriver *poDriver = poDS->GetDriver();
|
|
|
|
const bool bLayerShouldHaveEditCapabilities =
|
|
!bReadOnly && !pszSQLStatement;
|
|
|
|
// metadata measure tests
|
|
if (poLayer->TestCapability(OLCMeasuredGeometries) &&
|
|
!poDriver->GetMetadataItem(GDAL_DCAP_MEASURED_GEOMETRIES))
|
|
{
|
|
bRet = FALSE;
|
|
printf("FAILURE: Layer advertises OLCMeasuredGeometries capability "
|
|
"but driver metadata does not advertise "
|
|
"GDAL_DCAP_MEASURED_GEOMETRIES!\n");
|
|
}
|
|
if (poDS->TestCapability(ODsCMeasuredGeometries) &&
|
|
!poDriver->GetMetadataItem(GDAL_DCAP_MEASURED_GEOMETRIES))
|
|
{
|
|
bRet = FALSE;
|
|
printf("FAILURE: Datasource advertises ODsCMeasuredGeometries "
|
|
"capability but driver metadata does not advertise "
|
|
"GDAL_DCAP_MEASURED_GEOMETRIES!\n");
|
|
}
|
|
if (poLayer->TestCapability(OLCMeasuredGeometries) &&
|
|
!poDS->TestCapability(ODsCMeasuredGeometries))
|
|
{
|
|
bRet = FALSE;
|
|
printf(
|
|
"FAILURE: Layer advertises OLCMeasuredGeometries capability "
|
|
"but datasource does not advertise ODsCMeasuredGeometries!\n");
|
|
}
|
|
|
|
// metadata curve tests
|
|
if (poLayer->TestCapability(OLCCurveGeometries) &&
|
|
!poDriver->GetMetadataItem(GDAL_DCAP_CURVE_GEOMETRIES))
|
|
{
|
|
bRet = FALSE;
|
|
printf("FAILURE: Layer advertises OLCCurveGeometries capability "
|
|
"but driver metadata does not advertise "
|
|
"GDAL_DCAP_CURVE_GEOMETRIES!\n");
|
|
}
|
|
if (poDS->TestCapability(ODsCCurveGeometries) &&
|
|
!poDriver->GetMetadataItem(GDAL_DCAP_CURVE_GEOMETRIES))
|
|
{
|
|
bRet = FALSE;
|
|
printf("FAILURE: Datasource advertises ODsCCurveGeometries "
|
|
"capability but driver metadata does not advertise "
|
|
"GDAL_DCAP_CURVE_GEOMETRIES!\n");
|
|
}
|
|
if (poLayer->TestCapability(OLCCurveGeometries) &&
|
|
!poDS->TestCapability(ODsCCurveGeometries))
|
|
{
|
|
bRet = FALSE;
|
|
printf("FAILURE: Layer advertises OLCCurveGeometries capability "
|
|
"but datasource does not advertise ODsCCurveGeometries!\n");
|
|
}
|
|
|
|
// metadata z dimension tests
|
|
if (poLayer->TestCapability(OLCZGeometries) &&
|
|
!poDriver->GetMetadataItem(GDAL_DCAP_Z_GEOMETRIES))
|
|
{
|
|
bRet = FALSE;
|
|
printf(
|
|
"FAILURE: Layer advertises OLCZGeometries capability but "
|
|
"driver metadata does not advertise GDAL_DCAP_Z_GEOMETRIES!\n");
|
|
}
|
|
if (poDS->TestCapability(ODsCZGeometries) &&
|
|
!poDriver->GetMetadataItem(GDAL_DCAP_Z_GEOMETRIES))
|
|
{
|
|
bRet = FALSE;
|
|
printf(
|
|
"FAILURE: Datasource advertises ODsCZGeometries capability but "
|
|
"driver metadata does not advertise GDAL_DCAP_Z_GEOMETRIES!\n");
|
|
}
|
|
if (poLayer->TestCapability(OLCZGeometries) &&
|
|
!poDS->TestCapability(ODsCZGeometries))
|
|
{
|
|
bRet = FALSE;
|
|
printf("FAILURE: Layer advertises OLCZGeometries capability but "
|
|
"datasource does not advertise ODsCZGeometries!\n");
|
|
}
|
|
|
|
// note -- it's not safe to test the reverse case for these next two
|
|
// situations as some drivers only support CreateField() on newly
|
|
// created layers before the first feature is written
|
|
if (poLayer->TestCapability(OLCCreateField) &&
|
|
!poDriver->GetMetadataItem(GDAL_DCAP_CREATE_FIELD))
|
|
{
|
|
bRet = FALSE;
|
|
printf(
|
|
"FAILURE: Layer advertises OLCCreateField capability but "
|
|
"driver metadata does not advertise GDAL_DCAP_CREATE_FIELD!\n");
|
|
}
|
|
if (poLayer->TestCapability(OLCCreateField) &&
|
|
!poDriver->GetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES))
|
|
{
|
|
bRet = FALSE;
|
|
printf("FAILURE: Layer advertises OLCCreateField capability but "
|
|
"driver metadata does not include "
|
|
"GDAL_DMD_CREATIONFIELDDATATYPES!\n");
|
|
}
|
|
|
|
if (poLayer->TestCapability(OLCDeleteField) &&
|
|
!poDriver->GetMetadataItem(GDAL_DCAP_DELETE_FIELD))
|
|
{
|
|
bRet = FALSE;
|
|
printf(
|
|
"FAILURE: Layer advertises OLCDeleteField capability but "
|
|
"driver metadata does not advertise GDAL_DCAP_DELETE_FIELD!\n");
|
|
}
|
|
if (bLayerShouldHaveEditCapabilities &&
|
|
poDriver->GetMetadataItem(GDAL_DCAP_DELETE_FIELD) &&
|
|
!poLayer->TestCapability(OLCDeleteField))
|
|
{
|
|
bRet = FALSE;
|
|
printf("FAILURE: Driver metadata advertises GDAL_DCAP_DELETE_FIELD "
|
|
"but layer capability does not advertise OLCDeleteField!\n");
|
|
}
|
|
|
|
if (poLayer->TestCapability(OLCReorderFields) &&
|
|
!poDriver->GetMetadataItem(GDAL_DCAP_REORDER_FIELDS))
|
|
{
|
|
bRet = FALSE;
|
|
printf("FAILURE: Layer advertises OLCReorderFields capability but "
|
|
"driver metadata does not advertise "
|
|
"GDAL_DCAP_REORDER_FIELDS!\n");
|
|
}
|
|
if (bLayerShouldHaveEditCapabilities &&
|
|
poDriver->GetMetadataItem(GDAL_DCAP_REORDER_FIELDS) &&
|
|
!poLayer->TestCapability(OLCReorderFields))
|
|
{
|
|
bRet = FALSE;
|
|
printf(
|
|
"FAILURE: Driver metadata advertises GDAL_DCAP_REORDER_FIELDS "
|
|
"but layer capability does not advertise OLCReorderFields!\n");
|
|
}
|
|
|
|
if (poLayer->TestCapability(OLCAlterFieldDefn) &&
|
|
!poDriver->GetMetadataItem(GDAL_DMD_ALTER_FIELD_DEFN_FLAGS))
|
|
{
|
|
bRet = FALSE;
|
|
printf("FAILURE: Layer advertises OLCAlterFieldDefn capability but "
|
|
"driver metadata does not include "
|
|
"GDAL_DMD_ALTER_FIELD_DEFN_FLAGS!\n");
|
|
}
|
|
if (bLayerShouldHaveEditCapabilities &&
|
|
poDriver->GetMetadataItem(GDAL_DMD_ALTER_FIELD_DEFN_FLAGS) &&
|
|
!poLayer->TestCapability(OLCAlterFieldDefn))
|
|
{
|
|
bRet = FALSE;
|
|
printf("FAILURE: Driver metadata advertises "
|
|
"GDAL_DMD_ALTER_FIELD_DEFN_FLAGS but layer capability does "
|
|
"not advertise OLCAlterFieldDefn!\n");
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestLayerErrorConditions() */
|
|
/************************************************************************/
|
|
|
|
static int TestLayerErrorConditions(OGRLayer *poLyr)
|
|
{
|
|
int bRet = TRUE;
|
|
OGRFeature *poFeat = nullptr;
|
|
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
|
|
if (LOG_ACTION(poLyr->TestCapability("fake_capability")))
|
|
{
|
|
printf("ERROR: poLyr->TestCapability(\"fake_capability\") "
|
|
"should have returned FALSE\n");
|
|
bRet = FALSE;
|
|
goto bye;
|
|
}
|
|
|
|
if (LOG_ACTION(poLyr->GetFeature(-10)) != nullptr)
|
|
{
|
|
printf("ERROR: GetFeature(-10) should have returned NULL\n");
|
|
bRet = FALSE;
|
|
goto bye;
|
|
}
|
|
|
|
if (LOG_ACTION(poLyr->GetFeature(2000000000)) != nullptr)
|
|
{
|
|
printf("ERROR: GetFeature(2000000000) should have returned NULL\n");
|
|
bRet = FALSE;
|
|
goto bye;
|
|
}
|
|
|
|
// This should detect int overflow
|
|
if (LOG_ACTION(poLyr->GetFeature(
|
|
static_cast<GIntBig>(std::numeric_limits<int>::max()) + 1)) !=
|
|
nullptr)
|
|
{
|
|
printf("ERROR: GetFeature((GIntBig)INT_MAX + 1) "
|
|
"should have returned NULL\n");
|
|
bRet = FALSE;
|
|
goto bye;
|
|
}
|
|
|
|
poLyr->ResetReading();
|
|
poFeat = poLyr->GetNextFeature();
|
|
if (poFeat)
|
|
{
|
|
poFeat->SetFID(-10);
|
|
if (poLyr->SetFeature(poFeat) == OGRERR_NONE)
|
|
{
|
|
printf("ERROR: SetFeature(-10) should have returned an error\n");
|
|
delete poFeat;
|
|
bRet = FALSE;
|
|
goto bye;
|
|
}
|
|
delete poFeat;
|
|
}
|
|
|
|
if (poLyr->DeleteFeature(-10) == OGRERR_NONE)
|
|
{
|
|
printf("ERROR: DeleteFeature(-10) should have returned an error\n");
|
|
bRet = FALSE;
|
|
goto bye;
|
|
}
|
|
|
|
if (poLyr->DeleteFeature(2000000000) == OGRERR_NONE)
|
|
{
|
|
printf("ERROR: DeleteFeature(2000000000) should have "
|
|
"returned an error\n");
|
|
bRet = FALSE;
|
|
goto bye;
|
|
}
|
|
|
|
if (LOG_ACTION(poLyr->SetNextByIndex(-10)) != OGRERR_FAILURE)
|
|
{
|
|
printf("ERROR: SetNextByIndex(-10) should have "
|
|
"returned OGRERR_FAILURE\n");
|
|
bRet = FALSE;
|
|
goto bye;
|
|
}
|
|
|
|
if (LOG_ACTION(poLyr->SetNextByIndex(2000000000)) == OGRERR_NONE &&
|
|
LOG_ACTION(poLyr->GetNextFeature()) != nullptr)
|
|
{
|
|
printf("ERROR: SetNextByIndex(2000000000) and then GetNextFeature() "
|
|
"should have returned NULL\n");
|
|
bRet = FALSE;
|
|
goto bye;
|
|
}
|
|
|
|
bye:
|
|
CPLPopErrorHandler();
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetLayerNameForSQL() */
|
|
/************************************************************************/
|
|
|
|
static const char *GetLayerNameForSQL(GDALDataset *poDS,
|
|
const char *pszLayerName)
|
|
{
|
|
/* Only quote if needed. Quoting conventions depend on the driver... */
|
|
if (!EQUAL(pszLayerName, "SELECT") && !EQUAL(pszLayerName, "AS") &&
|
|
!EQUAL(pszLayerName, "CAST") && !EQUAL(pszLayerName, "FROM") &&
|
|
!EQUAL(pszLayerName, "JOIN") && !EQUAL(pszLayerName, "WHERE") &&
|
|
!EQUAL(pszLayerName, "ON") && !EQUAL(pszLayerName, "USING") &&
|
|
!EQUAL(pszLayerName, "ORDER") && !EQUAL(pszLayerName, "BY") &&
|
|
!EQUAL(pszLayerName, "ASC") && !EQUAL(pszLayerName, "DESC") &&
|
|
!EQUAL(pszLayerName, "GROUP") && !EQUAL(pszLayerName, "LIMIT") &&
|
|
!EQUAL(pszLayerName, "OFFSET"))
|
|
{
|
|
char ch;
|
|
for (int i = 0; (ch = pszLayerName[i]) != 0; i++)
|
|
{
|
|
if (ch >= '0' && ch <= '9')
|
|
{
|
|
if (i == 0)
|
|
break;
|
|
}
|
|
else if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')))
|
|
break;
|
|
}
|
|
if (ch == 0)
|
|
return pszLayerName;
|
|
}
|
|
|
|
if (EQUAL(poDS->GetDriverName(), "MYSQL"))
|
|
return CPLSPrintf("`%s`", pszLayerName);
|
|
|
|
if (EQUAL(poDS->GetDriverName(), "PostgreSQL") && strchr(pszLayerName, '.'))
|
|
{
|
|
const char *pszRet = nullptr;
|
|
char **papszTokens = CSLTokenizeStringComplex(pszLayerName, ".", 0, 0);
|
|
if (CSLCount(papszTokens) == 2)
|
|
pszRet =
|
|
CPLSPrintf("\"%s\".\"%s\"", papszTokens[0], papszTokens[1]);
|
|
else
|
|
pszRet = CPLSPrintf("\"%s\"", pszLayerName);
|
|
CSLDestroy(papszTokens);
|
|
return pszRet;
|
|
}
|
|
|
|
if (EQUAL(poDS->GetDriverName(), "SQLAnywhere"))
|
|
return pszLayerName;
|
|
|
|
if (EQUAL(poDS->GetDriverName(), "DB2ODBC"))
|
|
return pszLayerName;
|
|
|
|
return CPLSPrintf("\"%s\"", pszLayerName);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestOGRLayerFeatureCount() */
|
|
/* */
|
|
/* Verify that the feature count matches the actual number of */
|
|
/* features returned during sequential reading. */
|
|
/************************************************************************/
|
|
|
|
static int TestOGRLayerFeatureCount(GDALDataset *poDS, OGRLayer *poLayer,
|
|
int bIsSQLLayer)
|
|
|
|
{
|
|
int bRet = TRUE;
|
|
GIntBig nFC = 0;
|
|
GIntBig nClaimedFC = LOG_ACTION(poLayer->GetFeatureCount());
|
|
bool bWarnAboutSRS = false;
|
|
OGRFeatureDefn *poLayerDefn = LOG_ACTION(poLayer->GetLayerDefn());
|
|
int nGeomFieldCount = LOG_ACTION(poLayerDefn->GetGeomFieldCount());
|
|
|
|
const bool bLayerHasMeasuredGeometriesCapability =
|
|
poLayer->TestCapability(ODsCMeasuredGeometries);
|
|
const bool bLayerHasCurveGeometriesCapability =
|
|
poLayer->TestCapability(OLCCurveGeometries);
|
|
const bool bLayerHasZGeometriesCapability =
|
|
poLayer->TestCapability(OLCZGeometries);
|
|
|
|
CPLErrorReset();
|
|
|
|
for (auto &&poFeature : poLayer)
|
|
{
|
|
nFC++;
|
|
|
|
if (poFeature->GetDefnRef() != poLayerDefn)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Feature defn differs from layer defn.\n"
|
|
"Feature defn = %p\n"
|
|
"Layer defn = %p\n",
|
|
poFeature->GetDefnRef(), poLayerDefn);
|
|
}
|
|
|
|
for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
|
|
{
|
|
OGRGeometry *poGeom = poFeature->GetGeomFieldRef(iGeom);
|
|
|
|
if (poGeom && poGeom->IsMeasured() &&
|
|
!bLayerHasMeasuredGeometriesCapability)
|
|
{
|
|
bRet = FALSE;
|
|
printf("FAILURE: Layer has a feature with measured geometries "
|
|
"but no ODsCMeasuredGeometries capability!\n");
|
|
}
|
|
if (poGeom && poGeom->hasCurveGeometry() &&
|
|
!bLayerHasCurveGeometriesCapability)
|
|
{
|
|
bRet = FALSE;
|
|
printf("FAILURE: Layer has a feature with curved geometries "
|
|
"but no OLCCurveGeometries capability!\n");
|
|
}
|
|
if (poGeom && poGeom->Is3D() && !bLayerHasZGeometriesCapability)
|
|
{
|
|
bRet = FALSE;
|
|
printf("FAILURE: Layer has a feature with 3D geometries but no "
|
|
"OLCZGeometries capability!\n");
|
|
}
|
|
|
|
const OGRSpatialReference *poGFldSRS =
|
|
poLayerDefn->GetGeomFieldDefn(iGeom)->GetSpatialRef();
|
|
|
|
// Compatibility with old drivers anterior to RFC 41
|
|
if (iGeom == 0 && nGeomFieldCount == 1 && poGFldSRS == nullptr)
|
|
poGFldSRS = poLayer->GetSpatialRef();
|
|
|
|
if (poGeom != nullptr &&
|
|
poGeom->getSpatialReference() != poGFldSRS && !bWarnAboutSRS)
|
|
{
|
|
bWarnAboutSRS = true;
|
|
|
|
char *pszLayerSRSWKT = nullptr;
|
|
if (poGFldSRS != nullptr)
|
|
poGFldSRS->exportToWkt(&pszLayerSRSWKT);
|
|
else
|
|
pszLayerSRSWKT = CPLStrdup("(NULL)");
|
|
|
|
char *pszFeatureSRSWKT = nullptr;
|
|
if (poGeom->getSpatialReference() != nullptr)
|
|
poGeom->getSpatialReference()->exportToWkt(
|
|
&pszFeatureSRSWKT);
|
|
else
|
|
pszFeatureSRSWKT = CPLStrdup("(NULL)");
|
|
|
|
bRet = FALSE;
|
|
printf("ERROR: Feature SRS differs from layer SRS.\n"
|
|
"Feature SRS = %s (%p)\n"
|
|
"Layer SRS = %s (%p)\n",
|
|
pszFeatureSRSWKT, poGeom->getSpatialReference(),
|
|
pszLayerSRSWKT, poGFldSRS);
|
|
CPLFree(pszLayerSRSWKT);
|
|
CPLFree(pszFeatureSRSWKT);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* mapogr.cpp doesn't like errors after GetNextFeature() */
|
|
if (CPLGetLastErrorType() == CE_Failure)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: An error was reported : %s\n", CPLGetLastErrorMsg());
|
|
}
|
|
|
|
// Drivers might or might not emit errors when attempting to iterate
|
|
// after EOF
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
auto poFeat = LOG_ACTION(poLayer->GetNextFeature());
|
|
CPLPopErrorHandler();
|
|
if (poFeat != nullptr)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: GetNextFeature() returned non NULL feature after end of "
|
|
"iteration.\n");
|
|
}
|
|
delete poFeat;
|
|
|
|
const auto nFCEndOfIter = LOG_ACTION(poLayer->GetFeatureCount());
|
|
if (nFC != nClaimedFC)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Claimed feature count " CPL_FRMT_GIB
|
|
" doesn't match actual, " CPL_FRMT_GIB ".\n",
|
|
nClaimedFC, nFC);
|
|
}
|
|
else if (nFC != nFCEndOfIter)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Feature count at end of layer, " CPL_FRMT_GIB
|
|
", differs from at start, " CPL_FRMT_GIB ".\n",
|
|
nFCEndOfIter, nFC);
|
|
}
|
|
else if (bVerbose)
|
|
printf("INFO: Feature count verified.\n");
|
|
|
|
if (!bIsSQLLayer)
|
|
{
|
|
CPLString osSQL;
|
|
|
|
osSQL.Printf("SELECT COUNT(*) FROM %s",
|
|
GetLayerNameForSQL(poDS, poLayer->GetName()));
|
|
|
|
OGRLayer *poSQLLyr = poDS->ExecuteSQL(osSQL.c_str(), nullptr, nullptr);
|
|
if (poSQLLyr)
|
|
{
|
|
OGRFeature *poFeatCount = poSQLLyr->GetNextFeature();
|
|
if (poFeatCount == nullptr)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: '%s' failed.\n", osSQL.c_str());
|
|
}
|
|
else if (nClaimedFC != poFeatCount->GetFieldAsInteger(0))
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Claimed feature count " CPL_FRMT_GIB
|
|
" doesn't match '%s' one, " CPL_FRMT_GIB ".\n",
|
|
nClaimedFC, osSQL.c_str(),
|
|
poFeatCount->GetFieldAsInteger64(0));
|
|
}
|
|
DestroyFeatureAndNullify(poFeatCount);
|
|
poDS->ReleaseResultSet(poSQLLyr);
|
|
}
|
|
}
|
|
|
|
if (bVerbose && !bWarnAboutSRS)
|
|
{
|
|
printf("INFO: Feature/layer spatial ref. consistency verified.\n");
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestOGRLayerRandomRead() */
|
|
/* */
|
|
/* Read the first 5 features, and then try to use random */
|
|
/* reading to reread 2 and 5 and verify that this works OK. */
|
|
/* Don't attempt if there aren't at least 5 features. */
|
|
/************************************************************************/
|
|
|
|
static int TestOGRLayerRandomRead(OGRLayer *poLayer)
|
|
|
|
{
|
|
int bRet = TRUE;
|
|
OGRFeature *papoFeatures[5], *poFeature = nullptr;
|
|
|
|
LOG_ACTION(poLayer->SetSpatialFilter(nullptr));
|
|
|
|
if (LOG_ACTION(poLayer->GetFeatureCount()) < 5)
|
|
{
|
|
if (bVerbose)
|
|
printf("INFO: Only " CPL_FRMT_GIB " features on layer,"
|
|
"skipping random read test.\n",
|
|
poLayer->GetFeatureCount());
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Fetch five features. */
|
|
/* -------------------------------------------------------------------- */
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
|
|
for (int iFeature = 0; iFeature < 5; iFeature++)
|
|
{
|
|
papoFeatures[iFeature] = nullptr;
|
|
}
|
|
for (int iFeature = 0; iFeature < 5; iFeature++)
|
|
{
|
|
papoFeatures[iFeature] = LOG_ACTION(poLayer->GetNextFeature());
|
|
if (papoFeatures[iFeature] == nullptr)
|
|
{
|
|
if (bVerbose)
|
|
printf("INFO: Only %d features on layer,"
|
|
"skipping random read test.\n",
|
|
iFeature);
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test feature 2. */
|
|
/* -------------------------------------------------------------------- */
|
|
poFeature = LOG_ACTION(poLayer->GetFeature(papoFeatures[1]->GetFID()));
|
|
if (poFeature == nullptr)
|
|
{
|
|
printf("ERROR: Cannot fetch feature " CPL_FRMT_GIB ".\n",
|
|
papoFeatures[1]->GetFID());
|
|
goto end;
|
|
}
|
|
|
|
if (!poFeature->Equal(papoFeatures[1]))
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Attempt to randomly read feature " CPL_FRMT_GIB
|
|
" appears to\n"
|
|
" have returned a different feature than sequential\n"
|
|
" reading indicates should have happened.\n",
|
|
papoFeatures[1]->GetFID());
|
|
poFeature->DumpReadable(stdout);
|
|
papoFeatures[1]->DumpReadable(stdout);
|
|
|
|
goto end;
|
|
}
|
|
|
|
DestroyFeatureAndNullify(poFeature);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test feature 5. */
|
|
/* -------------------------------------------------------------------- */
|
|
poFeature = LOG_ACTION(poLayer->GetFeature(papoFeatures[4]->GetFID()));
|
|
if (poFeature == nullptr)
|
|
{
|
|
printf("ERROR: Cannot fetch feature " CPL_FRMT_GIB ".\n",
|
|
papoFeatures[4]->GetFID());
|
|
goto end;
|
|
}
|
|
|
|
if (!poFeature->Equal(papoFeatures[4]))
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Attempt to randomly read feature " CPL_FRMT_GIB
|
|
" appears to\n"
|
|
" have returned a different feature than sequential\n"
|
|
" reading indicates should have happened.\n",
|
|
papoFeatures[4]->GetFID());
|
|
poFeature->DumpReadable(stdout);
|
|
papoFeatures[4]->DumpReadable(stdout);
|
|
|
|
goto end;
|
|
}
|
|
|
|
DestroyFeatureAndNullify(poFeature);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test feature 2 again */
|
|
/* -------------------------------------------------------------------- */
|
|
poFeature = LOG_ACTION(poLayer->GetFeature(papoFeatures[2]->GetFID()));
|
|
if (poFeature == nullptr)
|
|
{
|
|
printf("ERROR: Cannot fetch feature " CPL_FRMT_GIB ".\n",
|
|
papoFeatures[2]->GetFID());
|
|
goto end;
|
|
}
|
|
|
|
if (!poFeature->Equal(papoFeatures[2]))
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Attempt to randomly read feature " CPL_FRMT_GIB
|
|
" appears to\n"
|
|
" have returned a different feature than sequential\n"
|
|
" reading indicates should have happened.\n",
|
|
papoFeatures[2]->GetFID());
|
|
poFeature->DumpReadable(stdout);
|
|
papoFeatures[2]->DumpReadable(stdout);
|
|
|
|
goto end;
|
|
}
|
|
|
|
if (bVerbose)
|
|
printf("INFO: Random read test passed.\n");
|
|
|
|
end:
|
|
DestroyFeatureAndNullify(poFeature);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Cleanup. */
|
|
/* -------------------------------------------------------------------- */
|
|
for (int iFeature = 0; iFeature < 5; iFeature++)
|
|
DestroyFeatureAndNullify(papoFeatures[iFeature]);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestOGRLayerSetNextByIndex() */
|
|
/* */
|
|
/************************************************************************/
|
|
|
|
static int TestOGRLayerSetNextByIndex(OGRLayer *poLayer)
|
|
|
|
{
|
|
int bRet = TRUE;
|
|
OGRFeature *poFeature = nullptr;
|
|
OGRFeature *papoFeatures[5];
|
|
|
|
memset(papoFeatures, 0, sizeof(papoFeatures));
|
|
|
|
LOG_ACTION(poLayer->SetSpatialFilter(nullptr));
|
|
|
|
if (LOG_ACTION(poLayer->GetFeatureCount()) < 5)
|
|
{
|
|
if (bVerbose)
|
|
printf("INFO: Only " CPL_FRMT_GIB " features on layer,"
|
|
"skipping SetNextByIndex test.\n",
|
|
poLayer->GetFeatureCount());
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Fetch five features. */
|
|
/* -------------------------------------------------------------------- */
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
|
|
for (int iFeature = 0; iFeature < 5; iFeature++)
|
|
{
|
|
papoFeatures[iFeature] = LOG_ACTION(poLayer->GetNextFeature());
|
|
if (papoFeatures[iFeature] == nullptr)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Cannot get feature %d.\n", iFeature);
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test feature at index 1. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (LOG_ACTION(poLayer->SetNextByIndex(1)) != OGRERR_NONE)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: SetNextByIndex(%d) failed.\n", 1);
|
|
goto end;
|
|
}
|
|
|
|
poFeature = LOG_ACTION(poLayer->GetNextFeature());
|
|
if (poFeature == nullptr || !poFeature->Equal(papoFeatures[1]))
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Attempt to read feature at index %d appears to\n"
|
|
" have returned a different feature than sequential\n"
|
|
" reading indicates should have happened.\n",
|
|
1);
|
|
|
|
goto end;
|
|
}
|
|
|
|
DestroyFeatureAndNullify(poFeature);
|
|
|
|
poFeature = LOG_ACTION(poLayer->GetNextFeature());
|
|
if (poFeature == nullptr || !poFeature->Equal(papoFeatures[2]))
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Attempt to read feature after feature at index %d "
|
|
"appears to\n"
|
|
" have returned a different feature than sequential\n"
|
|
" reading indicates should have happened.\n",
|
|
1);
|
|
|
|
goto end;
|
|
}
|
|
|
|
DestroyFeatureAndNullify(poFeature);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test feature at index 3. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (LOG_ACTION(poLayer->SetNextByIndex(3)) != OGRERR_NONE)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: SetNextByIndex(%d) failed.\n", 3);
|
|
goto end;
|
|
}
|
|
|
|
poFeature = LOG_ACTION(poLayer->GetNextFeature());
|
|
if (!poFeature->Equal(papoFeatures[3]))
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Attempt to read feature at index %d appears to\n"
|
|
" have returned a different feature than sequential\n"
|
|
" reading indicates should have happened.\n",
|
|
3);
|
|
|
|
goto end;
|
|
}
|
|
|
|
DestroyFeatureAndNullify(poFeature);
|
|
|
|
poFeature = LOG_ACTION(poLayer->GetNextFeature());
|
|
if (!poFeature->Equal(papoFeatures[4]))
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Attempt to read feature after feature at index %d "
|
|
"appears to\n"
|
|
" have returned a different feature than sequential\n"
|
|
" reading indicates should have happened.\n",
|
|
3);
|
|
|
|
goto end;
|
|
}
|
|
|
|
if (bVerbose)
|
|
printf("INFO: SetNextByIndex() read test passed.\n");
|
|
|
|
end:
|
|
DestroyFeatureAndNullify(poFeature);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Cleanup. */
|
|
/* -------------------------------------------------------------------- */
|
|
for (int iFeature = 0; iFeature < 5; iFeature++)
|
|
DestroyFeatureAndNullify(papoFeatures[iFeature]);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestOGRLayerRandomWrite() */
|
|
/* */
|
|
/* Test random writing by trying to switch the 2nd and 5th */
|
|
/* features. */
|
|
/************************************************************************/
|
|
|
|
static int TestOGRLayerRandomWrite(OGRLayer *poLayer)
|
|
|
|
{
|
|
int bRet = TRUE;
|
|
OGRFeature *papoFeatures[5];
|
|
|
|
memset(papoFeatures, 0, sizeof(papoFeatures));
|
|
|
|
LOG_ACTION(poLayer->SetSpatialFilter(nullptr));
|
|
|
|
if (LOG_ACTION(poLayer->GetFeatureCount()) < 5)
|
|
{
|
|
if (bVerbose)
|
|
printf("INFO: Only " CPL_FRMT_GIB " features on layer,"
|
|
"skipping random write test.\n",
|
|
poLayer->GetFeatureCount());
|
|
|
|
return bRet;
|
|
}
|
|
|
|
if (!LOG_ACTION(poLayer->TestCapability(OLCRandomRead)))
|
|
{
|
|
if (bVerbose)
|
|
printf("INFO: Skipping random write test since this layer "
|
|
"doesn't support random read.\n");
|
|
return bRet;
|
|
}
|
|
|
|
GIntBig nFID2;
|
|
GIntBig nFID5;
|
|
|
|
CPLString os_Id2;
|
|
CPLString os_Id5;
|
|
|
|
const bool bHas_Id = poLayer->GetLayerDefn()->GetFieldIndex("_id") == 0 ||
|
|
poLayer->GetLayerDefn()->GetFieldIndex("id") == 0;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Fetch five features. */
|
|
/* -------------------------------------------------------------------- */
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
|
|
for (int iFeature = 0; iFeature < 5; iFeature++)
|
|
{
|
|
papoFeatures[iFeature] = LOG_ACTION(poLayer->GetNextFeature());
|
|
if (papoFeatures[iFeature] == nullptr)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Cannot get feature %d.\n", iFeature);
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Switch feature ids of feature 2 and 5. */
|
|
/* -------------------------------------------------------------------- */
|
|
nFID2 = papoFeatures[1]->GetFID();
|
|
nFID5 = papoFeatures[4]->GetFID();
|
|
|
|
papoFeatures[1]->SetFID(nFID5);
|
|
papoFeatures[4]->SetFID(nFID2);
|
|
|
|
if (bHas_Id)
|
|
{
|
|
os_Id2 = papoFeatures[1]->GetFieldAsString(0);
|
|
os_Id5 = papoFeatures[4]->GetFieldAsString(0);
|
|
|
|
papoFeatures[1]->SetField(0, os_Id5);
|
|
papoFeatures[4]->SetField(0, os_Id2);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Rewrite them. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (LOG_ACTION(poLayer->SetFeature(papoFeatures[1])) != OGRERR_NONE)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Attempt to SetFeature(1) failed.\n");
|
|
goto end;
|
|
}
|
|
if (LOG_ACTION(poLayer->SetFeature(papoFeatures[4])) != OGRERR_NONE)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Attempt to SetFeature(4) failed.\n");
|
|
goto end;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Now re-read feature 2 to verify the effect stuck. */
|
|
/* -------------------------------------------------------------------- */
|
|
{
|
|
auto poFeature =
|
|
std::unique_ptr<OGRFeature>(LOG_ACTION(poLayer->GetFeature(nFID5)));
|
|
if (poFeature == nullptr)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Attempt to GetFeature( nFID5 ) failed.\n");
|
|
goto end;
|
|
}
|
|
if (!poFeature->Equal(papoFeatures[1]))
|
|
{
|
|
bRet = FALSE;
|
|
poFeature->DumpReadable(stderr);
|
|
papoFeatures[1]->DumpReadable(stderr);
|
|
printf("ERROR: Written feature didn't seem to retain value.\n");
|
|
}
|
|
else if (bVerbose)
|
|
{
|
|
printf("INFO: Random write test passed.\n");
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Re-invert the features to restore to original state */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
papoFeatures[1]->SetFID(nFID2);
|
|
papoFeatures[4]->SetFID(nFID5);
|
|
|
|
if (bHas_Id)
|
|
{
|
|
papoFeatures[1]->SetField(0, os_Id2);
|
|
papoFeatures[4]->SetField(0, os_Id5);
|
|
}
|
|
|
|
if (LOG_ACTION(poLayer->SetFeature(papoFeatures[1])) != OGRERR_NONE)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Attempt to restore SetFeature(1) failed.\n");
|
|
}
|
|
if (LOG_ACTION(poLayer->SetFeature(papoFeatures[4])) != OGRERR_NONE)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Attempt to restore SetFeature(4) failed.\n");
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test UpdateFeature() */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
if (bRet)
|
|
{
|
|
int nOldVal = 0;
|
|
std::string osOldVal;
|
|
int iUpdatedFeature = -1;
|
|
int iUpdatedField = -1;
|
|
std::unique_ptr<OGRFeature> poUpdatedFeature;
|
|
|
|
for (int iFeature = 0; iUpdatedFeature < 0 && iFeature < 5; iFeature++)
|
|
{
|
|
for (int iField = 0;
|
|
iField < poLayer->GetLayerDefn()->GetFieldCount(); ++iField)
|
|
{
|
|
if (papoFeatures[iFeature]->IsFieldSetAndNotNull(iField))
|
|
{
|
|
if (poLayer->GetLayerDefn()
|
|
->GetFieldDefn(iField)
|
|
->GetType() == OFTInteger)
|
|
{
|
|
iUpdatedFeature = iFeature;
|
|
iUpdatedField = iField;
|
|
nOldVal =
|
|
papoFeatures[iFeature]->GetFieldAsInteger(iField);
|
|
poUpdatedFeature.reset(papoFeatures[iFeature]->Clone());
|
|
poUpdatedFeature->SetField(iField, 0xBEEF);
|
|
break;
|
|
}
|
|
else if (poLayer->GetLayerDefn()
|
|
->GetFieldDefn(iField)
|
|
->GetType() == OFTString)
|
|
{
|
|
iUpdatedFeature = iFeature;
|
|
iUpdatedField = iField;
|
|
osOldVal =
|
|
papoFeatures[iFeature]->GetFieldAsString(iField);
|
|
poUpdatedFeature.reset(papoFeatures[iFeature]->Clone());
|
|
poUpdatedFeature->SetField(iField, "0xBEEF");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (poUpdatedFeature)
|
|
{
|
|
if (LOG_ACTION(poLayer->UpdateFeature(poUpdatedFeature.get(), 1,
|
|
&iUpdatedField, 0, nullptr,
|
|
false)) != OGRERR_NONE)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: UpdateFeature() failed.\n");
|
|
}
|
|
if (bRet)
|
|
{
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
for (int iFeature = 0; iFeature < 5; iFeature++)
|
|
{
|
|
auto poFeature = std::unique_ptr<OGRFeature>(LOG_ACTION(
|
|
poLayer->GetFeature(papoFeatures[iFeature]->GetFID())));
|
|
if (iFeature != iUpdatedFeature)
|
|
{
|
|
if (!poFeature ||
|
|
!poFeature->Equal(papoFeatures[iFeature]))
|
|
{
|
|
bRet = false;
|
|
printf("ERROR: UpdateFeature() test: "
|
|
"!poFeature->Equals(papoFeatures[iFeature]) "
|
|
"unexpected.\n");
|
|
if (poFeature)
|
|
poFeature->DumpReadable(stdout);
|
|
papoFeatures[iFeature]->DumpReadable(stdout);
|
|
}
|
|
}
|
|
}
|
|
|
|
auto poFeature =
|
|
std::unique_ptr<OGRFeature>(LOG_ACTION(poLayer->GetFeature(
|
|
papoFeatures[iUpdatedFeature]->GetFID())));
|
|
if (!poFeature)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: at line %d", __LINE__);
|
|
goto end;
|
|
}
|
|
if (poLayer->GetLayerDefn()
|
|
->GetFieldDefn(iUpdatedField)
|
|
->GetType() == OFTInteger)
|
|
{
|
|
if (poFeature->GetFieldAsInteger(iUpdatedField) != 0xBEEF)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Did not get expected field value after "
|
|
"UpdateFeature().\n");
|
|
}
|
|
poFeature->SetField(iUpdatedField, nOldVal);
|
|
}
|
|
else if (poLayer->GetLayerDefn()
|
|
->GetFieldDefn(iUpdatedField)
|
|
->GetType() == OFTString)
|
|
{
|
|
if (!EQUAL(poFeature->GetFieldAsString(iUpdatedField),
|
|
"0xBEEF"))
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Did not get expected field value after "
|
|
"UpdateFeature().\n");
|
|
}
|
|
poFeature->SetField(iUpdatedField, osOldVal.c_str());
|
|
}
|
|
else
|
|
{
|
|
CPLAssert(false);
|
|
}
|
|
|
|
if (LOG_ACTION(poLayer->UpdateFeature(
|
|
poFeature.get(), 1, &iUpdatedField, 0, nullptr,
|
|
false)) != OGRERR_NONE)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: UpdateFeature() failed.\n");
|
|
}
|
|
|
|
poFeature.reset(LOG_ACTION(poLayer->GetFeature(
|
|
papoFeatures[iUpdatedFeature]->GetFID())));
|
|
if (!poFeature)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: at line %d", __LINE__);
|
|
goto end;
|
|
}
|
|
if (!poFeature->Equal(papoFeatures[iUpdatedFeature]))
|
|
{
|
|
bRet = false;
|
|
printf("ERROR: UpdateFeature() test: "
|
|
"!poFeature->Equals(papoFeatures[iUpdatedFeature]) "
|
|
"unexpected.\n");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bVerbose)
|
|
printf("INFO: Could not test UpdateFeature().\n");
|
|
}
|
|
}
|
|
|
|
end:
|
|
/* -------------------------------------------------------------------- */
|
|
/* Cleanup. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
for (int iFeature = 0; iFeature < 5; iFeature++)
|
|
DestroyFeatureAndNullify(papoFeatures[iFeature]);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestSpatialFilter() */
|
|
/* */
|
|
/* This is intended to be a simple test of the spatial */
|
|
/* filtering. We read the first feature. Then construct a */
|
|
/* spatial filter geometry which includes it, install and */
|
|
/* verify that we get the feature. Next install a spatial */
|
|
/* filter that doesn't include this feature, and test again. */
|
|
/************************************************************************/
|
|
|
|
static int TestSpatialFilter(OGRLayer *poLayer, int iGeomField)
|
|
|
|
{
|
|
int bRet = TRUE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Read the target feature. */
|
|
/* -------------------------------------------------------------------- */
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
OGRFeature *poTargetFeature = LOG_ACTION(poLayer->GetNextFeature());
|
|
|
|
if (poTargetFeature == nullptr)
|
|
{
|
|
if (bVerbose)
|
|
{
|
|
printf("INFO: Skipping Spatial Filter test for %s.\n"
|
|
" No features in layer.\n",
|
|
poLayer->GetName());
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
OGRGeometry *poGeom = poTargetFeature->GetGeomFieldRef(iGeomField);
|
|
if (poGeom == nullptr || poGeom->IsEmpty())
|
|
{
|
|
if (bVerbose)
|
|
{
|
|
printf("INFO: Skipping Spatial Filter test for %s,\n"
|
|
" target feature has no geometry.\n",
|
|
poTargetFeature->GetDefnRef()->GetName());
|
|
}
|
|
DestroyFeatureAndNullify(poTargetFeature);
|
|
return bRet;
|
|
}
|
|
|
|
OGREnvelope sEnvelope;
|
|
poGeom->getEnvelope(&sEnvelope);
|
|
|
|
OGREnvelope sLayerExtent;
|
|
double epsilon = 10.0;
|
|
if (LOG_ACTION(poLayer->TestCapability(OLCFastGetExtent)) &&
|
|
LOG_ACTION(poLayer->GetExtent(iGeomField, &sLayerExtent)) ==
|
|
OGRERR_NONE &&
|
|
sLayerExtent.MinX < sLayerExtent.MaxX &&
|
|
sLayerExtent.MinY < sLayerExtent.MaxY)
|
|
{
|
|
epsilon = std::min(sLayerExtent.MaxX - sLayerExtent.MinX,
|
|
sLayerExtent.MaxY - sLayerExtent.MinY) /
|
|
10.0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Construct inclusive filter. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
OGRLinearRing oRing;
|
|
oRing.setPoint(0, sEnvelope.MinX - 2 * epsilon,
|
|
sEnvelope.MinY - 2 * epsilon);
|
|
oRing.setPoint(1, sEnvelope.MinX - 2 * epsilon,
|
|
sEnvelope.MaxY + 1 * epsilon);
|
|
oRing.setPoint(2, sEnvelope.MaxX + 1 * epsilon,
|
|
sEnvelope.MaxY + 1 * epsilon);
|
|
oRing.setPoint(3, sEnvelope.MaxX + 1 * epsilon,
|
|
sEnvelope.MinY - 2 * epsilon);
|
|
oRing.setPoint(4, sEnvelope.MinX - 2 * epsilon,
|
|
sEnvelope.MinY - 2 * epsilon);
|
|
|
|
OGRPolygon oInclusiveFilter;
|
|
oInclusiveFilter.addRing(&oRing);
|
|
|
|
LOG_ACTION(poLayer->SetSpatialFilter(iGeomField, &oInclusiveFilter));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Verify that we can find the target feature. */
|
|
/* -------------------------------------------------------------------- */
|
|
bool bFound = false;
|
|
GIntBig nIterCount = 0;
|
|
for (auto &&poFeature : poLayer)
|
|
{
|
|
if (poFeature->Equal(poTargetFeature))
|
|
{
|
|
bFound = true;
|
|
}
|
|
nIterCount++;
|
|
}
|
|
|
|
if (!bFound)
|
|
{
|
|
bRet = FALSE;
|
|
printf(
|
|
"ERROR: Spatial filter (%d) eliminated a feature unexpectedly!\n",
|
|
iGeomField);
|
|
}
|
|
else if (bVerbose)
|
|
{
|
|
printf("INFO: Spatial filter inclusion seems to work.\n");
|
|
}
|
|
|
|
GIntBig nInclusiveCount = LOG_ACTION(poLayer->GetFeatureCount());
|
|
|
|
// Identity check doesn't always work depending on feature geometries
|
|
if (nIterCount > nInclusiveCount)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: GetFeatureCount() with spatial filter smaller (%d) than "
|
|
"count while iterating over features (%d).\n",
|
|
static_cast<int>(nInclusiveCount), static_cast<int>(nIterCount));
|
|
}
|
|
|
|
LOG_ACTION(poLayer->SetAttributeFilter("1=1"));
|
|
GIntBig nShouldBeSame = LOG_ACTION(poLayer->GetFeatureCount());
|
|
LOG_ACTION(poLayer->SetAttributeFilter(nullptr));
|
|
if (nShouldBeSame != nInclusiveCount)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Attribute filter seems to be make spatial "
|
|
"filter fail with GetFeatureCount().\n");
|
|
}
|
|
|
|
LOG_ACTION(poLayer->SetAttributeFilter("1=0"));
|
|
GIntBig nShouldBeZero = LOG_ACTION(poLayer->GetFeatureCount());
|
|
LOG_ACTION(poLayer->SetAttributeFilter(nullptr));
|
|
if (nShouldBeZero != 0)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Attribute filter seems to be ignored in "
|
|
"GetFeatureCount() when spatial filter is set.\n");
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Construct exclusive filter. */
|
|
/* -------------------------------------------------------------------- */
|
|
oRing.setPoint(0, sEnvelope.MinX - 2 * epsilon,
|
|
sEnvelope.MinY - 2 * epsilon);
|
|
oRing.setPoint(1, sEnvelope.MinX - 1 * epsilon,
|
|
sEnvelope.MinY - 2 * epsilon);
|
|
oRing.setPoint(2, sEnvelope.MinX - 1 * epsilon,
|
|
sEnvelope.MinY - 1 * epsilon);
|
|
oRing.setPoint(3, sEnvelope.MinX - 2 * epsilon,
|
|
sEnvelope.MinY - 1 * epsilon);
|
|
oRing.setPoint(4, sEnvelope.MinX - 2 * epsilon,
|
|
sEnvelope.MinY - 2 * epsilon);
|
|
|
|
OGRPolygon oExclusiveFilter;
|
|
oExclusiveFilter.addRing(&oRing);
|
|
|
|
LOG_ACTION(poLayer->SetSpatialFilter(iGeomField, &oExclusiveFilter));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Verify that we can NOT find the target feature. */
|
|
/* -------------------------------------------------------------------- */
|
|
OGRFeatureUniquePtr poUniquePtrFeature;
|
|
for (auto &&poFeatureIter : poLayer)
|
|
{
|
|
if (poFeatureIter->Equal(poTargetFeature))
|
|
{
|
|
poUniquePtrFeature.swap(poFeatureIter);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (poUniquePtrFeature != nullptr)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Spatial filter (%d) failed to eliminate "
|
|
"a feature unexpectedly!\n",
|
|
iGeomField);
|
|
}
|
|
else if (LOG_ACTION(poLayer->GetFeatureCount()) >= nInclusiveCount)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: GetFeatureCount() may not be taking spatial "
|
|
"filter (%d) into account.\n",
|
|
iGeomField);
|
|
}
|
|
else if (bVerbose)
|
|
{
|
|
printf("INFO: Spatial filter exclusion seems to work.\n");
|
|
}
|
|
|
|
// Check that GetFeature() ignores the spatial filter
|
|
poUniquePtrFeature.reset(
|
|
LOG_ACTION(poLayer->GetFeature(poTargetFeature->GetFID())));
|
|
if (poUniquePtrFeature == nullptr ||
|
|
!poUniquePtrFeature->Equal(poTargetFeature))
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Spatial filter has been taken into account "
|
|
"by GetFeature()\n");
|
|
}
|
|
else if (bVerbose)
|
|
{
|
|
printf("INFO: Spatial filter is ignored by GetFeature() "
|
|
"as expected.\n");
|
|
}
|
|
|
|
if (bRet)
|
|
{
|
|
poUniquePtrFeature.reset();
|
|
for (auto &&poFeatureIter : poLayer)
|
|
{
|
|
if (poFeatureIter->Equal(poTargetFeature))
|
|
{
|
|
poUniquePtrFeature.swap(poFeatureIter);
|
|
break;
|
|
}
|
|
}
|
|
if (poUniquePtrFeature != nullptr)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Spatial filter has not been restored correctly "
|
|
"after GetFeature()\n");
|
|
}
|
|
}
|
|
|
|
DestroyFeatureAndNullify(poTargetFeature);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test infinity envelope */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
constexpr double NEG_INF = -std::numeric_limits<double>::infinity();
|
|
constexpr double POS_INF = std::numeric_limits<double>::infinity();
|
|
|
|
oRing.setPoint(0, NEG_INF, NEG_INF);
|
|
oRing.setPoint(1, NEG_INF, POS_INF);
|
|
oRing.setPoint(2, POS_INF, POS_INF);
|
|
oRing.setPoint(3, POS_INF, NEG_INF);
|
|
oRing.setPoint(4, NEG_INF, NEG_INF);
|
|
|
|
OGRPolygon oInfinityFilter;
|
|
oInfinityFilter.addRing(&oRing);
|
|
|
|
LOG_ACTION(poLayer->SetSpatialFilter(iGeomField, &oInfinityFilter));
|
|
int nCountInf = 0;
|
|
for (auto &&poFeatureIter : poLayer)
|
|
{
|
|
auto poGeomIter = poFeatureIter->GetGeomFieldRef(iGeomField);
|
|
if (poGeomIter != nullptr)
|
|
nCountInf++;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test envelope with huge coords */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
constexpr double HUGE_COORDS = 1.0e300;
|
|
|
|
oRing.setPoint(0, -HUGE_COORDS, -HUGE_COORDS);
|
|
oRing.setPoint(1, -HUGE_COORDS, HUGE_COORDS);
|
|
oRing.setPoint(2, HUGE_COORDS, HUGE_COORDS);
|
|
oRing.setPoint(3, HUGE_COORDS, -HUGE_COORDS);
|
|
oRing.setPoint(4, -HUGE_COORDS, -HUGE_COORDS);
|
|
|
|
OGRPolygon oHugeFilter;
|
|
oHugeFilter.addRing(&oRing);
|
|
|
|
LOG_ACTION(poLayer->SetSpatialFilter(iGeomField, &oHugeFilter));
|
|
int nCountHuge = 0;
|
|
for (auto &&poFeatureIter : poLayer)
|
|
{
|
|
auto poGeomIter = poFeatureIter->GetGeomFieldRef(iGeomField);
|
|
if (poGeomIter != nullptr)
|
|
nCountHuge++;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Reset spatial filter */
|
|
/* -------------------------------------------------------------------- */
|
|
LOG_ACTION(poLayer->SetSpatialFilter(nullptr));
|
|
|
|
int nExpected = 0;
|
|
for (auto &&poFeatureIter : poLayer)
|
|
{
|
|
auto poGeomIter = poFeatureIter->GetGeomFieldRef(iGeomField);
|
|
if (poGeomIter != nullptr && !poGeomIter->IsEmpty())
|
|
nExpected++;
|
|
}
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
|
|
if (nCountInf != nExpected)
|
|
{
|
|
/*bRet = FALSE; */
|
|
printf("WARNING: Infinity spatial filter returned %d features "
|
|
"instead of %d\n",
|
|
nCountInf, nExpected);
|
|
}
|
|
else if (bVerbose)
|
|
{
|
|
printf("INFO: Infinity spatial filter works as expected.\n");
|
|
}
|
|
|
|
if (nCountHuge != nExpected)
|
|
{
|
|
/* bRet = FALSE; */
|
|
printf("WARNING: Huge coords spatial filter returned %d features "
|
|
"instead of %d\n",
|
|
nCountHuge, nExpected);
|
|
}
|
|
else if (bVerbose)
|
|
{
|
|
printf("INFO: Huge coords spatial filter works as expected.\n");
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
static int TestFullSpatialFilter(OGRLayer *poLayer, int iGeomField)
|
|
|
|
{
|
|
int bRet = TRUE;
|
|
|
|
OGREnvelope sLayerExtent;
|
|
double epsilon = 10.0;
|
|
if (LOG_ACTION(poLayer->TestCapability(OLCFastGetExtent)) &&
|
|
LOG_ACTION(poLayer->GetExtent(iGeomField, &sLayerExtent)) ==
|
|
OGRERR_NONE &&
|
|
sLayerExtent.MinX < sLayerExtent.MaxX &&
|
|
sLayerExtent.MinY < sLayerExtent.MaxY)
|
|
{
|
|
epsilon = std::min(sLayerExtent.MaxX - sLayerExtent.MinX,
|
|
sLayerExtent.MaxY - sLayerExtent.MinY) /
|
|
10.0;
|
|
}
|
|
|
|
const GIntBig nTotalFeatureCount = LOG_ACTION(poLayer->GetFeatureCount());
|
|
for (GIntBig i = 0; i < nTotalFeatureCount; i++)
|
|
{
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Read the target feature. */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
LOG_ACTION(poLayer->SetSpatialFilter(nullptr));
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
LOG_ACTION(poLayer->SetNextByIndex(i));
|
|
OGRFeature *poTargetFeature = LOG_ACTION(poLayer->GetNextFeature());
|
|
|
|
if (poTargetFeature == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
OGRGeometry *poGeom = poTargetFeature->GetGeomFieldRef(iGeomField);
|
|
if (poGeom == nullptr || poGeom->IsEmpty())
|
|
{
|
|
DestroyFeatureAndNullify(poTargetFeature);
|
|
continue;
|
|
}
|
|
|
|
OGREnvelope sEnvelope;
|
|
poGeom->getEnvelope(&sEnvelope);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Construct inclusive filter. */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
|
|
OGRLinearRing oRing;
|
|
oRing.setPoint(0, sEnvelope.MinX - 2 * epsilon,
|
|
sEnvelope.MinY - 2 * epsilon);
|
|
oRing.setPoint(1, sEnvelope.MinX - 2 * epsilon,
|
|
sEnvelope.MaxY + 1 * epsilon);
|
|
oRing.setPoint(2, sEnvelope.MaxX + 1 * epsilon,
|
|
sEnvelope.MaxY + 1 * epsilon);
|
|
oRing.setPoint(3, sEnvelope.MaxX + 1 * epsilon,
|
|
sEnvelope.MinY - 2 * epsilon);
|
|
oRing.setPoint(4, sEnvelope.MinX - 2 * epsilon,
|
|
sEnvelope.MinY - 2 * epsilon);
|
|
|
|
OGRPolygon oInclusiveFilter;
|
|
oInclusiveFilter.addRing(&oRing);
|
|
|
|
LOG_ACTION(poLayer->SetSpatialFilter(iGeomField, &oInclusiveFilter));
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Verify that we can find the target feature. */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
|
|
bool bFound = false;
|
|
OGRFeature *poFeature = nullptr;
|
|
while ((poFeature = LOG_ACTION(poLayer->GetNextFeature())) != nullptr)
|
|
{
|
|
if (poFeature->Equal(poTargetFeature))
|
|
{
|
|
bFound = true;
|
|
DestroyFeatureAndNullify(poFeature);
|
|
break;
|
|
}
|
|
else
|
|
DestroyFeatureAndNullify(poFeature);
|
|
}
|
|
|
|
if (!bFound)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Spatial filter (%d) eliminated feature " CPL_FRMT_GIB
|
|
" unexpectedly!\n",
|
|
iGeomField, poTargetFeature->GetFID());
|
|
DestroyFeatureAndNullify(poTargetFeature);
|
|
break;
|
|
}
|
|
|
|
DestroyFeatureAndNullify(poTargetFeature);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Reset spatial filter */
|
|
/* -------------------------------------------------------------------- */
|
|
LOG_ACTION(poLayer->SetSpatialFilter(nullptr));
|
|
|
|
if (bRet && bVerbose)
|
|
{
|
|
printf("INFO: Full spatial filter succeeded.\n");
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
static int TestSpatialFilter(OGRLayer *poLayer)
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Read the target feature. */
|
|
/* -------------------------------------------------------------------- */
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
OGRFeature *poTargetFeature = LOG_ACTION(poLayer->GetNextFeature());
|
|
|
|
if (poTargetFeature == nullptr)
|
|
{
|
|
if (bVerbose)
|
|
{
|
|
printf("INFO: Skipping Spatial Filter test for %s.\n"
|
|
" No features in layer.\n",
|
|
poLayer->GetName());
|
|
}
|
|
return TRUE;
|
|
}
|
|
DestroyFeatureAndNullify(poTargetFeature);
|
|
|
|
const int nGeomFieldCount =
|
|
LOG_ACTION(poLayer->GetLayerDefn()->GetGeomFieldCount());
|
|
if (nGeomFieldCount == 0)
|
|
{
|
|
if (bVerbose)
|
|
{
|
|
printf("INFO: Skipping Spatial Filter test for %s,\n"
|
|
" target feature has no geometry.\n",
|
|
poLayer->GetName());
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
int bRet = TRUE;
|
|
for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
|
|
{
|
|
bRet &= TestSpatialFilter(poLayer, iGeom);
|
|
|
|
if (bFullSpatialFilter)
|
|
bRet &= TestFullSpatialFilter(poLayer, iGeom);
|
|
}
|
|
|
|
CPLErrorReset();
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
OGRPolygon oPolygon;
|
|
LOG_ACTION(poLayer->SetSpatialFilter(-1, &oPolygon));
|
|
CPLPopErrorHandler();
|
|
if (CPLGetLastErrorType() == 0)
|
|
printf("WARNING: poLayer->SetSpatialFilter(-1) "
|
|
"should emit an error.\n");
|
|
|
|
CPLErrorReset();
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
LOG_ACTION(poLayer->SetSpatialFilter(nGeomFieldCount, &oPolygon));
|
|
CPLPopErrorHandler();
|
|
if (CPLGetLastErrorType() == 0)
|
|
printf("WARNING: poLayer->SetSpatialFilter(nGeomFieldCount) "
|
|
"should emit an error.\n");
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetQuotedIfNeededIdentifier() */
|
|
/************************************************************************/
|
|
|
|
static std::string GetQuotedIfNeededIdentifier(const char *pszFieldName)
|
|
{
|
|
std::string osIdentifier;
|
|
const bool bMustQuoteAttrName =
|
|
pszFieldName[0] == '\0' || strchr(pszFieldName, '_') ||
|
|
strchr(pszFieldName, ' ') || swq_is_reserved_keyword(pszFieldName);
|
|
if (bMustQuoteAttrName)
|
|
{
|
|
osIdentifier = "\"";
|
|
osIdentifier += pszFieldName;
|
|
osIdentifier += "\"";
|
|
}
|
|
else
|
|
{
|
|
osIdentifier = pszFieldName;
|
|
}
|
|
return osIdentifier;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetAttributeFilters() */
|
|
/************************************************************************/
|
|
|
|
static bool GetAttributeFilters(OGRLayer *poLayer,
|
|
std::unique_ptr<OGRFeature> &poTargetFeature,
|
|
std::string &osInclusiveFilter,
|
|
std::string &osExclusiveFilter)
|
|
{
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Read the target feature. */
|
|
/* -------------------------------------------------------------------- */
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
poTargetFeature.reset(LOG_ACTION(poLayer->GetNextFeature()));
|
|
|
|
if (poTargetFeature == nullptr)
|
|
{
|
|
if (bVerbose)
|
|
{
|
|
printf("INFO: Skipping Attribute Filter test for %s.\n"
|
|
" No features in layer.\n",
|
|
poLayer->GetName());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int i = 0;
|
|
OGRFieldType eType = OFTString;
|
|
for (i = 0; i < poTargetFeature->GetFieldCount(); i++)
|
|
{
|
|
eType = poTargetFeature->GetFieldDefnRef(i)->GetType();
|
|
if (poTargetFeature->IsFieldSetAndNotNull(i) &&
|
|
(eType == OFTString || eType == OFTInteger || eType == OFTReal))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (i == poTargetFeature->GetFieldCount())
|
|
{
|
|
if (bVerbose)
|
|
{
|
|
printf("INFO: Skipping Attribute Filter test for %s.\n"
|
|
" Could not find non NULL field.\n",
|
|
poLayer->GetName());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const std::string osFieldName =
|
|
poTargetFeature->GetFieldDefnRef(i)->GetNameRef();
|
|
CPLString osValue = poTargetFeature->GetFieldAsString(i);
|
|
if (eType == OFTReal)
|
|
{
|
|
int nWidth = poTargetFeature->GetFieldDefnRef(i)->GetWidth();
|
|
int nPrecision = poTargetFeature->GetFieldDefnRef(i)->GetPrecision();
|
|
if (nWidth > 0)
|
|
{
|
|
char szFormat[32];
|
|
snprintf(szFormat, sizeof(szFormat), "%%%d.%df", nWidth,
|
|
nPrecision);
|
|
osValue.Printf(szFormat, poTargetFeature->GetFieldAsDouble(i));
|
|
}
|
|
else
|
|
osValue.Printf("%.18g", poTargetFeature->GetFieldAsDouble(i));
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Construct inclusive filter. */
|
|
/* -------------------------------------------------------------------- */
|
|
osInclusiveFilter = GetQuotedIfNeededIdentifier(osFieldName.c_str());
|
|
osInclusiveFilter += " = ";
|
|
if (eType == OFTString)
|
|
osInclusiveFilter += "'";
|
|
osInclusiveFilter += osValue;
|
|
if (eType == OFTString)
|
|
osInclusiveFilter += "'";
|
|
/* Make sure that the literal will be recognized as a float value */
|
|
/* to avoid int underflow/overflow */
|
|
else if (eType == OFTReal && strchr(osValue, '.') == nullptr)
|
|
osInclusiveFilter += ".";
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Construct exclusive filter. */
|
|
/* -------------------------------------------------------------------- */
|
|
osExclusiveFilter = GetQuotedIfNeededIdentifier(osFieldName.c_str());
|
|
osExclusiveFilter += " <> ";
|
|
if (eType == OFTString)
|
|
osExclusiveFilter += "'";
|
|
osExclusiveFilter += osValue;
|
|
if (eType == OFTString)
|
|
osExclusiveFilter += "'";
|
|
/* Make sure that the literal will be recognized as a float value */
|
|
/* to avoid int underflow/overflow */
|
|
else if (eType == OFTReal && strchr(osValue, '.') == nullptr)
|
|
osExclusiveFilter += ".";
|
|
|
|
return true;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestAttributeFilter() */
|
|
/* */
|
|
/* This is intended to be a simple test of the attribute */
|
|
/* filtering. We read the first feature. Then construct a */
|
|
/* attribute filter which includes it, install and */
|
|
/* verify that we get the feature. Next install a attribute */
|
|
/* filter that doesn't include this feature, and test again. */
|
|
/************************************************************************/
|
|
|
|
static int TestAttributeFilter(CPL_UNUSED GDALDataset *poDS, OGRLayer *poLayer)
|
|
|
|
{
|
|
int bRet = TRUE;
|
|
|
|
std::unique_ptr<OGRFeature> poTargetFeature;
|
|
std::string osInclusiveFilter;
|
|
std::string osExclusiveFilter;
|
|
if (!GetAttributeFilters(poLayer, poTargetFeature, osInclusiveFilter,
|
|
osExclusiveFilter))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Apply inclusive filter. */
|
|
/* -------------------------------------------------------------------- */
|
|
LOG_ACTION(poLayer->SetAttributeFilter(osInclusiveFilter.c_str()));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Verify that we can find the target feature. */
|
|
/* -------------------------------------------------------------------- */
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
|
|
bool bFoundFeature = false;
|
|
OGRFeature *poFeature = nullptr;
|
|
while ((poFeature = LOG_ACTION(poLayer->GetNextFeature())) != nullptr)
|
|
{
|
|
if (poFeature->Equal(poTargetFeature.get()))
|
|
{
|
|
bFoundFeature = true;
|
|
DestroyFeatureAndNullify(poFeature);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
DestroyFeatureAndNullify(poFeature);
|
|
}
|
|
}
|
|
|
|
if (!bFoundFeature)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Attribute filter eliminated a feature unexpectedly!\n");
|
|
}
|
|
else if (bVerbose)
|
|
{
|
|
printf("INFO: Attribute filter inclusion seems to work.\n");
|
|
}
|
|
|
|
const GIntBig nInclusiveCount = LOG_ACTION(poLayer->GetFeatureCount());
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Apply exclusive filter. */
|
|
/* -------------------------------------------------------------------- */
|
|
LOG_ACTION(poLayer->SetAttributeFilter(osExclusiveFilter.c_str()));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Verify that we can find the target feature. */
|
|
/* -------------------------------------------------------------------- */
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
|
|
GIntBig nExclusiveCountWhileIterating = 0;
|
|
while ((poFeature = LOG_ACTION(poLayer->GetNextFeature())) != nullptr)
|
|
{
|
|
if (poFeature->Equal(poTargetFeature.get()))
|
|
{
|
|
DestroyFeatureAndNullify(poFeature);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
DestroyFeatureAndNullify(poFeature);
|
|
}
|
|
nExclusiveCountWhileIterating++;
|
|
}
|
|
|
|
const GIntBig nExclusiveCount = LOG_ACTION(poLayer->GetFeatureCount());
|
|
|
|
// Check that GetFeature() ignores the attribute filter
|
|
OGRFeature *poFeature2 =
|
|
LOG_ACTION(poLayer->GetFeature(poTargetFeature.get()->GetFID()));
|
|
|
|
poLayer->ResetReading();
|
|
OGRFeature *poFeature3 = nullptr;
|
|
while ((poFeature3 = LOG_ACTION(poLayer->GetNextFeature())) != nullptr)
|
|
{
|
|
if (poFeature3->Equal(poTargetFeature.get()))
|
|
{
|
|
DestroyFeatureAndNullify(poFeature3);
|
|
break;
|
|
}
|
|
else
|
|
DestroyFeatureAndNullify(poFeature3);
|
|
}
|
|
|
|
LOG_ACTION(poLayer->SetAttributeFilter(nullptr));
|
|
|
|
const GIntBig nTotalCount = LOG_ACTION(poLayer->GetFeatureCount());
|
|
|
|
if (poFeature != nullptr)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Attribute filter failed to eliminate "
|
|
"a feature unexpectedly!\n");
|
|
}
|
|
else if (nExclusiveCountWhileIterating != nExclusiveCount ||
|
|
nExclusiveCount >= nTotalCount || nInclusiveCount > nTotalCount ||
|
|
(nInclusiveCount == nTotalCount && nExclusiveCount != 0))
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: GetFeatureCount() may not be taking attribute "
|
|
"filter into account (nInclusiveCount = " CPL_FRMT_GIB
|
|
", nExclusiveCount = " CPL_FRMT_GIB
|
|
", nExclusiveCountWhileIterating = " CPL_FRMT_GIB
|
|
", nTotalCount = " CPL_FRMT_GIB ").\n",
|
|
nInclusiveCount, nExclusiveCount, nExclusiveCountWhileIterating,
|
|
nTotalCount);
|
|
}
|
|
else if (bVerbose)
|
|
{
|
|
printf("INFO: Attribute filter exclusion seems to work.\n");
|
|
}
|
|
|
|
if (poFeature2 == nullptr || !poFeature2->Equal(poTargetFeature.get()))
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Attribute filter has been taken into account "
|
|
"by GetFeature()\n");
|
|
}
|
|
else if (bVerbose)
|
|
{
|
|
printf("INFO: Attribute filter is ignored by GetFeature() "
|
|
"as expected.\n");
|
|
}
|
|
|
|
if (poFeature3 != nullptr)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Attribute filter has not been restored correctly "
|
|
"after GetFeature()\n");
|
|
}
|
|
|
|
if (poFeature2 != nullptr)
|
|
DestroyFeatureAndNullify(poFeature2);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestOGRLayerUTF8() */
|
|
/************************************************************************/
|
|
|
|
static int TestOGRLayerUTF8(OGRLayer *poLayer)
|
|
{
|
|
int bRet = TRUE;
|
|
|
|
LOG_ACTION(poLayer->SetSpatialFilter(nullptr));
|
|
LOG_ACTION(poLayer->SetAttributeFilter(nullptr));
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
|
|
const int bIsAdvertizedAsUTF8 =
|
|
LOG_ACTION(poLayer->TestCapability(OLCStringsAsUTF8));
|
|
const int nFields = LOG_ACTION(poLayer->GetLayerDefn()->GetFieldCount());
|
|
bool bFoundString = false;
|
|
bool bFoundNonASCII = false;
|
|
bool bFoundUTF8 = false;
|
|
bool bCanAdvertiseUTF8 = true;
|
|
|
|
OGRFeature *poFeature = nullptr;
|
|
while (bRet &&
|
|
(poFeature = LOG_ACTION(poLayer->GetNextFeature())) != nullptr)
|
|
{
|
|
for (int i = 0; i < nFields; i++)
|
|
{
|
|
if (!poFeature->IsFieldSet(i))
|
|
continue;
|
|
if (poFeature->GetFieldDefnRef(i)->GetType() == OFTString)
|
|
{
|
|
const char *pszVal = poFeature->GetFieldAsString(i);
|
|
if (pszVal[0] != 0)
|
|
{
|
|
bFoundString = true;
|
|
const GByte *pszIter =
|
|
reinterpret_cast<const GByte *>(pszVal);
|
|
bool bIsASCII = true;
|
|
while (*pszIter)
|
|
{
|
|
if (*pszIter >= 128)
|
|
{
|
|
bFoundNonASCII = true;
|
|
bIsASCII = false;
|
|
break;
|
|
}
|
|
pszIter++;
|
|
}
|
|
int bIsUTF8 = CPLIsUTF8(pszVal, -1);
|
|
if (bIsUTF8 && !bIsASCII)
|
|
bFoundUTF8 = true;
|
|
if (bIsAdvertizedAsUTF8)
|
|
{
|
|
if (!bIsUTF8)
|
|
{
|
|
printf("ERROR: Found non-UTF8 content at field %d "
|
|
"of feature " CPL_FRMT_GIB
|
|
", but layer is advertized as UTF-8.\n",
|
|
i, poFeature->GetFID());
|
|
bRet = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!bIsUTF8)
|
|
bCanAdvertiseUTF8 = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
DestroyFeatureAndNullify(poFeature);
|
|
}
|
|
|
|
if (!bFoundString)
|
|
{
|
|
}
|
|
else if (bCanAdvertiseUTF8 && bVerbose)
|
|
{
|
|
if (bIsAdvertizedAsUTF8)
|
|
{
|
|
if (bFoundUTF8)
|
|
{
|
|
printf("INFO: Layer has UTF-8 content and is consistently "
|
|
"declared as having UTF-8 content.\n");
|
|
}
|
|
else if (!bFoundNonASCII)
|
|
{
|
|
printf("INFO: Layer has ASCII only content and is "
|
|
"consistently declared as having UTF-8 content.\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bFoundUTF8)
|
|
{
|
|
printf("INFO: Layer could perhaps be advertized as UTF-8 "
|
|
"compatible (and it has non-ASCII UTF-8 content).\n");
|
|
}
|
|
else if (!bFoundNonASCII)
|
|
{
|
|
printf("INFO: Layer could perhaps be advertized as UTF-8 "
|
|
"compatible (it has only ASCII content).\n");
|
|
}
|
|
}
|
|
}
|
|
else if (bVerbose)
|
|
{
|
|
printf("INFO: Layer has non UTF-8 content (and is consistently "
|
|
"declared as not being UTF-8 compatible).\n");
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestGetExtent() */
|
|
/************************************************************************/
|
|
|
|
static int TestGetExtent(OGRLayer *poLayer, int iGeomField)
|
|
{
|
|
int bRet = TRUE;
|
|
|
|
LOG_ACTION(poLayer->SetSpatialFilter(nullptr));
|
|
LOG_ACTION(poLayer->SetAttributeFilter(nullptr));
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
|
|
OGREnvelope sExtent;
|
|
OGREnvelope sExtentSlow;
|
|
|
|
OGRErr eErr = LOG_ACTION(poLayer->GetExtent(iGeomField, &sExtent, TRUE));
|
|
OGRErr eErr2 = LOG_ACTION(
|
|
poLayer->OGRLayer::GetExtent(iGeomField, &sExtentSlow, TRUE));
|
|
|
|
if (eErr != eErr2)
|
|
{
|
|
if (eErr == OGRERR_NONE && eErr2 != OGRERR_NONE)
|
|
{
|
|
// With the LIBKML driver and test_ogrsf:
|
|
// ../autotest/ogr/data/samples.kml "Styles and Markup"
|
|
if (bVerbose)
|
|
{
|
|
printf("INFO: GetExtent() succeeded but OGRLayer::GetExtent() "
|
|
"failed.\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bRet = FALSE;
|
|
if (bVerbose)
|
|
{
|
|
printf("ERROR: GetExtent() failed but OGRLayer::GetExtent() "
|
|
"succeeded.\n");
|
|
}
|
|
}
|
|
}
|
|
else if (eErr == OGRERR_NONE && bVerbose)
|
|
{
|
|
if (fabs(sExtentSlow.MinX - sExtent.MinX) < 1e-10 &&
|
|
fabs(sExtentSlow.MinY - sExtent.MinY) < 1e-10 &&
|
|
fabs(sExtentSlow.MaxX - sExtent.MaxX) < 1e-10 &&
|
|
fabs(sExtentSlow.MaxY - sExtent.MaxY) < 1e-10)
|
|
{
|
|
printf("INFO: GetExtent() test passed.\n");
|
|
}
|
|
else
|
|
{
|
|
if (sExtentSlow.Contains(sExtent))
|
|
{
|
|
printf("INFO: sExtentSlow.Contains(sExtent)\n");
|
|
}
|
|
else if (sExtent.Contains(sExtentSlow))
|
|
{
|
|
printf("INFO: sExtent.Contains(sExtentSlow)\n");
|
|
}
|
|
else
|
|
{
|
|
printf("INFO: unknown relationship between sExtent and "
|
|
"sExtentSlow.\n");
|
|
}
|
|
printf("INFO: sExtentSlow.MinX = %.15f\n", sExtentSlow.MinX);
|
|
printf("INFO: sExtentSlow.MinY = %.15f\n", sExtentSlow.MinY);
|
|
printf("INFO: sExtentSlow.MaxX = %.15f\n", sExtentSlow.MaxX);
|
|
printf("INFO: sExtentSlow.MaxY = %.15f\n", sExtentSlow.MaxY);
|
|
printf("INFO: sExtent.MinX = %.15f\n", sExtent.MinX);
|
|
printf("INFO: sExtent.MinY = %.15f\n", sExtent.MinY);
|
|
printf("INFO: sExtent.MaxX = %.15f\n", sExtent.MaxX);
|
|
printf("INFO: sExtent.MaxY = %.15f\n", sExtent.MaxY);
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
static int TestGetExtent(OGRLayer *poLayer)
|
|
{
|
|
int bRet = TRUE;
|
|
const int nGeomFieldCount =
|
|
LOG_ACTION(poLayer->GetLayerDefn()->GetGeomFieldCount());
|
|
for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
|
|
bRet &= TestGetExtent(poLayer, iGeom);
|
|
|
|
OGREnvelope sExtent;
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
OGRErr eErr = LOG_ACTION(poLayer->GetExtent(-1, &sExtent, TRUE));
|
|
CPLPopErrorHandler();
|
|
if (eErr != OGRERR_FAILURE)
|
|
{
|
|
printf("ERROR: poLayer->GetExtent(-1) should fail.\n");
|
|
bRet = FALSE;
|
|
}
|
|
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
eErr = LOG_ACTION(poLayer->GetExtent(nGeomFieldCount, &sExtent, TRUE));
|
|
CPLPopErrorHandler();
|
|
if (eErr != OGRERR_FAILURE)
|
|
{
|
|
printf("ERROR: poLayer->GetExtent(nGeomFieldCount) should fail.\n");
|
|
bRet = FALSE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/* TestOGRLayerDeleteAndCreateFeature() */
|
|
/* */
|
|
/* Test delete feature by trying to delete the last feature and */
|
|
/* recreate it. */
|
|
/*************************************************************************/
|
|
|
|
static int TestOGRLayerDeleteAndCreateFeature(OGRLayer *poLayer)
|
|
|
|
{
|
|
int bRet = TRUE;
|
|
|
|
LOG_ACTION(poLayer->SetSpatialFilter(nullptr));
|
|
|
|
if (!LOG_ACTION(poLayer->TestCapability(OLCRandomRead)))
|
|
{
|
|
if (bVerbose)
|
|
printf("INFO: Skipping delete feature test since this layer "
|
|
"doesn't support random read.\n");
|
|
return bRet;
|
|
}
|
|
|
|
if (LOG_ACTION(poLayer->GetFeatureCount()) == 0)
|
|
{
|
|
if (bVerbose)
|
|
printf("INFO: No feature available on layer '%s',"
|
|
"skipping delete/create feature test.\n",
|
|
poLayer->GetName());
|
|
|
|
return bRet;
|
|
}
|
|
/* -------------------------------------------------------------------- */
|
|
/* Fetch the last feature */
|
|
/* -------------------------------------------------------------------- */
|
|
OGRFeature *poFeatureTest = nullptr;
|
|
GIntBig nFID = 0;
|
|
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
|
|
LOG_ACTION(poLayer->SetNextByIndex(poLayer->GetFeatureCount() - 1));
|
|
OGRFeature *poFeature = LOG_ACTION(poLayer->GetNextFeature());
|
|
if (poFeature == nullptr)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Could not get last feature of layer.\n");
|
|
goto end;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the feature ID of the last feature */
|
|
/* -------------------------------------------------------------------- */
|
|
nFID = poFeature->GetFID();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Delete the feature. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
if (LOG_ACTION(poLayer->DeleteFeature(nFID)) != OGRERR_NONE)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Attempt to DeleteFeature() failed.\n");
|
|
goto end;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Now re-read the feature to verify the delete effect worked. */
|
|
/* -------------------------------------------------------------------- */
|
|
// Silent legitimate error message.
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
poFeatureTest = LOG_ACTION(poLayer->GetFeature(nFID));
|
|
CPLPopErrorHandler();
|
|
if (poFeatureTest != nullptr)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: The feature was not deleted.\n");
|
|
}
|
|
else if (bVerbose)
|
|
{
|
|
printf("INFO: Delete Feature test passed.\n");
|
|
}
|
|
DestroyFeatureAndNullify(poFeatureTest);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Re-insert the features to restore to original state */
|
|
/* -------------------------------------------------------------------- */
|
|
if (LOG_ACTION(poLayer->CreateFeature(poFeature)) != OGRERR_NONE)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: Attempt to restore feature failed.\n");
|
|
}
|
|
|
|
if (poFeature->GetFID() != nFID)
|
|
{
|
|
/* Case of shapefile driver for example that will not try to */
|
|
/* reuse the existing FID, but will assign a new one */
|
|
if (bVerbose)
|
|
{
|
|
printf("INFO: Feature was created, "
|
|
"but with not its original FID.\n");
|
|
}
|
|
nFID = poFeature->GetFID();
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Now re-read the feature to verify the create effect worked. */
|
|
/* -------------------------------------------------------------------- */
|
|
poFeatureTest = LOG_ACTION(poLayer->GetFeature(nFID));
|
|
if (poFeatureTest == nullptr)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: The feature was not created.\n");
|
|
}
|
|
else if (bVerbose)
|
|
{
|
|
printf("INFO: Create Feature test passed.\n");
|
|
}
|
|
DestroyFeatureAndNullify(poFeatureTest);
|
|
|
|
end:
|
|
/* -------------------------------------------------------------------- */
|
|
/* Cleanup. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
DestroyFeatureAndNullify(poFeature);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/* TestTransactions() */
|
|
/*************************************************************************/
|
|
|
|
static int TestTransactions(OGRLayer *poLayer)
|
|
|
|
{
|
|
GIntBig nInitialFeatureCount = LOG_ACTION(poLayer->GetFeatureCount());
|
|
|
|
OGRErr eErr = LOG_ACTION(poLayer->StartTransaction());
|
|
if (eErr == OGRERR_NONE)
|
|
{
|
|
if (LOG_ACTION(poLayer->TestCapability(OLCTransactions)) == FALSE)
|
|
{
|
|
eErr = LOG_ACTION(poLayer->RollbackTransaction());
|
|
if (eErr == OGRERR_UNSUPPORTED_OPERATION &&
|
|
LOG_ACTION(poLayer->TestCapability(OLCTransactions)) == FALSE)
|
|
{
|
|
// The default implementation has a dummy
|
|
// StartTransaction(), but RollbackTransaction()
|
|
// returns OGRERR_UNSUPPORTED_OPERATION
|
|
if (bVerbose)
|
|
{
|
|
printf("INFO: Transactions test skipped due to lack of "
|
|
"transaction support.\n");
|
|
}
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
printf("WARN: StartTransaction() is supported, but "
|
|
"TestCapability(OLCTransactions) returns FALSE.\n");
|
|
}
|
|
}
|
|
}
|
|
else if (eErr == OGRERR_FAILURE)
|
|
{
|
|
if (LOG_ACTION(poLayer->TestCapability(OLCTransactions)) == TRUE)
|
|
{
|
|
printf("ERROR: StartTransaction() failed, but "
|
|
"TestCapability(OLCTransactions) returns TRUE.\n");
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
eErr = LOG_ACTION(poLayer->RollbackTransaction());
|
|
if (eErr != OGRERR_NONE)
|
|
{
|
|
printf("ERROR: RollbackTransaction() failed after successful "
|
|
"StartTransaction().\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* ---------------- */
|
|
|
|
eErr = LOG_ACTION(poLayer->StartTransaction());
|
|
if (eErr != OGRERR_NONE)
|
|
{
|
|
printf("ERROR: StartTransaction() failed.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
eErr = LOG_ACTION(poLayer->CommitTransaction());
|
|
if (eErr != OGRERR_NONE)
|
|
{
|
|
printf("ERROR: CommitTransaction() failed after successful "
|
|
"StartTransaction().\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* ---------------- */
|
|
|
|
eErr = LOG_ACTION(poLayer->StartTransaction());
|
|
if (eErr != OGRERR_NONE)
|
|
{
|
|
printf("ERROR: StartTransaction() failed.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
|
|
if (poLayer->GetLayerDefn()->GetFieldCount() > 0)
|
|
poFeature->SetField(0, "0");
|
|
eErr = LOG_ACTION(poLayer->CreateFeature(poFeature));
|
|
delete poFeature;
|
|
poFeature = nullptr;
|
|
|
|
if (eErr == OGRERR_FAILURE)
|
|
{
|
|
if (bVerbose)
|
|
{
|
|
printf("INFO: CreateFeature() failed. Exiting this test now.\n");
|
|
}
|
|
LOG_ACTION(poLayer->RollbackTransaction());
|
|
return TRUE;
|
|
}
|
|
|
|
eErr = LOG_ACTION(poLayer->RollbackTransaction());
|
|
if (eErr != OGRERR_NONE)
|
|
{
|
|
printf("ERROR: RollbackTransaction() failed after successful "
|
|
"StartTransaction().\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (LOG_ACTION(poLayer->GetFeatureCount()) != nInitialFeatureCount)
|
|
{
|
|
printf("ERROR: GetFeatureCount() should have returned its initial "
|
|
"value after RollbackTransaction().\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* ---------------- */
|
|
|
|
if (LOG_ACTION(poLayer->TestCapability(OLCDeleteFeature)))
|
|
{
|
|
eErr = LOG_ACTION(poLayer->StartTransaction());
|
|
if (eErr != OGRERR_NONE)
|
|
{
|
|
printf("ERROR: StartTransaction() failed.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
poFeature = new OGRFeature(poLayer->GetLayerDefn());
|
|
if (poLayer->GetLayerDefn()->GetFieldCount() > 0)
|
|
poFeature->SetField(0, "0");
|
|
eErr = poLayer->CreateFeature(poFeature);
|
|
GIntBig nFID = poFeature->GetFID();
|
|
delete poFeature;
|
|
poFeature = nullptr;
|
|
|
|
if (eErr == OGRERR_FAILURE)
|
|
{
|
|
printf("ERROR: CreateFeature() failed. Exiting this test now.\n");
|
|
LOG_ACTION(poLayer->RollbackTransaction());
|
|
return FALSE;
|
|
}
|
|
|
|
if (nFID < 0)
|
|
{
|
|
printf("WARNING: CreateFeature() returned featured without FID.\n");
|
|
LOG_ACTION(poLayer->RollbackTransaction());
|
|
return FALSE;
|
|
}
|
|
|
|
eErr = LOG_ACTION(poLayer->CommitTransaction());
|
|
if (eErr != OGRERR_NONE)
|
|
{
|
|
printf("ERROR: CommitTransaction() failed after successful "
|
|
"StartTransaction().\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (LOG_ACTION(poLayer->GetFeatureCount()) != nInitialFeatureCount + 1)
|
|
{
|
|
printf("ERROR: GetFeatureCount() should have returned its initial "
|
|
"value + 1 after CommitTransaction().\n");
|
|
return FALSE;
|
|
}
|
|
|
|
eErr = LOG_ACTION(poLayer->DeleteFeature(nFID));
|
|
if (eErr != OGRERR_NONE)
|
|
{
|
|
printf("ERROR: DeleteFeature() failed.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (LOG_ACTION(poLayer->GetFeatureCount()) != nInitialFeatureCount)
|
|
{
|
|
printf("ERROR: GetFeatureCount() should have returned its initial "
|
|
"value after DeleteFeature().\n");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* ---------------- */
|
|
|
|
if (bVerbose)
|
|
{
|
|
printf("INFO: Transactions test passed.\n");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestOGRLayerIgnoreFields() */
|
|
/************************************************************************/
|
|
|
|
static int TestOGRLayerIgnoreFields(OGRLayer *poLayer)
|
|
{
|
|
int iFieldNonEmpty = -1;
|
|
int iFieldNonEmpty2 = -1;
|
|
bool bGeomNonEmpty = false;
|
|
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
OGRFeature *poFeature = nullptr;
|
|
while ((poFeature = LOG_ACTION(poLayer->GetNextFeature())) != nullptr)
|
|
{
|
|
if (iFieldNonEmpty < 0)
|
|
{
|
|
for (int i = 0; i < poFeature->GetFieldCount(); i++)
|
|
{
|
|
if (poFeature->IsFieldSetAndNotNull(i))
|
|
{
|
|
iFieldNonEmpty = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (iFieldNonEmpty2 < 0)
|
|
{
|
|
for (int i = 0; i < poFeature->GetFieldCount(); i++)
|
|
{
|
|
if (i != iFieldNonEmpty && poFeature->IsFieldSetAndNotNull(i))
|
|
{
|
|
iFieldNonEmpty2 = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bGeomNonEmpty && poFeature->GetGeometryRef() != nullptr)
|
|
bGeomNonEmpty = true;
|
|
|
|
delete poFeature;
|
|
}
|
|
|
|
if (iFieldNonEmpty < 0 && !bGeomNonEmpty)
|
|
{
|
|
if (bVerbose)
|
|
{
|
|
printf("INFO: IgnoreFields test skipped.\n");
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
char **papszIgnoredFields = nullptr;
|
|
if (iFieldNonEmpty >= 0)
|
|
papszIgnoredFields =
|
|
CSLAddString(papszIgnoredFields, poLayer->GetLayerDefn()
|
|
->GetFieldDefn(iFieldNonEmpty)
|
|
->GetNameRef());
|
|
|
|
if (bGeomNonEmpty)
|
|
papszIgnoredFields = CSLAddString(papszIgnoredFields, "OGR_GEOMETRY");
|
|
|
|
OGRErr eErr = LOG_ACTION(poLayer->SetIgnoredFields(
|
|
const_cast<const char **>(papszIgnoredFields)));
|
|
CSLDestroy(papszIgnoredFields);
|
|
|
|
if (eErr == OGRERR_FAILURE)
|
|
{
|
|
printf("ERROR: SetIgnoredFields() failed.\n");
|
|
poLayer->SetIgnoredFields(nullptr);
|
|
return FALSE;
|
|
}
|
|
|
|
bool bFoundNonEmpty2 = false;
|
|
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
while ((poFeature = LOG_ACTION(poLayer->GetNextFeature())) != nullptr)
|
|
{
|
|
if (iFieldNonEmpty >= 0 &&
|
|
poFeature->IsFieldSetAndNotNull(iFieldNonEmpty))
|
|
{
|
|
delete poFeature;
|
|
printf("ERROR: After SetIgnoredFields(), "
|
|
"found a non empty field that should have been ignored.\n");
|
|
poLayer->SetIgnoredFields(nullptr);
|
|
return FALSE;
|
|
}
|
|
|
|
if (iFieldNonEmpty2 >= 0 &&
|
|
poFeature->IsFieldSetAndNotNull(iFieldNonEmpty2))
|
|
bFoundNonEmpty2 = true;
|
|
|
|
if (bGeomNonEmpty && poFeature->GetGeometryRef() != nullptr)
|
|
{
|
|
delete poFeature;
|
|
printf(
|
|
"ERROR: After SetIgnoredFields(), "
|
|
"found a non empty geometry that should have been ignored.\n");
|
|
poLayer->SetIgnoredFields(nullptr);
|
|
return FALSE;
|
|
}
|
|
|
|
delete poFeature;
|
|
}
|
|
|
|
if (iFieldNonEmpty2 >= 0 && !bFoundNonEmpty2)
|
|
{
|
|
printf("ERROR: SetIgnoredFields() discarded fields that it "
|
|
"should not have discarded.\n");
|
|
poLayer->SetIgnoredFields(nullptr);
|
|
return FALSE;
|
|
}
|
|
|
|
LOG_ACTION(poLayer->SetIgnoredFields(nullptr));
|
|
|
|
if (bVerbose)
|
|
{
|
|
printf("INFO: IgnoreFields test passed.\n");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestLayerSQL() */
|
|
/************************************************************************/
|
|
|
|
static int TestLayerSQL(GDALDataset *poDS, OGRLayer *poLayer)
|
|
|
|
{
|
|
int bRet = TRUE;
|
|
bool bGotFeature = false;
|
|
|
|
/* Test consistency between result layer and traditional layer */
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
OGRFeature *poLayerFeat = LOG_ACTION(poLayer->GetNextFeature());
|
|
|
|
/* Reset to avoid potentially a statement to be active which cause */
|
|
/* issue in the transaction test of the second layer, when testing */
|
|
/* multi-tables sqlite and gpkg databases */
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
|
|
CPLString osSQL;
|
|
osSQL.Printf("SELECT * FROM %s",
|
|
GetLayerNameForSQL(poDS, poLayer->GetName()));
|
|
OGRLayer *poSQLLyr =
|
|
LOG_ACTION(poDS->ExecuteSQL(osSQL.c_str(), nullptr, nullptr));
|
|
OGRFeature *poSQLFeat = nullptr;
|
|
if (poSQLLyr == nullptr)
|
|
{
|
|
printf("ERROR: ExecuteSQL(%s) failed.\n", osSQL.c_str());
|
|
bRet = FALSE;
|
|
return bRet;
|
|
}
|
|
else
|
|
{
|
|
poSQLFeat = LOG_ACTION(poSQLLyr->GetNextFeature());
|
|
if (poSQLFeat != nullptr)
|
|
bGotFeature = TRUE;
|
|
if (poLayerFeat == nullptr && poSQLFeat != nullptr)
|
|
{
|
|
printf("ERROR: poLayerFeat == NULL && poSQLFeat != NULL.\n");
|
|
bRet = FALSE;
|
|
}
|
|
else if (poLayerFeat != nullptr && poSQLFeat == nullptr)
|
|
{
|
|
printf("ERROR: poLayerFeat != NULL && poSQLFeat == NULL.\n");
|
|
bRet = FALSE;
|
|
}
|
|
else if (poLayerFeat != nullptr && poSQLFeat != nullptr)
|
|
{
|
|
if (poLayer->GetLayerDefn()->GetGeomFieldCount() !=
|
|
poSQLLyr->GetLayerDefn()->GetGeomFieldCount())
|
|
{
|
|
printf("ERROR: poLayer->GetLayerDefn()->GetGeomFieldCount() != "
|
|
"poSQLLyr->GetLayerDefn()->GetGeomFieldCount().\n");
|
|
bRet = FALSE;
|
|
}
|
|
else
|
|
{
|
|
int nGeomFieldCount =
|
|
poLayer->GetLayerDefn()->GetGeomFieldCount();
|
|
for (int i = 0; i < nGeomFieldCount; i++)
|
|
{
|
|
int iOtherI;
|
|
if (nGeomFieldCount != 1)
|
|
{
|
|
OGRGeomFieldDefn *poGFldDefn =
|
|
poLayer->GetLayerDefn()->GetGeomFieldDefn(i);
|
|
iOtherI = poSQLLyr->GetLayerDefn()->GetGeomFieldIndex(
|
|
poGFldDefn->GetNameRef());
|
|
if (iOtherI == -1)
|
|
{
|
|
printf("ERROR: Cannot find geom field in SQL "
|
|
"matching %s.\n",
|
|
poGFldDefn->GetNameRef());
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
iOtherI = 0;
|
|
OGRGeometry *poLayerFeatGeom =
|
|
poLayerFeat->GetGeomFieldRef(i);
|
|
OGRGeometry *poSQLFeatGeom =
|
|
poSQLFeat->GetGeomFieldRef(iOtherI);
|
|
if (poLayerFeatGeom == nullptr && poSQLFeatGeom != nullptr)
|
|
{
|
|
printf("ERROR: poLayerFeatGeom[%d] == NULL && "
|
|
"poSQLFeatGeom[%d] != NULL.\n",
|
|
i, iOtherI);
|
|
bRet = FALSE;
|
|
}
|
|
else if (poLayerFeatGeom != nullptr &&
|
|
poSQLFeatGeom == nullptr)
|
|
{
|
|
printf("ERROR: poLayerFeatGeom[%d] != NULL && "
|
|
"poSQLFeatGeom[%d] == NULL.\n",
|
|
i, iOtherI);
|
|
bRet = FALSE;
|
|
}
|
|
else if (poLayerFeatGeom != nullptr &&
|
|
poSQLFeatGeom != nullptr)
|
|
{
|
|
const OGRSpatialReference *poLayerFeatSRS =
|
|
poLayerFeatGeom->getSpatialReference();
|
|
const OGRSpatialReference *poSQLFeatSRS =
|
|
poSQLFeatGeom->getSpatialReference();
|
|
if (poLayerFeatSRS == nullptr &&
|
|
poSQLFeatSRS != nullptr)
|
|
{
|
|
printf("ERROR: poLayerFeatSRS == NULL && "
|
|
"poSQLFeatSRS != NULL.\n");
|
|
bRet = FALSE;
|
|
}
|
|
else if (poLayerFeatSRS != nullptr &&
|
|
poSQLFeatSRS == nullptr)
|
|
{
|
|
printf("ERROR: poLayerFeatSRS != NULL && "
|
|
"poSQLFeatSRS == NULL.\n");
|
|
bRet = FALSE;
|
|
}
|
|
else if (poLayerFeatSRS != nullptr &&
|
|
poSQLFeatSRS != nullptr)
|
|
{
|
|
if (!(poLayerFeatSRS->IsSame(poSQLFeatSRS)))
|
|
{
|
|
printf("ERROR: !(poLayerFeatSRS->IsSame("
|
|
"poSQLFeatSRS)).\n");
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DestroyFeatureAndNullify(poLayerFeat);
|
|
DestroyFeatureAndNullify(poSQLFeat);
|
|
|
|
LOG_ACTION(poDS->ReleaseResultSet(poSQLLyr));
|
|
|
|
/* Try ResetReading(), GetNextFeature(), ResetReading(), GetNextFeature() */
|
|
poSQLLyr = LOG_ACTION(poDS->ExecuteSQL(osSQL.c_str(), nullptr, nullptr));
|
|
if (poSQLLyr == nullptr)
|
|
{
|
|
printf("ERROR: ExecuteSQL(%s) failed at line %d "
|
|
"(but succeeded before).\n",
|
|
osSQL.c_str(), __LINE__);
|
|
bRet = FALSE;
|
|
return bRet;
|
|
}
|
|
LOG_ACTION(poSQLLyr->ResetReading());
|
|
|
|
poSQLFeat = LOG_ACTION(poSQLLyr->GetNextFeature());
|
|
if (poSQLFeat == nullptr && bGotFeature)
|
|
{
|
|
printf("ERROR: Should have got feature (1)\n");
|
|
bRet = FALSE;
|
|
}
|
|
DestroyFeatureAndNullify(poSQLFeat);
|
|
|
|
LOG_ACTION(poSQLLyr->ResetReading());
|
|
|
|
poSQLFeat = LOG_ACTION(poSQLLyr->GetNextFeature());
|
|
if (poSQLFeat == nullptr && bGotFeature)
|
|
{
|
|
printf("ERROR: Should have got feature (2)\n");
|
|
bRet = FALSE;
|
|
}
|
|
DestroyFeatureAndNullify(poSQLFeat);
|
|
|
|
LOG_ACTION(poDS->ReleaseResultSet(poSQLLyr));
|
|
|
|
/* Return an empty layer */
|
|
osSQL.Printf("SELECT * FROM %s WHERE 0 = 1",
|
|
GetLayerNameForSQL(poDS, poLayer->GetName()));
|
|
|
|
poSQLLyr = LOG_ACTION(poDS->ExecuteSQL(osSQL.c_str(), nullptr, nullptr));
|
|
if (poSQLLyr)
|
|
{
|
|
poSQLFeat = LOG_ACTION(poSQLLyr->GetNextFeature());
|
|
if (poSQLFeat != nullptr)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: ExecuteSQL() should have returned "
|
|
"a layer without features.\n");
|
|
}
|
|
DestroyFeatureAndNullify(poSQLFeat);
|
|
|
|
LOG_ACTION(poDS->ReleaseResultSet(poSQLLyr));
|
|
}
|
|
else
|
|
{
|
|
printf("ERROR: ExecuteSQL() should have returned a non-NULL result.\n");
|
|
bRet = FALSE;
|
|
}
|
|
|
|
// Test that installing a spatial filter on an empty layer at ExecuteSQL()
|
|
// does not raise an error
|
|
osSQL.Printf("SELECT * FROM %s WHERE 0 = 1",
|
|
GetLayerNameForSQL(poDS, poLayer->GetName()));
|
|
|
|
OGRLinearRing oRing;
|
|
oRing.setPoint(0, 0, 0);
|
|
oRing.setPoint(1, 0, 1);
|
|
oRing.setPoint(2, 1, 1);
|
|
oRing.setPoint(3, 1, 0);
|
|
oRing.setPoint(4, 0, 0);
|
|
|
|
OGRPolygon oPoly;
|
|
oPoly.addRing(&oRing);
|
|
|
|
CPLErrorReset();
|
|
if (poLayer->GetLayerDefn()->GetGeomFieldCount() == 0)
|
|
{
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
poSQLLyr = LOG_ACTION(poDS->ExecuteSQL(osSQL.c_str(), &oPoly, nullptr));
|
|
CPLPopErrorHandler();
|
|
if (poSQLLyr)
|
|
{
|
|
printf("WARNING: ExecuteSQL() with a spatial filter on a "
|
|
"non-spatial layer should have triggered an error.\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
poSQLLyr = LOG_ACTION(poDS->ExecuteSQL(osSQL.c_str(), &oPoly, nullptr));
|
|
if (CPLGetLastErrorType() == CE_Failure &&
|
|
poLayer->GetLayerDefn()->GetGeomFieldCount() > 0)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: ExecuteSQL() triggered an unexpected error.\n");
|
|
}
|
|
if (!poSQLLyr)
|
|
{
|
|
printf("ERROR: ExecuteSQL() should have returned a non-NULL "
|
|
"result.\n");
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
if (poSQLLyr)
|
|
{
|
|
CPLErrorReset();
|
|
poSQLFeat = LOG_ACTION(poSQLLyr->GetNextFeature());
|
|
if (CPLGetLastErrorType() == CE_Failure)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: GetNextFeature() triggered an unexpected error.\n");
|
|
}
|
|
if (poSQLFeat != nullptr)
|
|
{
|
|
bRet = FALSE;
|
|
printf("ERROR: ExecuteSQL() should have returned "
|
|
"a layer without features.\n");
|
|
}
|
|
DestroyFeatureAndNullify(poSQLFeat);
|
|
LOG_ACTION(poDS->ReleaseResultSet(poSQLLyr));
|
|
}
|
|
|
|
if (bRet && bVerbose)
|
|
printf("INFO: TestLayerSQL passed.\n");
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CountFeaturesUsingArrowStream() */
|
|
/************************************************************************/
|
|
|
|
static int64_t CountFeaturesUsingArrowStream(OGRLayer *poLayer,
|
|
int64_t nExpectedFID,
|
|
int64_t nUnexpectedFID, bool &bOK)
|
|
{
|
|
struct ArrowArrayStream stream;
|
|
if (!LOG_ACTION(poLayer->GetArrowStream(&stream)))
|
|
{
|
|
printf("ERROR: GetArrowStream() failed\n");
|
|
return -1;
|
|
}
|
|
struct ArrowSchema schema;
|
|
if (stream.get_schema(&stream, &schema) != 0)
|
|
{
|
|
printf("ERROR: stream.get_schema() failed\n");
|
|
stream.release(&stream);
|
|
return -1;
|
|
}
|
|
int iFIDColumn = -1;
|
|
if (schema.n_children > 0 &&
|
|
(strcmp(schema.children[0]->name, "OGC_FID") == 0 ||
|
|
strcmp(schema.children[0]->name, poLayer->GetFIDColumn()) == 0) &&
|
|
strcmp(schema.children[0]->format, "l") == 0)
|
|
{
|
|
iFIDColumn = 0;
|
|
}
|
|
schema.release(&schema);
|
|
int64_t nFeatureCountFiltered = 0;
|
|
bool bExpectedFIDFound = false;
|
|
bool bUnexpectedFIDFound = false;
|
|
while (true)
|
|
{
|
|
struct ArrowArray array;
|
|
if (stream.get_next(&stream, &array) != 0)
|
|
{
|
|
printf("ERROR: stream.get_next() is NULL\n");
|
|
stream.release(&stream);
|
|
return -1;
|
|
}
|
|
if (!array.release)
|
|
break;
|
|
if (iFIDColumn >= 0 && (nExpectedFID >= 0 || nUnexpectedFID >= 0))
|
|
{
|
|
const int64_t *panIds =
|
|
static_cast<const int64_t *>(
|
|
array.children[iFIDColumn]->buffers[1]) +
|
|
array.children[iFIDColumn]->offset;
|
|
for (int64_t i = 0; i < array.length; ++i)
|
|
{
|
|
if (nExpectedFID >= 0 && panIds[i] == nExpectedFID)
|
|
bExpectedFIDFound = true;
|
|
if (nUnexpectedFID >= 0 && panIds[i] == nUnexpectedFID)
|
|
bUnexpectedFIDFound = true;
|
|
}
|
|
}
|
|
nFeatureCountFiltered += array.length;
|
|
array.release(&array);
|
|
}
|
|
if (iFIDColumn >= 0)
|
|
{
|
|
if (nExpectedFID >= 0 && !bExpectedFIDFound)
|
|
{
|
|
bOK = false;
|
|
printf("ERROR: CountFeaturesUsingArrowStream() :"
|
|
"expected to find feature of id %" PRId64
|
|
", but did not get it\n",
|
|
nExpectedFID);
|
|
}
|
|
if (nUnexpectedFID >= 0 && bUnexpectedFIDFound)
|
|
{
|
|
bOK = false;
|
|
printf("ERROR: CountFeaturesUsingArrowStream(): "
|
|
"expected *not* to find feature of id %" PRId64
|
|
", but did get it\n",
|
|
nUnexpectedFID);
|
|
}
|
|
}
|
|
stream.release(&stream);
|
|
return nFeatureCountFiltered;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestLayerGetArrowStream() */
|
|
/************************************************************************/
|
|
|
|
static int TestLayerGetArrowStream(OGRLayer *poLayer)
|
|
{
|
|
LOG_ACTION(poLayer->SetSpatialFilter(nullptr));
|
|
LOG_ACTION(poLayer->SetAttributeFilter(nullptr));
|
|
LOG_ACTION(poLayer->ResetReading());
|
|
|
|
struct ArrowArrayStream stream;
|
|
if (!LOG_ACTION(poLayer->GetArrowStream(&stream)))
|
|
{
|
|
printf("ERROR: GetArrowStream() failed\n");
|
|
return false;
|
|
}
|
|
|
|
if (!stream.release)
|
|
{
|
|
printf("ERROR: stream.release is NULL\n");
|
|
return false;
|
|
}
|
|
|
|
struct ArrowSchema schema;
|
|
if (stream.get_schema(&stream, &schema) != 0)
|
|
{
|
|
printf("ERROR: stream.get_schema() failed\n");
|
|
stream.release(&stream);
|
|
return false;
|
|
}
|
|
|
|
if (!schema.release)
|
|
{
|
|
printf("ERROR: schema.release is NULL\n");
|
|
stream.release(&stream);
|
|
return false;
|
|
}
|
|
|
|
if (strcmp(schema.format, "+s") != 0)
|
|
{
|
|
printf("ERROR: expected schema.format to be '+s'. Got '%s'\n",
|
|
schema.format);
|
|
schema.release(&schema);
|
|
stream.release(&stream);
|
|
return false;
|
|
}
|
|
|
|
int64_t nFeatureCount = 0;
|
|
while (true)
|
|
{
|
|
struct ArrowArray array;
|
|
if (stream.get_next(&stream, &array) != 0)
|
|
{
|
|
printf("ERROR: stream.get_next() is NULL\n");
|
|
schema.release(&schema);
|
|
stream.release(&stream);
|
|
return false;
|
|
}
|
|
if (array.release == nullptr)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (array.n_children != schema.n_children)
|
|
{
|
|
printf("ERROR: expected array.n_children (=%d) to be "
|
|
"schema.n_children (=%d)\n",
|
|
int(array.n_children), int(schema.n_children));
|
|
array.release(&array);
|
|
schema.release(&schema);
|
|
stream.release(&stream);
|
|
return false;
|
|
}
|
|
|
|
int bRet = true;
|
|
for (int i = 0; i < array.n_children; ++i)
|
|
{
|
|
if (array.children[i]->length != array.length)
|
|
{
|
|
bRet = false;
|
|
printf("ERROR: expected array.children[i]->length (=%d) to be "
|
|
"array.length (=%d)\n",
|
|
int(array.children[i]->length), int(array.length));
|
|
}
|
|
}
|
|
if (!bRet)
|
|
{
|
|
array.release(&array);
|
|
schema.release(&schema);
|
|
stream.release(&stream);
|
|
return false;
|
|
}
|
|
|
|
nFeatureCount += array.length;
|
|
|
|
array.release(&array);
|
|
|
|
if (array.release)
|
|
{
|
|
printf("ERROR: array.release should be NULL after release\n");
|
|
schema.release(&schema);
|
|
stream.release(&stream);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool bRet = true;
|
|
// We are a bit in non-specified behavior below by calling get_next()
|
|
// after end of iteration.
|
|
{
|
|
struct ArrowArray array;
|
|
if (stream.get_next(&stream, &array) == 0)
|
|
{
|
|
if (array.length != 0)
|
|
{
|
|
printf("WARNING: get_next() return an array with length != 0 "
|
|
"after end of iteration\n");
|
|
}
|
|
if (array.release)
|
|
array.release(&array);
|
|
}
|
|
}
|
|
|
|
schema.release(&schema);
|
|
if (schema.release)
|
|
{
|
|
printf("ERROR: schema.release should be NULL after release\n");
|
|
stream.release(&stream);
|
|
return false;
|
|
}
|
|
|
|
stream.release(&stream);
|
|
if (stream.release)
|
|
{
|
|
printf("ERROR: stream.release should be NULL after release\n");
|
|
return false;
|
|
}
|
|
|
|
const int64_t nFCClassic = poLayer->GetFeatureCount(true);
|
|
if (nFeatureCount != nFCClassic)
|
|
{
|
|
printf("ERROR: GetArrowStream() returned %" PRId64
|
|
" features, whereas GetFeatureCount() returned %" PRId64 "\n",
|
|
nFeatureCount, nFCClassic);
|
|
bRet = false;
|
|
}
|
|
|
|
{
|
|
LOG_ACTION(poLayer->SetAttributeFilter("1=1"));
|
|
const auto nFeatureCountFiltered =
|
|
CountFeaturesUsingArrowStream(poLayer, -1, -1, bRet);
|
|
LOG_ACTION(poLayer->SetAttributeFilter(nullptr));
|
|
if (nFeatureCount != nFeatureCountFiltered)
|
|
{
|
|
printf("ERROR: GetArrowStream() with 1=1 filter returned %" PRId64
|
|
" features, whereas %" PRId64 " expected\n",
|
|
nFeatureCountFiltered, nFeatureCount);
|
|
bRet = false;
|
|
}
|
|
}
|
|
|
|
{
|
|
LOG_ACTION(poLayer->SetAttributeFilter("1=0"));
|
|
const auto nFeatureCountFiltered =
|
|
CountFeaturesUsingArrowStream(poLayer, -1, -1, bRet);
|
|
LOG_ACTION(poLayer->SetAttributeFilter(nullptr));
|
|
if (nFeatureCountFiltered != 0)
|
|
{
|
|
printf("ERROR: GetArrowStream() with 1=0 filter returned %" PRId64
|
|
" features, whereas 0 expected\n",
|
|
nFeatureCountFiltered);
|
|
bRet = false;
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<OGRFeature> poTargetFeature;
|
|
std::string osInclusiveFilter;
|
|
std::string osExclusiveFilter;
|
|
if (GetAttributeFilters(poLayer, poTargetFeature, osInclusiveFilter,
|
|
osExclusiveFilter))
|
|
{
|
|
{
|
|
LOG_ACTION(poLayer->SetAttributeFilter(osInclusiveFilter.c_str()));
|
|
const auto nFeatureCountFiltered = CountFeaturesUsingArrowStream(
|
|
poLayer, poTargetFeature->GetFID(), -1, bRet);
|
|
LOG_ACTION(poLayer->SetAttributeFilter(nullptr));
|
|
if (nFeatureCountFiltered == 0)
|
|
{
|
|
printf(
|
|
"ERROR: GetArrowStream() with %s filter returned %" PRId64
|
|
" features, whereas at least one expected\n",
|
|
osInclusiveFilter.c_str(), nFeatureCountFiltered);
|
|
bRet = false;
|
|
}
|
|
else if (bVerbose)
|
|
{
|
|
printf("INFO: Attribute filter inclusion with GetArrowStream "
|
|
"seems to work.\n");
|
|
}
|
|
}
|
|
|
|
{
|
|
LOG_ACTION(poLayer->SetAttributeFilter(osExclusiveFilter.c_str()));
|
|
const auto nFeatureCountFiltered =
|
|
CountFeaturesUsingArrowStream(poLayer, -1, -1, bRet);
|
|
LOG_ACTION(poLayer->SetAttributeFilter(nullptr));
|
|
if (nFeatureCountFiltered >= nFCClassic)
|
|
{
|
|
printf(
|
|
"ERROR: GetArrowStream() with %s filter returned %" PRId64
|
|
" features, whereas less than %" PRId64 " expected\n",
|
|
osExclusiveFilter.c_str(), nFeatureCountFiltered,
|
|
nFCClassic);
|
|
bRet = false;
|
|
}
|
|
else if (bVerbose)
|
|
{
|
|
printf("INFO: Attribute filter exclusion with GetArrowStream "
|
|
"seems to work.\n");
|
|
}
|
|
}
|
|
|
|
auto poGeom = poTargetFeature->GetGeometryRef();
|
|
if (poGeom && !poGeom->IsEmpty())
|
|
{
|
|
OGREnvelope sEnvelope;
|
|
poGeom->getEnvelope(&sEnvelope);
|
|
|
|
OGREnvelope sLayerExtent;
|
|
double epsilon = 10.0;
|
|
if (LOG_ACTION(poLayer->TestCapability(OLCFastGetExtent)) &&
|
|
LOG_ACTION(poLayer->GetExtent(&sLayerExtent)) == OGRERR_NONE &&
|
|
sLayerExtent.MinX < sLayerExtent.MaxX &&
|
|
sLayerExtent.MinY < sLayerExtent.MaxY)
|
|
{
|
|
epsilon = std::min(sLayerExtent.MaxX - sLayerExtent.MinX,
|
|
sLayerExtent.MaxY - sLayerExtent.MinY) /
|
|
10.0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Construct inclusive filter. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
OGRLinearRing oRing;
|
|
oRing.setPoint(0, sEnvelope.MinX - 2 * epsilon,
|
|
sEnvelope.MinY - 2 * epsilon);
|
|
oRing.setPoint(1, sEnvelope.MinX - 2 * epsilon,
|
|
sEnvelope.MaxY + 1 * epsilon);
|
|
oRing.setPoint(2, sEnvelope.MaxX + 1 * epsilon,
|
|
sEnvelope.MaxY + 1 * epsilon);
|
|
oRing.setPoint(3, sEnvelope.MaxX + 1 * epsilon,
|
|
sEnvelope.MinY - 2 * epsilon);
|
|
oRing.setPoint(4, sEnvelope.MinX - 2 * epsilon,
|
|
sEnvelope.MinY - 2 * epsilon);
|
|
|
|
OGRPolygon oInclusiveFilter;
|
|
oInclusiveFilter.addRing(&oRing);
|
|
|
|
LOG_ACTION(poLayer->SetSpatialFilter(&oInclusiveFilter));
|
|
const auto nFeatureCountFiltered = CountFeaturesUsingArrowStream(
|
|
poLayer, poTargetFeature->GetFID(), -1, bRet);
|
|
LOG_ACTION(poLayer->SetSpatialFilter(nullptr));
|
|
if (nFeatureCountFiltered == 0)
|
|
{
|
|
printf("ERROR: GetArrowStream() with inclusive spatial filter "
|
|
"returned %" PRId64
|
|
" features, whereas at least 1 expected\n",
|
|
nFeatureCountFiltered);
|
|
bRet = false;
|
|
}
|
|
else if (bVerbose)
|
|
{
|
|
printf("INFO: Spatial filter inclusion with GetArrowStream "
|
|
"seems to work.\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bRet && bVerbose)
|
|
printf("INFO: TestLayerGetArrowStream passed.\n");
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestOGRLayer() */
|
|
/************************************************************************/
|
|
|
|
static int TestOGRLayer(GDALDataset *poDS, OGRLayer *poLayer, int bIsSQLLayer)
|
|
|
|
{
|
|
int bRet = TRUE;
|
|
|
|
// Check that pszDomain == nullptr doesn't crash
|
|
poLayer->GetMetadata(nullptr);
|
|
poLayer->GetMetadataItem("", nullptr);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Verify that there is no spatial filter in place by default. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (LOG_ACTION(poLayer->GetSpatialFilter()) != nullptr)
|
|
{
|
|
printf("WARN: Spatial filter in place by default on layer %s.\n",
|
|
poLayer->GetName());
|
|
LOG_ACTION(poLayer->SetSpatialFilter(nullptr));
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Basic tests. */
|
|
/* -------------------------------------------------------------------- */
|
|
bRet &= TestBasic(poDS, poLayer);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test feature count accuracy. */
|
|
/* -------------------------------------------------------------------- */
|
|
bRet &= TestOGRLayerFeatureCount(poDS, poLayer, bIsSQLLayer);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test spatial filtering */
|
|
/* -------------------------------------------------------------------- */
|
|
bRet &= TestSpatialFilter(poLayer);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test attribute filtering */
|
|
/* -------------------------------------------------------------------- */
|
|
bRet &= TestAttributeFilter(poDS, poLayer);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test GetExtent() */
|
|
/* -------------------------------------------------------------------- */
|
|
bRet &= TestGetExtent(poLayer);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test GetArrowStream() interface */
|
|
/* -------------------------------------------------------------------- */
|
|
bRet &= TestLayerGetArrowStream(poLayer);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test random reading. */
|
|
/* -------------------------------------------------------------------- */
|
|
bRet &= TestOGRLayerRandomRead(poLayer);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test SetNextByIndex. */
|
|
/* -------------------------------------------------------------------- */
|
|
bRet &= TestOGRLayerSetNextByIndex(poLayer);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test delete feature. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (LOG_ACTION(poLayer->TestCapability(OLCDeleteFeature)))
|
|
{
|
|
bRet &= TestOGRLayerDeleteAndCreateFeature(poLayer);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test random writing. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (LOG_ACTION(poLayer->TestCapability(OLCRandomWrite)))
|
|
{
|
|
bRet &= TestOGRLayerRandomWrite(poLayer);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test OLCIgnoreFields. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (LOG_ACTION(poLayer->TestCapability(OLCIgnoreFields)))
|
|
{
|
|
bRet &= TestOGRLayerIgnoreFields(poLayer);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test UTF-8 reporting */
|
|
/* -------------------------------------------------------------------- */
|
|
bRet &= TestOGRLayerUTF8(poLayer);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test TestTransactions() */
|
|
/* -------------------------------------------------------------------- */
|
|
if (LOG_ACTION(poLayer->TestCapability(OLCSequentialWrite)))
|
|
{
|
|
bRet &= TestTransactions(poLayer);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test error conditions. */
|
|
/* -------------------------------------------------------------------- */
|
|
bRet &= TestLayerErrorConditions(poLayer);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Test some SQL. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (!bIsSQLLayer)
|
|
bRet &= TestLayerSQL(poDS, poLayer);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestInterleavedReading() */
|
|
/************************************************************************/
|
|
|
|
static int TestInterleavedReading(const char *pszDataSourceIn,
|
|
char **papszLayersIn)
|
|
{
|
|
int bRet = TRUE;
|
|
GDALDataset *poDS2 = nullptr;
|
|
OGRLayer *poLayer1 = nullptr;
|
|
OGRLayer *poLayer2 = nullptr;
|
|
OGRFeature *poFeature11_Ref = nullptr;
|
|
OGRFeature *poFeature12_Ref = nullptr;
|
|
OGRFeature *poFeature21_Ref = nullptr;
|
|
OGRFeature *poFeature22_Ref = nullptr;
|
|
OGRFeature *poFeature11 = nullptr;
|
|
OGRFeature *poFeature12 = nullptr;
|
|
OGRFeature *poFeature21 = nullptr;
|
|
OGRFeature *poFeature22 = nullptr;
|
|
|
|
/* Check that we have 2 layers with at least 2 features */
|
|
GDALDataset *poDS = LOG_ACTION(static_cast<GDALDataset *>(GDALOpenEx(
|
|
pszDataSourceIn, GDAL_OF_VECTOR, nullptr, papszOpenOptions, nullptr)));
|
|
if (poDS == nullptr)
|
|
{
|
|
if (bVerbose)
|
|
{
|
|
printf("INFO: Skipping TestInterleavedReading(). "
|
|
"Cannot reopen datasource\n");
|
|
}
|
|
goto bye;
|
|
}
|
|
|
|
poLayer1 = LOG_ACTION(papszLayersIn ? poDS->GetLayerByName(papszLayersIn[0])
|
|
: poDS->GetLayer(0));
|
|
poLayer2 = LOG_ACTION(papszLayersIn ? poDS->GetLayerByName(papszLayersIn[1])
|
|
: poDS->GetLayer(1));
|
|
if (poLayer1 == nullptr || poLayer2 == nullptr ||
|
|
LOG_ACTION(poLayer1->GetFeatureCount()) < 2 ||
|
|
LOG_ACTION(poLayer2->GetFeatureCount()) < 2)
|
|
{
|
|
if (bVerbose)
|
|
{
|
|
printf("INFO: Skipping TestInterleavedReading(). "
|
|
"Test conditions are not met\n");
|
|
}
|
|
goto bye;
|
|
}
|
|
|
|
/* Test normal reading */
|
|
LOG_ACTION(GDALClose(poDS));
|
|
poDS = LOG_ACTION(static_cast<GDALDataset *>(GDALOpenEx(
|
|
pszDataSourceIn, GDAL_OF_VECTOR, nullptr, papszOpenOptions, nullptr)));
|
|
poDS2 = LOG_ACTION(static_cast<GDALDataset *>(GDALOpenEx(
|
|
pszDataSourceIn, GDAL_OF_VECTOR, nullptr, papszOpenOptions, nullptr)));
|
|
if (poDS == nullptr || poDS2 == nullptr)
|
|
{
|
|
if (bVerbose)
|
|
{
|
|
printf("INFO: Skipping TestInterleavedReading(). "
|
|
"Cannot reopen datasource\n");
|
|
}
|
|
goto bye;
|
|
}
|
|
|
|
poLayer1 = LOG_ACTION(papszLayersIn ? poDS->GetLayerByName(papszLayersIn[0])
|
|
: poDS->GetLayer(0));
|
|
poLayer2 = LOG_ACTION(papszLayersIn ? poDS->GetLayerByName(papszLayersIn[1])
|
|
: poDS->GetLayer(1));
|
|
if (poLayer1 == nullptr || poLayer2 == nullptr)
|
|
{
|
|
printf("ERROR: Skipping TestInterleavedReading(). "
|
|
"Test conditions are not met\n");
|
|
bRet = FALSE;
|
|
goto bye;
|
|
}
|
|
|
|
poFeature11_Ref = LOG_ACTION(poLayer1->GetNextFeature());
|
|
poFeature12_Ref = LOG_ACTION(poLayer1->GetNextFeature());
|
|
poFeature21_Ref = LOG_ACTION(poLayer2->GetNextFeature());
|
|
poFeature22_Ref = LOG_ACTION(poLayer2->GetNextFeature());
|
|
if (poFeature11_Ref == nullptr || poFeature12_Ref == nullptr ||
|
|
poFeature21_Ref == nullptr || poFeature22_Ref == nullptr)
|
|
{
|
|
printf("ERROR: TestInterleavedReading() failed: poFeature11_Ref=%p, "
|
|
"poFeature12_Ref=%p, poFeature21_Ref=%p, poFeature22_Ref=%p\n",
|
|
poFeature11_Ref, poFeature12_Ref, poFeature21_Ref,
|
|
poFeature22_Ref);
|
|
bRet = FALSE;
|
|
goto bye;
|
|
}
|
|
|
|
/* Test interleaved reading */
|
|
poLayer1 =
|
|
LOG_ACTION(papszLayersIn ? poDS2->GetLayerByName(papszLayersIn[0])
|
|
: poDS2->GetLayer(0));
|
|
poLayer2 =
|
|
LOG_ACTION(papszLayersIn ? poDS2->GetLayerByName(papszLayersIn[1])
|
|
: poDS2->GetLayer(1));
|
|
if (poLayer1 == nullptr || poLayer2 == nullptr)
|
|
{
|
|
printf("ERROR: Skipping TestInterleavedReading(). "
|
|
"Test conditions are not met\n");
|
|
bRet = FALSE;
|
|
goto bye;
|
|
}
|
|
|
|
poFeature11 = LOG_ACTION(poLayer1->GetNextFeature());
|
|
poFeature21 = LOG_ACTION(poLayer2->GetNextFeature());
|
|
poFeature12 = LOG_ACTION(poLayer1->GetNextFeature());
|
|
poFeature22 = LOG_ACTION(poLayer2->GetNextFeature());
|
|
|
|
if (poFeature11 == nullptr || poFeature21 == nullptr ||
|
|
poFeature12 == nullptr || poFeature22 == nullptr)
|
|
{
|
|
printf("ERROR: TestInterleavedReading() failed: poFeature11=%p, "
|
|
"poFeature21=%p, poFeature12=%p, poFeature22=%p\n",
|
|
poFeature11, poFeature21, poFeature12, poFeature22);
|
|
bRet = FALSE;
|
|
goto bye;
|
|
}
|
|
|
|
if (poFeature12->Equal(poFeature11))
|
|
{
|
|
printf("WARN: TestInterleavedReading() failed: poFeature12 == "
|
|
"poFeature11. "
|
|
"The datasource resets the layer reading when interleaved "
|
|
"layer reading pattern is detected. Acceptable but could be "
|
|
"improved\n");
|
|
goto bye;
|
|
}
|
|
|
|
/* We cannot directly compare the feature as they don't share */
|
|
/* the same (pointer) layer definition, so just compare FIDs */
|
|
if (poFeature12_Ref->GetFID() != poFeature12->GetFID())
|
|
{
|
|
printf("ERROR: TestInterleavedReading() failed: "
|
|
"poFeature12_Ref != poFeature12\n");
|
|
poFeature12_Ref->DumpReadable(stdout, nullptr);
|
|
poFeature12->DumpReadable(stdout, nullptr);
|
|
bRet = FALSE;
|
|
goto bye;
|
|
}
|
|
|
|
if (bVerbose)
|
|
{
|
|
printf("INFO: TestInterleavedReading() successful.\n");
|
|
}
|
|
|
|
bye:
|
|
DestroyFeatureAndNullify(poFeature11_Ref);
|
|
DestroyFeatureAndNullify(poFeature12_Ref);
|
|
DestroyFeatureAndNullify(poFeature21_Ref);
|
|
DestroyFeatureAndNullify(poFeature22_Ref);
|
|
DestroyFeatureAndNullify(poFeature11);
|
|
DestroyFeatureAndNullify(poFeature21);
|
|
DestroyFeatureAndNullify(poFeature12);
|
|
DestroyFeatureAndNullify(poFeature22);
|
|
if (poDS != nullptr)
|
|
LOG_ACTION(GDALClose(poDS));
|
|
if (poDS2 != nullptr)
|
|
LOG_ACTION(GDALClose(poDS2));
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestDSErrorConditions() */
|
|
/************************************************************************/
|
|
|
|
static int TestDSErrorConditions(GDALDataset *poDS)
|
|
{
|
|
int bRet = TRUE;
|
|
OGRLayer *poLyr;
|
|
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
|
|
if (LOG_ACTION(poDS->TestCapability("fake_capability")))
|
|
{
|
|
printf("ERROR: TestCapability(\"fake_capability\") "
|
|
"should have returned FALSE\n");
|
|
bRet = FALSE;
|
|
goto bye;
|
|
}
|
|
|
|
if (LOG_ACTION(poDS->GetLayer(-1)) != nullptr)
|
|
{
|
|
printf("ERROR: GetLayer(-1) should have returned NULL\n");
|
|
bRet = FALSE;
|
|
goto bye;
|
|
}
|
|
|
|
if (LOG_ACTION(poDS->GetLayer(poDS->GetLayerCount())) != nullptr)
|
|
{
|
|
printf("ERROR: GetLayer(poDS->GetLayerCount()) should have "
|
|
"returned NULL\n");
|
|
bRet = FALSE;
|
|
goto bye;
|
|
}
|
|
|
|
if (LOG_ACTION(poDS->GetLayerByName("non_existing_layer")) != nullptr)
|
|
{
|
|
printf("ERROR: GetLayerByName(\"non_existing_layer\") should have "
|
|
"returned NULL\n");
|
|
bRet = FALSE;
|
|
goto bye;
|
|
}
|
|
|
|
poLyr =
|
|
LOG_ACTION(poDS->ExecuteSQL("a fake SQL command", nullptr, nullptr));
|
|
if (poLyr != nullptr)
|
|
{
|
|
LOG_ACTION(poDS->ReleaseResultSet(poLyr));
|
|
printf("ERROR: ExecuteSQL(\"a fake SQL command\") should have "
|
|
"returned NULL\n");
|
|
bRet = FALSE;
|
|
goto bye;
|
|
}
|
|
|
|
bye:
|
|
CPLPopErrorHandler();
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestVirtualIO() */
|
|
/************************************************************************/
|
|
|
|
static int TestVirtualIO(GDALDataset *poDS)
|
|
{
|
|
int bRet = TRUE;
|
|
|
|
if (STARTS_WITH(poDS->GetDescription(), "/vsimem/"))
|
|
return TRUE;
|
|
|
|
VSIStatBufL sStat;
|
|
if (!(VSIStatL(poDS->GetDescription(), &sStat) == 0))
|
|
return TRUE;
|
|
|
|
// Don't try with ODBC (will avoid a useless error message in ogr_odbc.py)
|
|
if (poDS->GetDriver() != nullptr &&
|
|
EQUAL(poDS->GetDriver()->GetDescription(), "ODBC"))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
const CPLStringList aosFileList(LOG_ACTION(poDS->GetFileList()));
|
|
CPLString osPath;
|
|
int bAllPathIdentical = TRUE;
|
|
for (const char *pszFilename : aosFileList)
|
|
{
|
|
if (pszFilename == aosFileList[0])
|
|
osPath = CPLGetPath(pszFilename);
|
|
else if (strcmp(osPath, CPLGetPath(pszFilename)) != 0)
|
|
{
|
|
bAllPathIdentical = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
CPLString osVirtPath;
|
|
if (bAllPathIdentical && aosFileList.size() > 1)
|
|
{
|
|
osVirtPath =
|
|
CPLFormFilename("/vsimem", CPLGetFilename(osPath), nullptr);
|
|
VSIMkdir(osVirtPath, 0666);
|
|
}
|
|
else
|
|
osVirtPath = "/vsimem";
|
|
|
|
for (const char *pszFilename : aosFileList)
|
|
{
|
|
const char *pszDestFile =
|
|
CPLFormFilename(osVirtPath, CPLGetFilename(pszFilename), nullptr);
|
|
/* CPLDebug("test_ogrsf", "Copying %s to %s", pszFilename, pszDestFile);
|
|
*/
|
|
CPLCopyFile(pszDestFile, pszFilename);
|
|
}
|
|
|
|
const char *pszVirtFile;
|
|
if (VSI_ISREG(sStat.st_mode))
|
|
pszVirtFile = CPLFormFilename(
|
|
osVirtPath, CPLGetFilename(poDS->GetDescription()), nullptr);
|
|
else
|
|
pszVirtFile = osVirtPath;
|
|
CPLDebug("test_ogrsf", "Trying to open %s", pszVirtFile);
|
|
GDALDataset *poDS2 = LOG_ACTION(static_cast<GDALDataset *>(
|
|
GDALOpenEx(pszVirtFile, GDAL_OF_VECTOR, nullptr, nullptr, nullptr)));
|
|
if (poDS2 != nullptr)
|
|
{
|
|
if (poDS->GetDriver()->GetMetadataItem(GDAL_DCAP_VIRTUALIO) == nullptr)
|
|
{
|
|
printf("WARNING: %s driver apparently supports VirtualIO "
|
|
"but does not declare it.\n",
|
|
poDS->GetDriver()->GetDescription());
|
|
}
|
|
if (poDS2->GetLayerCount() != poDS->GetLayerCount())
|
|
{
|
|
printf("WARNING: /vsimem dataset reports %d layers where as base "
|
|
"dataset reports %d layers.\n",
|
|
poDS2->GetLayerCount(), poDS->GetLayerCount());
|
|
}
|
|
GDALClose(poDS2);
|
|
|
|
if (bVerbose && bRet)
|
|
{
|
|
printf("INFO: TestVirtualIO successful.\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (poDS->GetDriver()->GetMetadataItem(GDAL_DCAP_VIRTUALIO) != nullptr)
|
|
{
|
|
printf("WARNING: %s driver declares supporting VirtualIO but "
|
|
"test with /vsimem does not work. It might be a sign that "
|
|
"GetFileList() is not properly implemented.\n",
|
|
poDS->GetDriver()->GetDescription());
|
|
}
|
|
}
|
|
|
|
for (const char *pszFilename : aosFileList)
|
|
{
|
|
VSIUnlink(
|
|
CPLFormFilename(osVirtPath, CPLGetFilename(pszFilename), nullptr));
|
|
}
|
|
|
|
return bRet;
|
|
}
|