diff --git a/mapmssql2008.c b/mapmssql2008.c index f93c0e5dc7..cea2026f47 100644 --- a/mapmssql2008.c +++ b/mapmssql2008.c @@ -205,6 +205,7 @@ typedef struct ms_MSSQL2008_layer_info_t { msODBCconn * conn; /* Connection to db */ msGeometryParserInfo gpi; /* struct for the geometry parser */ int geometry_format; /* Geometry format to be retrieved from the database */ + tokenListNodeObjPtr current_node; /* filter expression translation */ } msMSSQL2008LayerInfo; #define SQL_COLUMN_NAME_MAX_LENGTH 128 @@ -809,6 +810,28 @@ int msMSSQL2008LayerInitItemInfo(layerObj *layer) return MS_SUCCESS; } +/* Get the layer extent as specified in the mapfile or a largest area */ +/* covering all features */ +int msMSSQL2008LayerGetExtent(layerObj *layer, rectObj *extent) +{ + if(layer->debug) { + msDebug("msMSSQL2008LayerGetExtent called\n"); + } + + if (layer->extent.minx == -1.0 && layer->extent.miny == -1.0 && + layer->extent.maxx == -1.0 && layer->extent.maxy == -1.0) { + extent->minx = extent->miny = -1.0 * FLT_MAX; + extent->maxx = extent->maxy = FLT_MAX; + } else { + extent->minx = layer->extent.minx; + extent->miny = layer->extent.miny; + extent->maxx = layer->extent.maxx; + extent->maxy = layer->extent.maxy; + } + + return MS_SUCCESS; +} + /* Prepare and execute the SQL statement for this layer */ static int prepare_database(layerObj *layer, rectObj rect, char **query_string) { @@ -959,13 +982,19 @@ static int prepare_database(layerObj *layer, rectObj rect, char **query_string) msMSSQL2008LayerGetExtent(layer, &extent); if (rect.minx <= extent.minx && rect.miny <= extent.miny && rect.maxx >= extent.maxx && rect.maxy >= extent.maxy) { /* no spatial filter used */ - if(msLayerGetProcessingKey(layer, "NATIVE_FILTER") == NULL) { + if ( layer->filter.native_string ) { + snprintf(query_string_temp, sizeof(query_string_temp), "SELECT %s from %s WHERE (%s)", columns_wanted, data_source, layer->filter.native_string ); + } + else if(msLayerGetProcessingKey(layer, "NATIVE_FILTER") == NULL) { snprintf(query_string_temp, sizeof(query_string_temp), "SELECT %s from %s", columns_wanted, data_source ); } else { snprintf(query_string_temp, sizeof(query_string_temp), "SELECT %s from %s WHERE (%s)", columns_wanted, data_source, msLayerGetProcessingKey(layer, "NATIVE_FILTER")); } } else { - if(msLayerGetProcessingKey(layer, "NATIVE_FILTER") == NULL) { + if ( layer->filter.native_string ) { + snprintf(query_string_temp, sizeof(query_string_temp), "SELECT %s from %s WHERE (%s) and %s.STIntersects(%s) = 1 ", columns_wanted, data_source, layer->filter.native_string, layerinfo->geom_column, box3d ); + } + else if(msLayerGetProcessingKey(layer, "NATIVE_FILTER") == NULL) { snprintf(query_string_temp, sizeof(query_string_temp), "SELECT %s from %s WHERE %s.STIntersects(%s) = 1 ", columns_wanted, data_source, layerinfo->geom_column, box3d ); } else { snprintf(query_string_temp, sizeof(query_string_temp), "SELECT %s from %s WHERE (%s) and %s.STIntersects(%s) = 1 ", columns_wanted, data_source, msLayerGetProcessingKey(layer, "NATIVE_FILTER"), layerinfo->geom_column, box3d ); @@ -1951,28 +1980,6 @@ int msMSSQL2008LayerGetItems(layerObj *layer) return msMSSQL2008LayerInitItemInfo(layer); } -/* Get the layer extent as specified in the mapfile or a largest area */ -/* covering all features */ -int msMSSQL2008LayerGetExtent(layerObj *layer, rectObj *extent) -{ - if(layer->debug) { - msDebug("msMSSQL2008LayerGetExtent called\n"); - } - - if (layer->extent.minx == -1.0 && layer->extent.miny == -1.0 && - layer->extent.maxx == -1.0 && layer->extent.maxy == -1.0) { - extent->minx = extent->miny = -1.0 * FLT_MAX; - extent->maxx = extent->maxy = FLT_MAX; - } else { - extent->minx = layer->extent.minx; - extent->miny = layer->extent.miny; - extent->maxx = layer->extent.maxx; - extent->maxy = layer->extent.maxy; - } - - return MS_SUCCESS; -} - /* Get primary key column of table */ int msMSSQL2008LayerRetrievePK(layerObj *layer, char **urid_name, char* table_name, int debug) { @@ -2192,6 +2199,399 @@ static int msMSSQL2008LayerParseData(layerObj *layer, char **geom_column_name, c return MS_SUCCESS; } +char *msMSSQL2008LayerEscapePropertyName(layerObj *layer, const char* pszString) +{ + char* pszEscapedStr=NULL; + int i, j = 0; + + if (pszString && strlen(pszString) > 0) { + int nLength = strlen(pszString); + + pszEscapedStr = (char*) msSmallMalloc( 1 + nLength + 1 + 1); + pszEscapedStr[j++] = '['; + + for (i=0; ilayerinfo; + + if (!layerinfo->current_node) + { + msSetError(MS_MISCERR, "Unecpected end of expression", "msMSSQL2008LayerTranslateFilter()"); + return 0; + } + + switch(layerinfo->current_node->token) { + case '(': + filter->native_string = msStringConcatenate(filter->native_string, "("); + /* process subexpression */ + layerinfo->current_node = layerinfo->current_node->next; + while (layerinfo->current_node != NULL) { + if (!process_node(layer, filter)) + return 0; + if (!layerinfo->current_node) + break; + if (layerinfo->current_node->token == ')') + break; + layerinfo->current_node = layerinfo->current_node->next; + } + break; + case ')': + filter->native_string = msStringConcatenate(filter->native_string, ")"); + break; + /* literal tokens */ + case MS_TOKEN_LITERAL_BOOLEAN: + case MS_TOKEN_LITERAL_NUMBER: + strtmpl = "%lf"; + snippet = (char *) msSmallMalloc(strlen(strtmpl) + 16); + sprintf(snippet, strtmpl, layerinfo->current_node->tokenval.dblval); + filter->native_string = msStringConcatenate(filter->native_string, snippet); + msFree(snippet); + break; + case MS_TOKEN_LITERAL_STRING: + strtmpl = "'%s'"; + stresc = msMSSQL2008LayerEscapeSQLParam(layer, layerinfo->current_node->tokenval.strval); + snippet = (char *) msSmallMalloc(strlen(strtmpl) + strlen(stresc)); + sprintf(snippet, strtmpl, stresc); + filter->native_string = msStringConcatenate(filter->native_string, snippet); + msFree(snippet); + msFree(stresc); + break; + case MS_TOKEN_LITERAL_TIME: + { + char buffer[30]; + if(strftime(buffer,30,"'%Y-%m-%d %H:%M:%S'",&layerinfo->current_node->tokenval.tmval) == 0) { + msSetError(MS_MISCERR, "Invalid time literal in expression", "msMSSQL2008LayerTranslateFilter()"); + return 0; + } + filter->native_string = msStringConcatenate(filter->native_string, buffer); + } + break; + case MS_TOKEN_LITERAL_SHAPE: + if (strcasecmp(layerinfo->geom_column_type, "geography") == 0) + filter->native_string = msStringConcatenate(filter->native_string, "geography::STGeomFromText('"); + else + filter->native_string = msStringConcatenate(filter->native_string, "geometry::STGeomFromText('"); + filter->native_string = msStringConcatenate(filter->native_string, msShapeToWKT(layerinfo->current_node->tokenval.shpval)); + filter->native_string = msStringConcatenate(filter->native_string, "'"); + if(layerinfo->user_srid && strcmp(layerinfo->user_srid, "") != 0) { + filter->native_string = msStringConcatenate(filter->native_string, ", "); + filter->native_string = msStringConcatenate(filter->native_string, layerinfo->user_srid); + } + filter->native_string = msStringConcatenate(filter->native_string, ")"); + break; + case MS_TOKEN_BINDING_TIME: + case MS_TOKEN_BINDING_DOUBLE: + case MS_TOKEN_BINDING_INTEGER: + case MS_TOKEN_BINDING_STRING: + strtmpl = "%s"; + stresc = msMSSQL2008LayerEscapePropertyName(layer, layerinfo->current_node->tokenval.bindval.item); + snippet = (char *) msSmallMalloc(strlen(strtmpl) + strlen(stresc)); + sprintf(snippet, strtmpl, stresc); + filter->native_string = msStringConcatenate(filter->native_string, snippet); + msFree(snippet); + msFree(stresc); + break; + case MS_TOKEN_BINDING_SHAPE: + filter->native_string = msStringConcatenate(filter->native_string, layerinfo->geom_column); + break; + case MS_TOKEN_BINDING_MAP_CELLSIZE: + strtmpl = "%lf"; + snippet = (char *) msSmallMalloc(strlen(strtmpl) + 16); + sprintf(snippet, strtmpl, layer->map->cellsize); + filter->native_string = msStringConcatenate(filter->native_string, snippet); + msFree(snippet); + break; + + /* comparisons */ + case MS_TOKEN_COMPARISON_IN: + filter->native_string = msStringConcatenate(filter->native_string, " IN "); + break; + case MS_TOKEN_COMPARISON_LIKE: + filter->native_string = msStringConcatenate(filter->native_string, " LIKE "); + break; + case MS_TOKEN_COMPARISON_EQ: + filter->native_string = msStringConcatenate(filter->native_string, " = "); + break; + case MS_TOKEN_COMPARISON_NE: + filter->native_string = msStringConcatenate(filter->native_string, " != "); + break; + case MS_TOKEN_COMPARISON_GT: + filter->native_string = msStringConcatenate(filter->native_string, " > "); + break; + case MS_TOKEN_COMPARISON_GE: + filter->native_string = msStringConcatenate(filter->native_string, " >= "); + break; + case MS_TOKEN_COMPARISON_LT: + filter->native_string = msStringConcatenate(filter->native_string, " < "); + break; + case MS_TOKEN_COMPARISON_LE: + filter->native_string = msStringConcatenate(filter->native_string, " <= "); + break; + + /* logical ops */ + case MS_TOKEN_LOGICAL_AND: + filter->native_string = msStringConcatenate(filter->native_string, " AND "); + break; + case MS_TOKEN_LOGICAL_OR: + filter->native_string = msStringConcatenate(filter->native_string, " OR "); + break; + case MS_TOKEN_LOGICAL_NOT: + filter->native_string = msStringConcatenate(filter->native_string, " NOT "); + break; + + /* spatial comparison tokens */ + case MS_TOKEN_COMPARISON_INTERSECTS: + filter->native_string = msStringConcatenate(filter->native_string, ".STIntersects("); + layerinfo->current_node = layerinfo->current_node->next; + if (!process_node(layer, filter)) + return 0; + filter->native_string = msStringConcatenate(filter->native_string, ")=1"); + break; + + case MS_TOKEN_COMPARISON_DISJOINT: + filter->native_string = msStringConcatenate(filter->native_string, ".STDisjoint("); + layerinfo->current_node = layerinfo->current_node->next; + if (!process_node(layer, filter)) + return 0; + filter->native_string = msStringConcatenate(filter->native_string, ")=1"); + break; + + case MS_TOKEN_COMPARISON_TOUCHES: + filter->native_string = msStringConcatenate(filter->native_string, ".STTouches("); + layerinfo->current_node = layerinfo->current_node->next; + if (!process_node(layer, filter)) + return 0; + filter->native_string = msStringConcatenate(filter->native_string, ")=1"); + break; + + case MS_TOKEN_COMPARISON_OVERLAPS: + filter->native_string = msStringConcatenate(filter->native_string, ".STOverlaps("); + layerinfo->current_node = layerinfo->current_node->next; + if (!process_node(layer, filter)) + return 0; + filter->native_string = msStringConcatenate(filter->native_string, ")=1"); + break; + + case MS_TOKEN_COMPARISON_CROSSES: + filter->native_string = msStringConcatenate(filter->native_string, ".STCrosses("); + layerinfo->current_node = layerinfo->current_node->next; + if (!process_node(layer, filter)) + return 0; + filter->native_string = msStringConcatenate(filter->native_string, ")=1"); + break; + + case MS_TOKEN_COMPARISON_WITHIN: + filter->native_string = msStringConcatenate(filter->native_string, ".STWithin("); + layerinfo->current_node = layerinfo->current_node->next; + if (!process_node(layer, filter)) + return 0; + filter->native_string = msStringConcatenate(filter->native_string, ")=1"); + break; + + case MS_TOKEN_COMPARISON_CONTAINS: + filter->native_string = msStringConcatenate(filter->native_string, ".STContains("); + layerinfo->current_node = layerinfo->current_node->next; + if (!process_node(layer, filter)) + return 0; + filter->native_string = msStringConcatenate(filter->native_string, ")=1"); + break; + + case MS_TOKEN_COMPARISON_EQUALS: + filter->native_string = msStringConcatenate(filter->native_string, ".STEquals("); + layerinfo->current_node = layerinfo->current_node->next; + if (!process_node(layer, filter)) + return 0; + filter->native_string = msStringConcatenate(filter->native_string, ")=1"); + break; + + case MS_TOKEN_COMPARISON_BEYOND: + msSetError(MS_MISCERR, "Beyond operator is unsupported.", "msMSSQL2008LayerTranslateFilter()"); + return 0; + + case MS_TOKEN_COMPARISON_DWITHIN: + msSetError(MS_MISCERR, "DWithin operator is unsupported.", "msMSSQL2008LayerTranslateFilter()"); + return 0; + + /* spatial functions */ + case MS_TOKEN_FUNCTION_AREA: + filter->native_string = msStringConcatenate(filter->native_string, ".STArea("); + layerinfo->current_node = layerinfo->current_node->next; + if (!process_node(layer, filter)) + return 0; + filter->native_string = msStringConcatenate(filter->native_string, ")"); + break; + + case MS_TOKEN_FUNCTION_BUFFER: + filter->native_string = msStringConcatenate(filter->native_string, ".STBuffer("); + layerinfo->current_node = layerinfo->current_node->next; + if (!process_node(layer, filter)) + return 0; + filter->native_string = msStringConcatenate(filter->native_string, ")"); + break; + + case MS_TOKEN_FUNCTION_DIFFERENCE: + filter->native_string = msStringConcatenate(filter->native_string, ".STDifference("); + layerinfo->current_node = layerinfo->current_node->next; + if (!process_node(layer, filter)) + return 0; + filter->native_string = msStringConcatenate(filter->native_string, ")"); + break; + + case MS_TOKEN_FUNCTION_FROMTEXT: + if (strcasecmp(layerinfo->geom_column_type, "geography") == 0) + filter->native_string = msStringConcatenate(filter->native_string, "geography::STGeomFromText"); + else + filter->native_string = msStringConcatenate(filter->native_string, "geometry::STGeomFromText"); + break; + + case MS_TOKEN_FUNCTION_LENGTH: + filter->native_string = msStringConcatenate(filter->native_string, "len"); + break; + + case MS_TOKEN_FUNCTION_ROUND: + filter->native_string = msStringConcatenate(filter->native_string, "round"); + break; + + case MS_TOKEN_FUNCTION_TOSTRING: + break; + + case MS_TOKEN_FUNCTION_COMMIFY: + break; + + case MS_TOKEN_FUNCTION_SIMPLIFY: + filter->native_string = msStringConcatenate(filter->native_string, ".Reduce"); + break; + case MS_TOKEN_FUNCTION_SIMPLIFYPT: + break; + case MS_TOKEN_FUNCTION_GENERALIZE: + filter->native_string = msStringConcatenate(filter->native_string, ".Reduce"); + break; + + default: + msSetError(MS_MISCERR, "Translation to native SQL failed.","msMSSQL2008LayerTranslateFilter()"); + if (layer->debug) { + msDebug("Token not caught, exiting: Token is %i\n", layerinfo->current_node->token); + } + return 0; + } + return 1; +} + +/* Translate filter expression to native msssql filter */ +int msMSSQL2008LayerTranslateFilter(layerObj *layer, expressionObj *filter, char *filteritem) +{ + char *snippet = NULL; + char *strtmpl = NULL; + char *stresc = NULL; + msMSSQL2008LayerInfo *layerinfo = (msMSSQL2008LayerInfo *) layer->layerinfo; + + if(!filter->string) return MS_SUCCESS; + + msFree(filter->native_string); + filter->native_string = NULL; + + if(filter->type == MS_STRING && filter->string && filteritem) { /* item/value pair */ + stresc = msMSSQL2008LayerEscapePropertyName(layer, filteritem); + if(filter->flags & MS_EXP_INSENSITIVE) { + filter->native_string = msStringConcatenate(filter->native_string, "upper("); + filter->native_string = msStringConcatenate(filter->native_string, stresc); + filter->native_string = msStringConcatenate(filter->native_string, ") = upper("); + } else { + filter->native_string = msStringConcatenate(filter->native_string, stresc); + filter->native_string = msStringConcatenate(filter->native_string, " = "); + } + msFree(stresc); + + strtmpl = "'%s'"; /* don't have a type for the righthand literal so assume it's a string and we quote */ + snippet = (char *) msSmallMalloc(strlen(strtmpl) + strlen(filter->string)); + sprintf(snippet, strtmpl, filter->string); // TODO: escape filter->string + filter->native_string = msStringConcatenate(filter->native_string, snippet); + free(snippet); + + if(filter->flags & MS_EXP_INSENSITIVE) + filter->native_string = msStringConcatenate(filter->native_string, ")"); + + } else if(filter->type == MS_REGEX && filter->string && filteritem) { /* item/regex pair */ + /* NOTE: regex is not supported by MSSQL natively. We should install it in CLR UDF + according to https://msdn.microsoft.com/en-us/magazine/cc163473.aspx*/ + filter->native_string = msStringConcatenate(filter->native_string, "dbo.RegexMatch("); + if(filter->flags & MS_EXP_INSENSITIVE) { + filter->native_string = msStringConcatenate(filter->native_string, "'(?i)"); + } + filter->native_string = msStrdup(filteritem); + filter->native_string = msStringConcatenate(filter->native_string, ", "); + strtmpl = "'%s'"; + snippet = (char *) msSmallMalloc(strlen(strtmpl) + strlen(filter->string)); + sprintf(snippet, strtmpl, filter->string); // TODO: escape filter->string + filter->native_string = msStringConcatenate(filter->native_string, snippet); + + filter->native_string = msStringConcatenate(filter->native_string, "')"); + free(snippet); + } else if(filter->type == MS_EXPRESSION) { + + if(layer->debug >= 2) + msDebug("msMSSQL2008LayerTranslateFilter. String: %s.\n", filter->string); + + if(!filter->tokens) { + if(layer->debug >= 2) + msDebug("msMSSQL2008LayerTranslateFilter. There are tokens to process... \n"); + return MS_SUCCESS; + } + + /* start processing nodes */ + layerinfo->current_node = filter->tokens; + while (layerinfo->current_node != NULL) { + if (!process_node(layer, filter)) + { + msFree(filter->native_string); + filter->native_string = 0; + return MS_FAILURE; + } + + if (!layerinfo->current_node) + break; + + layerinfo->current_node = layerinfo->current_node->next; + } + } + + return MS_SUCCESS; +} + #else /* prototypes if MSSQL2008 isnt supposed to be compiled */ @@ -2261,6 +2661,24 @@ int msMSSQL2008LayerGetItems(layerObj *layer) return MS_FAILURE; } +int msMSSQL2008LayerTranslateFilter(layerObj *layer, expressionObj *filter, char *filteritem) +{ + msSetError(MS_QUERYERR, "msMSSQL2008LayerTranslateFilter called but unimplemented!(mapserver not compiled with MSSQL2008 support)", "msMSSQL2008LayerTranslateFilter()"); + return MS_FAILURE; +} + +char *msMSSQL2008LayerEscapeSQLParam(layerObj *layer, const char *pszString) +{ + msSetError(MS_QUERYERR, "msMSSQL2008EscapeSQLParam called but unimplemented!(mapserver not compiled with MSSQL2008 support)", "msMSSQL2008LayerEscapeSQLParam()"); + return NULL; +} + +char *msMSSQL2008LayerEscapePropertyName(layerObj *layer, const char *pszString) +{ + msSetError(MS_QUERYERR, "msMSSQL2008LayerEscapePropertyName called but unimplemented!(mapserver not compiled with MSSQL2008 support)", "msMSSQL2008LayerEscapePropertyName()"); + return NULL; +} + /* end above's #ifdef USE_MSSQL2008 */ #endif @@ -2271,7 +2689,9 @@ MS_DLL_EXPORT int PluginInitializeVirtualTable(layerVTableObj* vtable, layerObj assert(layer != NULL); assert(vtable != NULL); - /* vtable->LayerTranslateFilter, use default - need to add this... */ + vtable->LayerTranslateFilter = msMSSQL2008LayerTranslateFilter; + vtable->LayerEscapeSQLParam = msMSSQL2008LayerEscapeSQLParam; + vtable->LayerEscapePropertyName = msMSSQL2008LayerEscapePropertyName; vtable->LayerInitItemInfo = msMSSQL2008LayerInitItemInfo; vtable->LayerFreeItemInfo = msMSSQL2008LayerFreeItemInfo; @@ -2291,7 +2711,7 @@ MS_DLL_EXPORT int PluginInitializeVirtualTable(layerVTableObj* vtable, layerObj /* vtable->LayerGetAutoStyle, not supported for this layer */ vtable->LayerCloseConnection = msMSSQL2008LayerClose; - vtable->LayerSetTimeFilter = msLayerMakePlainTimeFilter; + vtable->LayerSetTimeFilter = msLayerMakeBackticsTimeFilter; /* vtable->LayerCreateItems, use default */ /* vtable->LayerGetNumFeatures, use default */ /* layer->vtable->LayerGetAutoProjection, use defaut*/ @@ -2307,6 +2727,10 @@ msMSSQL2008LayerInitializeVirtualTable(layerObj *layer) assert(layer != NULL); assert(layer->vtable != NULL); + layer->vtable->LayerTranslateFilter = msMSSQL2008LayerTranslateFilter; + layer->vtable->LayerEscapeSQLParam = msMSSQL2008LayerEscapeSQLParam; + layer->vtable->LayerEscapePropertyName = msMSSQL2008LayerEscapePropertyName; + layer->vtable->LayerInitItemInfo = msMSSQL2008LayerInitItemInfo; layer->vtable->LayerFreeItemInfo = msMSSQL2008LayerFreeItemInfo; layer->vtable->LayerOpen = msMSSQL2008LayerOpen; @@ -2325,7 +2749,7 @@ msMSSQL2008LayerInitializeVirtualTable(layerObj *layer) /* layer->vtable->LayerGetAutoStyle, not supported for this layer */ layer->vtable->LayerCloseConnection = msMSSQL2008LayerClose; - layer->vtable->LayerSetTimeFilter = msLayerMakePlainTimeFilter; + layer->vtable->LayerSetTimeFilter = msLayerMakeBackticsTimeFilter; /* layer->vtable->LayerCreateItems, use default */ /* layer->vtable->LayerGetNumFeatures, use default */ diff --git a/mappluginlayer.c b/mappluginlayer.c index 8b01508d7c..0099e9b393 100644 --- a/mappluginlayer.c +++ b/mappluginlayer.c @@ -154,6 +154,8 @@ static void copyVirtualTable(layerVTableObj *dest, const layerVTableObj *src) { + dest->LayerTranslateFilter = src->LayerTranslateFilter ? src->LayerTranslateFilter : dest->LayerTranslateFilter; + dest->LayerSupportsCommonFilters = src->LayerSupportsCommonFilters ? src->LayerSupportsCommonFilters : dest->LayerSupportsCommonFilters; dest->LayerInitItemInfo = src->LayerInitItemInfo ? src->LayerInitItemInfo : dest->LayerInitItemInfo; dest->LayerFreeItemInfo = src->LayerFreeItemInfo ? src->LayerFreeItemInfo : dest->LayerFreeItemInfo; dest->LayerOpen = src->LayerOpen ? src->LayerOpen : dest->LayerOpen; @@ -172,6 +174,11 @@ copyVirtualTable(layerVTableObj *dest, dest->LayerCreateItems = src->LayerCreateItems ? src->LayerCreateItems : dest->LayerCreateItems; dest->LayerGetNumFeatures = src->LayerGetNumFeatures ? src->LayerGetNumFeatures : dest->LayerGetNumFeatures; dest->LayerGetAutoProjection = src->LayerGetAutoProjection ? src->LayerGetAutoProjection: dest->LayerGetAutoProjection; + dest->LayerEscapeSQLParam = src->LayerEscapeSQLParam ? src->LayerEscapeSQLParam: dest->LayerEscapeSQLParam; + dest->LayerEscapePropertyName = src->LayerEscapePropertyName ? src->LayerEscapePropertyName: dest->LayerEscapePropertyName; + dest->LayerEscapeSQLParam = src->LayerEscapeSQLParam ? src->LayerEscapeSQLParam: dest->LayerEscapeSQLParam; + dest->LayerEnablePaging = src->LayerEnablePaging ? src->LayerEnablePaging: dest->LayerEnablePaging; + dest->LayerGetPaging = src->LayerGetPaging ? src->LayerGetPaging: dest->LayerGetPaging; } int diff --git a/maprasterquery.c b/maprasterquery.c index a420e0b3d8..80accfa206 100644 --- a/maprasterquery.c +++ b/maprasterquery.c @@ -1286,12 +1286,14 @@ int msRASTERLayerGetItems(layerObj *layer) #ifndef USE_GDAL return MS_FAILURE; #else + int maxnumitems = 0; rasterLayerInfo *rlinfo = (rasterLayerInfo *) layer->layerinfo; if( rlinfo == NULL ) return MS_FAILURE; - layer->items = (char **) msSmallCalloc(sizeof(char *),10); + maxnumitems = 8 + (rlinfo->qc_values?rlinfo->band_count:0); + layer->items = (char **) msSmallCalloc(sizeof(char *),maxnumitems); layer->numitems = 0; if( rlinfo->qc_x_reproj ) @@ -1318,6 +1320,8 @@ int msRASTERLayerGetItems(layerObj *layer) if( rlinfo->qc_count ) layer->items[layer->numitems++] = msStrdup("count"); + assert(layer->numitems <= maxnumitems); + return msRASTERLayerInitItemInfo(layer); #endif /* def USE_GDAL */ } diff --git a/maprendering.c b/maprendering.c index f7c5c50540..2a249850c4 100644 --- a/maprendering.c +++ b/maprendering.c @@ -247,8 +247,8 @@ imageObj *getTile(imageObj *img, symbolObj *symbol, symbolStyleObj *s, int widt if(UNLIKELY(!face)) { status = MS_FAILURE; break; } msUTF8ToUniChar(symbol->character, &unicode); unicode = msGetGlyphIndex(face,unicode); - glyphc = msGetGlyphByIndex(face, s->scale, unicode); - if(UNLIKELY(!face)) { status = MS_FAILURE; break; } + glyphc = msGetGlyphByIndex(face, MS_MAX(MS_NINT(s->scale),1), unicode); + if(UNLIKELY(!glyphc)) { status = MS_FAILURE; break; } status = drawGlyphMarker(tileimg, face, glyphc, p_x, p_y, s->scale, s->rotation, s->color, s->outlinecolor, s->outlinewidth); } @@ -311,7 +311,7 @@ imageObj *getTile(imageObj *img, symbolObj *symbol, symbolStyleObj *s, int widt if(UNLIKELY(!face)) { status = MS_FAILURE; break; } msUTF8ToUniChar(symbol->character, &unicode); unicode = msGetGlyphIndex(face,unicode); - glyphc = msGetGlyphByIndex(face, s->scale, unicode); + glyphc = msGetGlyphByIndex(face, MS_MAX(MS_NINT(s->scale),1), unicode); if(UNLIKELY(!glyphc)) { status = MS_FAILURE; break; } status = drawGlyphMarker(tileimg, face, glyphc, p_x, p_y, s->scale, s->rotation, s->color, s->outlinecolor, s->outlinewidth); @@ -384,7 +384,7 @@ int msImagePolylineMarkers(imageObj *image, shapeObj *p, symbolObj *symbol, face = msGetFontFace(symbol->font, &image->map->fontset); if(UNLIKELY(!face)) return MS_FAILURE; unicode = msGetGlyphIndex(face,unicode); - glyphc = msGetGlyphByIndex(face, style->scale, unicode); + glyphc = msGetGlyphByIndex(face, MS_MAX(MS_NINT(style->scale),1), unicode); if(UNLIKELY(!glyphc)) return MS_FAILURE; symbol_width = glyphc->metrics.maxx - glyphc->metrics.minx; symbol_height = glyphc->metrics.maxy - glyphc->metrics.miny; @@ -922,7 +922,7 @@ int msDrawMarkerSymbol(mapObj *map, imageObj *image, pointObj *p, styleObj *styl if(UNLIKELY(!face)) return MS_FAILURE; msUTF8ToUniChar(symbol->character,&unicode); unicode = msGetGlyphIndex(face,unicode); - glyphc = msGetGlyphByIndex(face,s.scale,unicode); + glyphc = msGetGlyphByIndex(face, MS_MAX(MS_NINT(s.scale),1), unicode); if(UNLIKELY(!glyphc)) return MS_FAILURE; ret = drawGlyphMarker(image, face, glyphc, p_x, p_y, s.scale, s.rotation, s.color, s.outlinecolor, s.outlinewidth); } diff --git a/mapwcs20.c b/mapwcs20.c index 69997cf648..95351b8efa 100644 --- a/mapwcs20.c +++ b/mapwcs20.c @@ -3707,8 +3707,8 @@ static int msWCSGetCoverage20_FinalizeParamsObj(wcs20ParamsObjPtr params, wcs20A params->subsetcrs = msStrdup(crs); } } else if (!params->subsetcrs) { - /* default to imageCRS */ - params->subsetcrs = msStrdup("imageCRS"); + /* default to CRS of image */ + /* leave params->subsetcrs to null */ } return MS_SUCCESS; @@ -4126,7 +4126,7 @@ this request. Check wcs/ows_enable_request settings.", "msWCSGetCoverage20()", p msWCSClearCoverageMetadata20(&cm); msSetError(MS_WCSERR, "Error loading CRS %s.", - "msWCSGetCoverage20()", params->subsetcrs); + "msWCSGetCoverage20()", cm.srs); return msWCSException(map, "InvalidParameterValue", "projection", params->version); } @@ -4165,6 +4165,12 @@ this request. Check wcs/ows_enable_request settings.", "msWCSGetCoverage20()", p subsets = params->bbox; + /* if no subsetCRS was specified use the coverages CRS + (Requirement 27 of the WCS 2.0 specification) */ + if (!params->subsetcrs) { + params->subsetcrs = msStrdup(cm.srs); + } + if(EQUAL(params->subsetcrs, "imageCRS")) { /* subsets are in imageCRS; reproject them to real coordinates */ rectObj orig_bbox = subsets; diff --git a/mapwfslayer.c b/mapwfslayer.c index 6eca05a2d6..fe6560ee10 100644 --- a/mapwfslayer.c +++ b/mapwfslayer.c @@ -658,7 +658,6 @@ int msPrepareWFSLayerRequest(int nLayerId, mapObj *map, layerObj *lp, pasReqInfo, *numRequests, map, "FO") != MS_SUCCESS) { if (psParams) { msWFSFreeParamsObj(psParams); - free(psParams); } return MS_FAILURE; } @@ -690,7 +689,6 @@ int msPrepareWFSLayerRequest(int nLayerId, mapObj *map, layerObj *lp, if (psParams) { msWFSFreeParamsObj(psParams); - free(psParams); } return nStatus; diff --git a/mapwms.c b/mapwms.c index 7418bbfedb..17a3a09cf3 100644 --- a/mapwms.c +++ b/mapwms.c @@ -326,9 +326,15 @@ int msWMSApplyTime(mapObj *map, int version, char *time, char *wms_exception_for */ void msWMSPrepareNestedGroups(mapObj* map, int nVersion, char*** nestedGroups, int* numNestedGroups, int* isUsedInNestedGroup) { - int i, j, k; + int i, k; const char* groups; char* errorMsg; + //Create array to hold unique groups + int maxgroups = 2000; + int maxgroupiter = 1; + char** uniqgroups = malloc(maxgroups * sizeof(char*)); + int uniqgroupcount = 0; + for (i = 0; i < map->numlayers; i++) { nestedGroups[i] = NULL; /* default */ @@ -351,24 +357,42 @@ void msWMSPrepareNestedGroups(mapObj* map, int nVersion, char*** nestedGroups, i } else { /* split into subgroups. Start at address + 1 because the first '/' would cause an extra empty group */ nestedGroups[i] = msStringSplit(groups + 1, '/', &numNestedGroups[i]); - /* */ - for (j = 0; j < map->numlayers; j++) { - if (isUsedInNestedGroup[j]) - continue; - - for (k=0; kname && strcasecmp(GET_LAYER(map, j)->name, nestedGroups[i][k]) == 0 ) { - isUsedInNestedGroup[j] = 1; + /* Iterate through the groups and add them to the unique groups array */ + for (k=0; knumlayers; i++) { + for (k=0; kname ,uniqgroups[k]) == 0 ){ + isUsedInNestedGroup[i] = 1; + break; } - } - } + } } } + /* ** Validate that a given dimension is inside the extents defined */ diff --git a/msautotest b/msautotest index d7a53c04aa..289dc11236 160000 --- a/msautotest +++ b/msautotest @@ -1 +1 @@ -Subproject commit d7a53c04aab2c61a965f30fafcfd6e3d6fc84570 +Subproject commit 289dc11236438ece46bfb058f9161a36fe328c6b