Ticket #210: postgis-nullunion-v3.patch
| File postgis-nullunion-v3.patch, 10.8 KB (added by mcayland, 3 years ago) |
|---|
-
postgis/lwgeom_geos.c
107 107 GEOSGeometry * geos_result=NULL; 108 108 int SRID=-1; 109 109 size_t offset = 0; 110 bits8 *bitmap; 111 int bitmask; 110 112 #if POSTGIS_DEBUG_LEVEL > 0 111 113 static int call=1; 112 114 #endif 113 115 #if POSTGIS_GEOS_VERSION >= 31 116 int gotsrid = 0; 114 117 int allpolys=1; 115 118 #endif 116 119 … … 128 131 129 132 nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array)); 130 133 134 bitmap = ARR_NULLBITMAP(array); 135 131 136 POSTGIS_DEBUGF(3, "unite_garray: number of elements: %d", nelems); 132 137 133 138 if ( nelems == 0 ) PG_RETURN_NULL(); 134 139 135 140 /* One-element union is the element itself */ 136 if ( nelems == 1 ) PG_RETURN_POINTER((PG_LWGEOM *)(ARR_DATA_PTR(array))); 141 if ( nelems == 1 ) 142 { 143 /* If the element is a NULL then we need to handle it separately */ 144 if (bitmap && (*bitmap & 1) == 0) 145 PG_RETURN_NULL(); 146 else 147 PG_RETURN_POINTER((PG_LWGEOM *)(ARR_DATA_PTR(array))); 148 } 137 149 138 150 /* Ok, we really need geos now ;) */ 139 151 initGEOS(lwnotice, lwnotice); … … 145 157 ** If they are, we can use UnionCascaded for faster results. 146 158 */ 147 159 offset = 0; 160 bitmask = 1; 161 gotsrid = 0; 148 162 for ( i = 0; i < nelems; i++ ) 149 163 { 150 PG_LWGEOM *pggeom = (PG_LWGEOM *)(ARR_DATA_PTR(array)+offset); 151 int pgtype = TYPE_GETTYPE(pggeom->type); 152 offset += INTALIGN(VARSIZE(pggeom)); 153 if ( ! i ) /* Initialize SRID */ 164 /* Don't do anything for NULL values */ 165 if ((bitmap && (*bitmap & bitmask) != 0) || !bitmap) 154 166 { 155 SRID = pglwgeom_getSRID(pggeom); 156 if ( TYPE_HASZ(pggeom->type) ) is3d = 1; 167 PG_LWGEOM *pggeom = (PG_LWGEOM *)(ARR_DATA_PTR(array)+offset); 168 int pgtype = TYPE_GETTYPE(pggeom->type); 169 offset += INTALIGN(VARSIZE(pggeom)); 170 if ( ! gotsrid ) /* Initialize SRID */ 171 { 172 SRID = pglwgeom_getSRID(pggeom); 173 gotsrid = 1; 174 if ( TYPE_HASZ(pggeom->type) ) is3d = 1; 175 } 176 else 177 { 178 errorIfSRIDMismatch(SRID, pglwgeom_getSRID(pggeom)); 179 } 180 181 if ( pgtype != POLYGONTYPE && pgtype != MULTIPOLYGONTYPE ) 182 { 183 allpolys = 0; 184 break; 185 } 157 186 } 158 if ( pgtype != POLYGONTYPE && pgtype != MULTIPOLYGONTYPE ) 159 { 160 allpolys = 0; 161 break; 187 188 /* Advance NULL bitmap */ 189 if (bitmap) 190 { 191 bitmask <<= 1; 192 if (bitmask == 0x100) 193 { 194 bitmap++; 195 bitmask = 1; 196 } 162 197 } 163 198 } 164 199 … … 176 211 ** First make an array of GEOS Polygons. 177 212 */ 178 213 offset = 0; 214 bitmap = ARR_NULLBITMAP(array); 215 bitmask = 1; 179 216 for ( i = 0; i < nelems; i++ ) 180 217 { 181 PG_LWGEOM *pggeom = (PG_LWGEOM *)(ARR_DATA_PTR(array)+offset); 182 int pgtype = TYPE_GETTYPE(pggeom->type); 183 offset += INTALIGN(VARSIZE(pggeom)); 184 if ( pgtype == POLYGONTYPE ) 218 /* Don't do anything for NULL values */ 219 if ((bitmap && (*bitmap & bitmask) != 0) || !bitmap) 185 220 { 186 if ( curgeom == geoms_size ) 221 PG_LWGEOM *pggeom = (PG_LWGEOM *)(ARR_DATA_PTR(array)+offset); 222 int pgtype = TYPE_GETTYPE(pggeom->type); 223 offset += INTALIGN(VARSIZE(pggeom)); 224 if ( pgtype == POLYGONTYPE ) 187 225 { 188 geoms_size *= 2;189 geoms = repalloc( geoms, sizeof(GEOSGeom) * geoms_size );190 }191 geoms[curgeom] = (GEOSGeometry *)POSTGIS2GEOS(pggeom);192 curgeom++;193 }194 if ( pgtype == MULTIPOLYGONTYPE )195 {196 int j = 0;197 LWGEOM_INSPECTED *lwgeom = lwgeom_inspect(SERIALIZED_FORM(pggeom));198 for ( j = 0; j < lwgeom->ngeometries; j++ )199 {200 LWPOLY *lwpoly = NULL;201 int k = 0;202 226 if ( curgeom == geoms_size ) 203 227 { 204 228 geoms_size *= 2; 205 229 geoms = repalloc( geoms, sizeof(GEOSGeom) * geoms_size ); 206 230 } 207 /* This builds a LWPOLY on top of the serialized form */ 208 lwpoly = lwgeom_getpoly_inspected(lwgeom, j); 209 geoms[curgeom] = LWGEOM2GEOS(lwpoly_as_lwgeom(lwpoly)); 210 /* We delicately free the LWPOLY and POINTARRAY structs, leaving the serialized form below untouched. */ 211 for ( k = 0; k < lwpoly->nrings; k++ ) 231 geoms[curgeom] = (GEOSGeometry *)POSTGIS2GEOS(pggeom); 232 curgeom++; 233 } 234 if ( pgtype == MULTIPOLYGONTYPE ) 235 { 236 int j = 0; 237 LWGEOM_INSPECTED *lwgeom = lwgeom_inspect(SERIALIZED_FORM(pggeom)); 238 for ( j = 0; j < lwgeom->ngeometries; j++ ) 212 239 { 213 lwfree(lwpoly->rings[k]); 240 LWPOLY *lwpoly = NULL; 241 int k = 0; 242 if ( curgeom == geoms_size ) 243 { 244 geoms_size *= 2; 245 geoms = repalloc( geoms, sizeof(GEOSGeom) * geoms_size ); 246 } 247 /* This builds a LWPOLY on top of the serialized form */ 248 lwpoly = lwgeom_getpoly_inspected(lwgeom, j); 249 geoms[curgeom] = LWGEOM2GEOS(lwpoly_as_lwgeom(lwpoly)); 250 /* We delicately free the LWPOLY and POINTARRAY structs, leaving the serialized form below untouched. */ 251 for ( k = 0; k < lwpoly->nrings; k++ ) 252 { 253 lwfree(lwpoly->rings[k]); 254 } 255 lwpoly_release(lwpoly); 256 curgeom++; 214 257 } 215 lwpoly_release(lwpoly);216 curgeom++;217 258 } 218 259 } 219 260 261 /* Advance NULL bitmap */ 262 if (bitmap) 263 { 264 bitmask <<= 1; 265 if (bitmask == 0x100) 266 { 267 bitmap++; 268 bitmask = 1; 269 } 270 } 220 271 } 221 272 /* 222 273 ** Take our GEOS Polygons and turn them into a GEOS MultiPolygon, 223 274 ** then pass that into cascaded union. 224 275 */ 225 g1 = GEOSGeom_createCollection(GEOS_MULTIPOLYGON, geoms, curgeom); 226 if ( g1 ) g2 = GEOSUnionCascaded(g1); 227 if ( g2 ) GEOSSetSRID(g2, SRID); 228 if ( g2 ) result = GEOS2POSTGIS(g2, is3d); 229 /* Clean up the mess. */ 230 if ( g1 ) GEOSGeom_destroy((GEOSGeometry *)g1); 231 if ( g2 ) GEOSGeom_destroy(g2); 276 if (curgeom > 0) 277 { 278 g1 = GEOSGeom_createCollection(GEOS_MULTIPOLYGON, geoms, curgeom); 279 if ( g1 ) g2 = GEOSUnionCascaded(g1); 280 if ( g2 ) GEOSSetSRID(g2, SRID); 281 if ( g2 ) result = GEOS2POSTGIS(g2, is3d); 282 /* Clean up the mess. */ 283 if ( g1 ) GEOSGeom_destroy((GEOSGeometry *)g1); 284 if ( g2 ) GEOSGeom_destroy(g2); 285 } 286 else 287 { 288 /* All we found were NULLs, so let's return NULL */ 289 PG_RETURN_NULL(); 290 } 232 291 } 233 292 else 234 293 { … … 237 296 ** Heterogeneous result, let's slog through this one union at a time. 238 297 */ 239 298 offset = 0; 299 bitmap = ARR_NULLBITMAP(array); 300 bitmask = 1; 240 301 for (i=0; i<nelems; i++) 241 302 { 242 PG_LWGEOM *geom = (PG_LWGEOM *)(ARR_DATA_PTR(array)+offset); 243 offset += INTALIGN(VARSIZE(geom)); 244 245 pgis_geom = geom; 246 247 POSTGIS_DEBUGF(3, "geom %d @ %p", i, geom); 248 249 /* Check is3d flag */ 250 if ( TYPE_HASZ(geom->type) ) is3d = 1; 251 252 /* Check SRID homogeneity and initialize geos result */ 253 if ( ! i ) 303 /* Don't do anything for NULL values */ 304 if ((bitmap && (*bitmap & bitmask) != 0) || !bitmap) 254 305 { 255 geos_result = (GEOSGeometry *)POSTGIS2GEOS(geom); 256 SRID = pglwgeom_getSRID(geom); 257 POSTGIS_DEBUGF(3, "first geom is a %s", lwgeom_typename(TYPE_GETTYPE(geom->type))); 258 continue; 306 PG_LWGEOM *geom = (PG_LWGEOM *)(ARR_DATA_PTR(array)+offset); 307 offset += INTALIGN(VARSIZE(geom)); 308 309 pgis_geom = geom; 310 311 POSTGIS_DEBUGF(3, "geom %d @ %p", i, geom); 312 313 /* Check is3d flag */ 314 if ( TYPE_HASZ(geom->type) ) is3d = 1; 315 316 /* Check SRID homogeneity and initialize geos result */ 317 if ( ! geos_result ) 318 { 319 geos_result = (GEOSGeometry *)POSTGIS2GEOS(geom); 320 SRID = pglwgeom_getSRID(geom); 321 POSTGIS_DEBUGF(3, "first geom is a %s", lwgeom_typename(TYPE_GETTYPE(geom->type))); 322 } 323 else 324 { 325 errorIfSRIDMismatch(SRID, pglwgeom_getSRID(geom)); 326 327 g1 = POSTGIS2GEOS(pgis_geom); 328 329 POSTGIS_DEBUGF(3, "unite_garray(%d): adding geom %d to union (%s)", 330 call, i, lwgeom_typename(TYPE_GETTYPE(geom->type))); 331 332 g2 = GEOSUnion(g1, geos_result); 333 if ( g2 == NULL ) 334 { 335 GEOSGeom_destroy((GEOSGeometry *)g1); 336 GEOSGeom_destroy((GEOSGeometry *)geos_result); 337 elog(ERROR,"GEOS union() threw an error!"); 338 } 339 GEOSGeom_destroy((GEOSGeometry *)g1); 340 GEOSGeom_destroy((GEOSGeometry *)geos_result); 341 geos_result = g2; 342 } 259 343 } 260 else261 {262 errorIfSRIDMismatch(SRID, pglwgeom_getSRID(geom));263 }264 344 265 g1 = POSTGIS2GEOS(pgis_geom); 266 267 POSTGIS_DEBUGF(3, "unite_garray(%d): adding geom %d to union (%s)", 268 call, i, lwgeom_typename(TYPE_GETTYPE(geom->type))); 269 270 g2 = GEOSUnion(g1, geos_result); 271 if ( g2 == NULL ) 345 /* Advance NULL bitmap */ 346 if (bitmap) 272 347 { 273 GEOSGeom_destroy((GEOSGeometry *)g1); 274 GEOSGeom_destroy((GEOSGeometry *)geos_result); 275 elog(ERROR,"GEOS union() threw an error!"); 348 bitmask <<= 1; 349 if (bitmask == 0x100) 350 { 351 bitmap++; 352 bitmask = 1; 353 } 276 354 } 277 GEOSGeom_destroy((GEOSGeometry *)g1); 278 GEOSGeom_destroy((GEOSGeometry *)geos_result); 279 geos_result = g2; 355 280 356 } 281 357 282 GEOSSetSRID(geos_result, SRID); 283 result = GEOS2POSTGIS(geos_result, is3d); 284 GEOSGeom_destroy(geos_result); 358 /* If geos_result is set then we found at least one non-NULL geometry */ 359 if (geos_result) 360 { 361 GEOSSetSRID(geos_result, SRID); 362 result = GEOS2POSTGIS(geos_result, is3d); 363 GEOSGeom_destroy(geos_result); 364 } 365 else 366 { 367 /* All we found were NULLs, so let's return NULL */ 368 PG_RETURN_NULL(); 369 } 285 370 286 371 #if POSTGIS_GEOS_VERSION >= 31 287 372 } … … 289 374 290 375 if ( result == NULL ) 291 376 { 292 elog(ERROR, "Union returned a NULL geometry.");293 PG_RETURN_NULL(); /* never get here */377 /* Union returned a NULL geometry */ 378 PG_RETURN_NULL(); 294 379 } 295 380 296 381 PG_RETURN_POINTER(result); -
postgis/lwgeom_accum.c
21 21 #include "lwgeom_pg.h" 22 22 23 23 /* Local prototypes */ 24 Datum PGISDirectFunctionCall1(PGFunction func, Datum arg1); 24 25 Datum pgis_geometry_accum_transfn(PG_FUNCTION_ARGS); 25 26 Datum pgis_geometry_accum_finalfn(PG_FUNCTION_ARGS); 26 27 Datum pgis_geometry_union_finalfn(PG_FUNCTION_ARGS); … … 210 211 p = (pgis_abs*) PG_GETARG_POINTER(0); 211 212 212 213 geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo); 213 result = DirectFunctionCall1( pgis_union_geometry_array, geometry_array ); 214 result = PGISDirectFunctionCall1( pgis_union_geometry_array, geometry_array ); 215 if (!result) 216 PG_RETURN_NULL(); 214 217 215 218 PG_RETURN_DATUM(result); 216 219 } … … 284 287 PG_RETURN_DATUM(result); 285 288 } 286 289 290 /** 291 * A modified version of PostgreSQL's DirectFunctionCall1 which allows NULL results; this 292 * is required for aggregates that return NULL. 293 */ 294 Datum 295 PGISDirectFunctionCall1(PGFunction func, Datum arg1) 296 { 297 FunctionCallInfoData fcinfo; 298 Datum result; 299 300 InitFunctionCallInfoData(fcinfo, NULL, 1, NULL, NULL); 301 302 fcinfo.arg[0] = arg1; 303 fcinfo.argnull[0] = false; 304 305 result = (*func) (&fcinfo); 306 307 /* Check for null result, returning a "NULL" Datum if indicated */ 308 if (fcinfo.isnull) 309 return (Datum) 0; 310 311 return result; 312 }
