ultimatepp/bazaar/plugin/gdal/ogr/ogrfeaturequery.cpp
cxl 23ff1e7e82 .gdal moved to bazaar
git-svn-id: svn://ultimatepp.org/upp/trunk@9273 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2015-12-07 13:36:24 +00:00

744 lines
25 KiB
C++

/******************************************************************************
* $Id: ogrfeaturequery.cpp 28968 2015-04-21 19:00:02Z rouault $
*
* Project: OpenGIS Simple Features Reference Implementation
* Purpose: Implementation of simple SQL WHERE style attributes queries
* for OGRFeatures.
* Author: Frank Warmerdam, warmerdam@pobox.com
*
******************************************************************************
* Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
* Copyright (c) 2008-2014, Even Rouault <even dot rouault at mines-paris dot org>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
****************************************************************************/
#include <assert.h>
#include "swq.h"
#include "ogr_feature.h"
#include "ogr_p.h"
#include "ogr_attrind.h"
CPL_CVSID("$Id: ogrfeaturequery.cpp 28968 2015-04-21 19:00:02Z rouault $");
/************************************************************************/
/* Support for special attributes (feature query and selection) */
/************************************************************************/
const char* SpecialFieldNames[SPECIAL_FIELD_COUNT]
= {"FID", "OGR_GEOMETRY", "OGR_STYLE", "OGR_GEOM_WKT", "OGR_GEOM_AREA"};
const swq_field_type SpecialFieldTypes[SPECIAL_FIELD_COUNT]
= {SWQ_INTEGER, SWQ_STRING, SWQ_STRING, SWQ_STRING, SWQ_FLOAT};
/************************************************************************/
/* OGRFeatureQuery() */
/************************************************************************/
OGRFeatureQuery::OGRFeatureQuery()
{
poTargetDefn = NULL;
pSWQExpr = NULL;
}
/************************************************************************/
/* ~OGRFeatureQuery() */
/************************************************************************/
OGRFeatureQuery::~OGRFeatureQuery()
{
delete (swq_expr_node *) pSWQExpr;
}
/************************************************************************/
/* Parse */
/************************************************************************/
OGRErr OGRFeatureQuery::Compile( OGRFeatureDefn *poDefn,
const char * pszExpression,
int bCheck,
swq_custom_func_registrar* poCustomFuncRegistrar )
{
/* -------------------------------------------------------------------- */
/* Clear any existing expression. */
/* -------------------------------------------------------------------- */
if( pSWQExpr != NULL )
{
delete (swq_expr_node *) pSWQExpr;
pSWQExpr = NULL;
}
/* -------------------------------------------------------------------- */
/* Build list of fields. */
/* -------------------------------------------------------------------- */
char **papszFieldNames;
swq_field_type *paeFieldTypes;
int iField;
int nFieldCount = poDefn->GetFieldCount() + SPECIAL_FIELD_COUNT +
poDefn->GetGeomFieldCount();
papszFieldNames = (char **)
CPLMalloc(sizeof(char *) * nFieldCount );
paeFieldTypes = (swq_field_type *)
CPLMalloc(sizeof(swq_field_type) * nFieldCount );
for( iField = 0; iField < poDefn->GetFieldCount(); iField++ )
{
OGRFieldDefn *poField = poDefn->GetFieldDefn( iField );
papszFieldNames[iField] = (char *) poField->GetNameRef();
switch( poField->GetType() )
{
case OFTInteger:
{
if( poField->GetSubType() == OFSTBoolean )
paeFieldTypes[iField] = SWQ_BOOLEAN;
else
paeFieldTypes[iField] = SWQ_INTEGER;
break;
}
case OFTInteger64:
{
if( poField->GetSubType() == OFSTBoolean )
paeFieldTypes[iField] = SWQ_BOOLEAN;
else
paeFieldTypes[iField] = SWQ_INTEGER64;
break;
}
case OFTReal:
paeFieldTypes[iField] = SWQ_FLOAT;
break;
case OFTString:
paeFieldTypes[iField] = SWQ_STRING;
break;
case OFTDate:
case OFTTime:
case OFTDateTime:
paeFieldTypes[iField] = SWQ_TIMESTAMP;
break;
default:
paeFieldTypes[iField] = SWQ_OTHER;
break;
}
}
iField = 0;
while (iField < SPECIAL_FIELD_COUNT)
{
papszFieldNames[poDefn->GetFieldCount() + iField] = (char *) SpecialFieldNames[iField];
paeFieldTypes[poDefn->GetFieldCount() + iField] = SpecialFieldTypes[iField];
++iField;
}
for( iField = 0; iField < poDefn->GetGeomFieldCount(); iField++ )
{
OGRGeomFieldDefn *poField = poDefn->GetGeomFieldDefn( iField );
int iDstField = poDefn->GetFieldCount() + SPECIAL_FIELD_COUNT + iField;
papszFieldNames[iDstField] = (char *) poField->GetNameRef();
if( *papszFieldNames[iDstField] == '\0' )
papszFieldNames[iDstField] = (char*) OGR_GEOMETRY_DEFAULT_NON_EMPTY_NAME;
paeFieldTypes[iDstField] = SWQ_GEOMETRY;
}
/* -------------------------------------------------------------------- */
/* Try to parse. */
/* -------------------------------------------------------------------- */
OGRErr eErr = OGRERR_NONE;
CPLErr eCPLErr;
poTargetDefn = poDefn;
eCPLErr = swq_expr_compile( pszExpression, nFieldCount,
papszFieldNames, paeFieldTypes,
bCheck,
poCustomFuncRegistrar,
(swq_expr_node **) &pSWQExpr );
if( eCPLErr != CE_None )
{
eErr = OGRERR_CORRUPT_DATA;
pSWQExpr = NULL;
}
CPLFree( papszFieldNames );
CPLFree( paeFieldTypes );
return eErr;
}
/************************************************************************/
/* OGRFeatureFetcher() */
/************************************************************************/
static swq_expr_node *OGRFeatureFetcher( swq_expr_node *op, void *pFeatureIn )
{
OGRFeature *poFeature = (OGRFeature *) pFeatureIn;
swq_expr_node *poRetNode = NULL;
if( op->field_type == SWQ_GEOMETRY )
{
int iField = op->field_index - (poFeature->GetFieldCount() + SPECIAL_FIELD_COUNT);
poRetNode = new swq_expr_node( poFeature->GetGeomFieldRef(iField) );
return poRetNode;
}
switch( op->field_type )
{
case SWQ_INTEGER:
case SWQ_BOOLEAN:
poRetNode = new swq_expr_node(
poFeature->GetFieldAsInteger(op->field_index) );
break;
case SWQ_INTEGER64:
poRetNode = new swq_expr_node(
poFeature->GetFieldAsInteger64(op->field_index) );
break;
case SWQ_FLOAT:
poRetNode = new swq_expr_node(
poFeature->GetFieldAsDouble(op->field_index) );
break;
default:
poRetNode = new swq_expr_node(
poFeature->GetFieldAsString(op->field_index) );
break;
}
poRetNode->is_null = !(poFeature->IsFieldSet(op->field_index));
return poRetNode;
}
/************************************************************************/
/* Evaluate() */
/************************************************************************/
int OGRFeatureQuery::Evaluate( OGRFeature *poFeature )
{
if( pSWQExpr == NULL )
return FALSE;
swq_expr_node *poResult;
poResult = ((swq_expr_node *) pSWQExpr)->Evaluate( OGRFeatureFetcher,
(void *) poFeature );
if( poResult == NULL )
return FALSE;
int bLogicalResult = FALSE;
if( poResult->field_type == SWQ_INTEGER ||
poResult->field_type == SWQ_INTEGER64 ||
poResult->field_type == SWQ_BOOLEAN )
bLogicalResult = (int)poResult->int_value;
delete poResult;
return bLogicalResult;
}
/************************************************************************/
/* CanUseIndex() */
/************************************************************************/
int OGRFeatureQuery::CanUseIndex( OGRLayer *poLayer )
{
swq_expr_node *psExpr = (swq_expr_node *) pSWQExpr;
/* -------------------------------------------------------------------- */
/* Do we have an index on the targetted layer? */
/* -------------------------------------------------------------------- */
if ( poLayer->GetIndex() == FALSE )
return FALSE;
return CanUseIndex( psExpr, poLayer );
}
int OGRFeatureQuery::CanUseIndex( swq_expr_node *psExpr,
OGRLayer *poLayer )
{
OGRAttrIndex *poIndex;
/* -------------------------------------------------------------------- */
/* Does the expression meet our requirements? */
/* -------------------------------------------------------------------- */
if( psExpr == NULL ||
psExpr->eNodeType != SNT_OPERATION )
return FALSE;
if ((psExpr->nOperation == SWQ_OR || psExpr->nOperation == SWQ_AND) &&
psExpr->nSubExprCount == 2)
{
return CanUseIndex( psExpr->papoSubExpr[0], poLayer ) &&
CanUseIndex( psExpr->papoSubExpr[1], poLayer );
}
if( !(psExpr->nOperation == SWQ_EQ || psExpr->nOperation == SWQ_IN)
|| psExpr->nSubExprCount < 2 )
return FALSE;
swq_expr_node *poColumn = psExpr->papoSubExpr[0];
swq_expr_node *poValue = psExpr->papoSubExpr[1];
if( poColumn->eNodeType != SNT_COLUMN
|| poValue->eNodeType != SNT_CONSTANT )
return FALSE;
poIndex = poLayer->GetIndex()->GetFieldIndex( poColumn->field_index );
if( poIndex == NULL )
return FALSE;
/* -------------------------------------------------------------------- */
/* OK, we have an index */
/* -------------------------------------------------------------------- */
return TRUE;
}
/************************************************************************/
/* EvaluateAgainstIndices() */
/* */
/* Attempt to return a list of FIDs matching the given */
/* attribute query conditions utilizing attribute indices. */
/* Returns NULL if the result cannot be computed from the */
/* available indices, or an "OGRNullFID" terminated list of */
/* FIDs if it can. */
/* */
/* For now we only support equality tests on a single indexed */
/* attribute field. Eventually we should make this support */
/* multi-part queries with ranges. */
/************************************************************************/
static int CompareGIntBig(const void *pa, const void *pb)
{
GIntBig a = *((const GIntBig*)pa);
GIntBig b = *((const GIntBig*)pb);
if( a < b )
return -1;
else if( a > b )
return 1;
else
return 0;
}
GIntBig *OGRFeatureQuery::EvaluateAgainstIndices( OGRLayer *poLayer,
OGRErr *peErr )
{
swq_expr_node *psExpr = (swq_expr_node *) pSWQExpr;
if( peErr != NULL )
*peErr = OGRERR_NONE;
/* -------------------------------------------------------------------- */
/* Do we have an index on the targetted layer? */
/* -------------------------------------------------------------------- */
if ( poLayer->GetIndex() == NULL )
return NULL;
GIntBig nFIDCount = 0;
return EvaluateAgainstIndices(psExpr, poLayer, nFIDCount);
}
/* The input arrays must be sorted ! */
static
GIntBig* OGRORGIntBigArray(GIntBig panFIDList1[], GIntBig nFIDCount1,
GIntBig panFIDList2[], GIntBig nFIDCount2,
GIntBig& nFIDCount)
{
GIntBig nMaxCount = nFIDCount1 + nFIDCount2;
GIntBig* panFIDList = (GIntBig*) CPLMalloc((size_t)(nMaxCount+1) * sizeof(GIntBig));
nFIDCount = 0;
GIntBig i1 = 0, i2 =0;
for(;i1<nFIDCount1 || i2<nFIDCount2;)
{
if (i1 < nFIDCount1 && i2 < nFIDCount2)
{
GIntBig nVal1 = panFIDList1[i1];
GIntBig nVal2 = panFIDList2[i2];
if (nVal1 < nVal2)
{
if (i1+1 < nFIDCount1 && panFIDList1[i1+1] <= nVal2)
{
panFIDList[nFIDCount ++] = nVal1;
i1 ++;
}
else
{
panFIDList[nFIDCount ++] = nVal1;
panFIDList[nFIDCount ++] = nVal2;
i1 ++;
i2 ++;
}
}
else if (nVal1 == nVal2)
{
panFIDList[nFIDCount ++] = nVal1;
i1 ++;
i2 ++;
}
else
{
if (i2+1 < nFIDCount2 && panFIDList2[i2+1] <= nVal1)
{
panFIDList[nFIDCount ++] = nVal2;
i2 ++;
}
else
{
panFIDList[nFIDCount ++] = nVal2;
panFIDList[nFIDCount ++] = nVal1;
i1 ++;
i2 ++;
}
}
}
else if (i1 < nFIDCount1)
{
GIntBig nVal1 = panFIDList1[i1];
panFIDList[nFIDCount ++] = nVal1;
i1 ++;
}
else if (i2 < nFIDCount2)
{
GIntBig nVal2 = panFIDList2[i2];
panFIDList[nFIDCount ++] = nVal2;
i2 ++;
}
}
panFIDList[nFIDCount] = OGRNullFID;
return panFIDList;
}
/* The input arrays must be sorted ! */
static
GIntBig* OGRANDGIntBigArray(GIntBig panFIDList1[], GIntBig nFIDCount1,
GIntBig panFIDList2[], GIntBig nFIDCount2,
GIntBig& nFIDCount)
{
GIntBig nMaxCount = MAX(nFIDCount1, nFIDCount2);
GIntBig* panFIDList = (GIntBig*) CPLMalloc((size_t)(nMaxCount+1) * sizeof(GIntBig));
nFIDCount = 0;
GIntBig i1 = 0, i2 =0;
for(;i1<nFIDCount1 && i2<nFIDCount2;)
{
GIntBig nVal1 = panFIDList1[i1];
GIntBig nVal2 = panFIDList2[i2];
if (nVal1 < nVal2)
{
if (i1+1 < nFIDCount1 && panFIDList1[i1+1] <= nVal2)
{
i1 ++;
}
else
{
i1 ++;
i2 ++;
}
}
else if (nVal1 == nVal2)
{
panFIDList[nFIDCount ++] = nVal1;
i1 ++;
i2 ++;
}
else
{
if (i2+1 < nFIDCount2 && panFIDList2[i2+1] <= nVal1)
{
i2 ++;
}
else
{
i1 ++;
i2 ++;
}
}
}
panFIDList[nFIDCount] = OGRNullFID;
return panFIDList;
}
GIntBig *OGRFeatureQuery::EvaluateAgainstIndices( swq_expr_node *psExpr,
OGRLayer *poLayer,
GIntBig& nFIDCount )
{
OGRAttrIndex *poIndex;
/* -------------------------------------------------------------------- */
/* Does the expression meet our requirements? */
/* -------------------------------------------------------------------- */
if( psExpr == NULL ||
psExpr->eNodeType != SNT_OPERATION )
return NULL;
if ((psExpr->nOperation == SWQ_OR || psExpr->nOperation == SWQ_AND) &&
psExpr->nSubExprCount == 2)
{
GIntBig nFIDCount1 = 0, nFIDCount2 = 0;
GIntBig* panFIDList1 = EvaluateAgainstIndices( psExpr->papoSubExpr[0], poLayer, nFIDCount1 );
GIntBig* panFIDList2 = panFIDList1 == NULL ? NULL :
EvaluateAgainstIndices( psExpr->papoSubExpr[1], poLayer, nFIDCount2 );
GIntBig* panFIDList = NULL;
if (panFIDList1 != NULL && panFIDList2 != NULL)
{
if (psExpr->nOperation == SWQ_OR )
panFIDList = OGRORGIntBigArray(panFIDList1, nFIDCount1,
panFIDList2, nFIDCount2, nFIDCount);
else if (psExpr->nOperation == SWQ_AND )
panFIDList = OGRANDGIntBigArray(panFIDList1, nFIDCount1,
panFIDList2, nFIDCount2, nFIDCount);
}
CPLFree(panFIDList1);
CPLFree(panFIDList2);
return panFIDList;
}
if( !(psExpr->nOperation == SWQ_EQ || psExpr->nOperation == SWQ_IN)
|| psExpr->nSubExprCount < 2 )
return NULL;
swq_expr_node *poColumn = psExpr->papoSubExpr[0];
swq_expr_node *poValue = psExpr->papoSubExpr[1];
if( poColumn->eNodeType != SNT_COLUMN
|| poValue->eNodeType != SNT_CONSTANT )
return NULL;
poIndex = poLayer->GetIndex()->GetFieldIndex( poColumn->field_index );
if( poIndex == NULL )
return NULL;
/* -------------------------------------------------------------------- */
/* OK, we have an index, now we need to query it. */
/* -------------------------------------------------------------------- */
OGRField sValue;
OGRFieldDefn *poFieldDefn;
poFieldDefn = poLayer->GetLayerDefn()->GetFieldDefn(poColumn->field_index);
/* -------------------------------------------------------------------- */
/* Handle the case of an IN operation. */
/* -------------------------------------------------------------------- */
if (psExpr->nOperation == SWQ_IN)
{
int nLength;
GIntBig *panFIDs = NULL;
int iIN;
for( iIN = 1; iIN < psExpr->nSubExprCount; iIN++ )
{
switch( poFieldDefn->GetType() )
{
case OFTInteger:
if (psExpr->papoSubExpr[iIN]->field_type == SWQ_FLOAT)
sValue.Integer = (int) psExpr->papoSubExpr[iIN]->float_value;
else
sValue.Integer = (int) psExpr->papoSubExpr[iIN]->int_value;
break;
case OFTInteger64:
if (psExpr->papoSubExpr[iIN]->field_type == SWQ_FLOAT)
sValue.Integer64 = (GIntBig) psExpr->papoSubExpr[iIN]->float_value;
else
sValue.Integer64 = psExpr->papoSubExpr[iIN]->int_value;
break;
case OFTReal:
sValue.Real = psExpr->papoSubExpr[iIN]->float_value;
break;
case OFTString:
sValue.String = psExpr->papoSubExpr[iIN]->string_value;
break;
default:
CPLAssert( FALSE );
return NULL;
}
int nFIDCount32 = 0;
panFIDs = poIndex->GetAllMatches( &sValue, panFIDs, &nFIDCount32, &nLength );
nFIDCount = nFIDCount32;
}
if (nFIDCount > 1)
{
/* the returned FIDs are expected to be in sorted order */
qsort(panFIDs, (size_t)nFIDCount, sizeof(GIntBig), CompareGIntBig);
}
return panFIDs;
}
/* -------------------------------------------------------------------- */
/* Handle equality test. */
/* -------------------------------------------------------------------- */
switch( poFieldDefn->GetType() )
{
case OFTInteger:
if (poValue->field_type == SWQ_FLOAT)
sValue.Integer = (int) poValue->float_value;
else
sValue.Integer = (int) poValue->int_value;
break;
case OFTInteger64:
if (poValue->field_type == SWQ_FLOAT)
sValue.Integer64 = (GIntBig) poValue->float_value;
else
sValue.Integer64 = poValue->int_value;
break;
case OFTReal:
sValue.Real = poValue->float_value;
break;
case OFTString:
sValue.String = poValue->string_value;
break;
default:
CPLAssert( FALSE );
return NULL;
}
int nLength = 0;
int nFIDCount32 = 0;
GIntBig* panFIDs = poIndex->GetAllMatches( &sValue, NULL, &nFIDCount32, &nLength );
nFIDCount = nFIDCount32;
if (nFIDCount > 1)
{
/* the returned FIDs are expected to be in sorted order */
qsort(panFIDs, (size_t)nFIDCount, sizeof(GIntBig), CompareGIntBig);
}
return panFIDs;
}
/************************************************************************/
/* OGRFieldCollector() */
/* */
/* Helper function for recursing through tree to satisfy */
/* GetUsedFields(). */
/************************************************************************/
char **OGRFeatureQuery::FieldCollector( void *pBareOp,
char **papszList )
{
swq_expr_node *op = (swq_expr_node *) pBareOp;
/* -------------------------------------------------------------------- */
/* References to tables other than the primarily are currently */
/* unsupported. Error out. */
/* -------------------------------------------------------------------- */
if( op->eNodeType == SNT_COLUMN )
{
if( op->table_index != 0 )
{
CSLDestroy( papszList );
return NULL;
}
/* -------------------------------------------------------------------- */
/* Add the field name into our list if it is not already there. */
/* -------------------------------------------------------------------- */
const char *pszFieldName;
if( op->field_index >= poTargetDefn->GetFieldCount()
&& op->field_index < poTargetDefn->GetFieldCount() + SPECIAL_FIELD_COUNT)
pszFieldName = SpecialFieldNames[op->field_index - poTargetDefn->GetFieldCount()];
else if( op->field_index >= 0
&& op->field_index < poTargetDefn->GetFieldCount() )
pszFieldName =
poTargetDefn->GetFieldDefn(op->field_index)->GetNameRef();
else
{
CSLDestroy( papszList );
return NULL;
}
if( CSLFindString( papszList, pszFieldName ) == -1 )
papszList = CSLAddString( papszList, pszFieldName );
}
/* -------------------------------------------------------------------- */
/* Add in fields from subexpressions. */
/* -------------------------------------------------------------------- */
if( op->eNodeType == SNT_OPERATION )
{
for( int iSubExpr = 0; iSubExpr < op->nSubExprCount; iSubExpr++ )
{
papszList = FieldCollector( op->papoSubExpr[iSubExpr], papszList );
}
}
return papszList;
}
/************************************************************************/
/* GetUsedFields() */
/************************************************************************/
/**
* Returns lists of fields in expression.
*
* All attribute fields are used in the expression of this feature
* query are returned as a StringList of field names. This function would
* primarily be used within drivers to recognise special case conditions
* depending only on attribute fields that can be very efficiently
* fetched.
*
* NOTE: If any fields in the expression are from tables other than the
* primary table then NULL is returned indicating an error. In succesful
* use, no non-empty expression should return an empty list.
*
* @return list of field names. Free list with CSLDestroy() when no longer
* required.
*/
char **OGRFeatureQuery::GetUsedFields( )
{
if( pSWQExpr == NULL )
return NULL;
return FieldCollector( pSWQExpr, NULL );
}