/****************************************************************************** * $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 * Copyright (c) 2008-2014, Even Rouault * * 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 #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(;i1eNodeType != 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 ); }