Ticket #210: postgis-nullunion-v3.patch

File postgis-nullunion-v3.patch, 10.8 KB (added by mcayland, 3 years ago)
  • postgis/lwgeom_geos.c

     
    107107        GEOSGeometry * geos_result=NULL; 
    108108        int SRID=-1; 
    109109        size_t offset = 0; 
     110        bits8 *bitmap; 
     111        int bitmask; 
    110112#if POSTGIS_DEBUG_LEVEL > 0 
    111113        static int call=1; 
    112114#endif 
    113115#if POSTGIS_GEOS_VERSION >= 31 
     116        int gotsrid = 0; 
    114117        int allpolys=1; 
    115118#endif 
    116119 
     
    128131 
    129132        nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array)); 
    130133 
     134        bitmap = ARR_NULLBITMAP(array); 
     135 
    131136        POSTGIS_DEBUGF(3, "unite_garray: number of elements: %d", nelems); 
    132137 
    133138        if ( nelems == 0 ) PG_RETURN_NULL(); 
    134139 
    135140        /* 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        } 
    137149 
    138150        /* Ok, we really need geos now ;) */ 
    139151        initGEOS(lwnotice, lwnotice); 
     
    145157        ** If they are, we can use UnionCascaded for faster results. 
    146158        */ 
    147159        offset = 0; 
     160        bitmask = 1; 
     161        gotsrid = 0; 
    148162        for ( i = 0; i < nelems; i++ ) 
    149163        { 
    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) 
    154166                { 
    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                        } 
    157186                } 
    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                        } 
    162197                } 
    163198        } 
    164199 
     
    176211                ** First make an array of GEOS Polygons. 
    177212                */ 
    178213                offset = 0; 
     214                bitmap = ARR_NULLBITMAP(array); 
     215                bitmask = 1; 
    179216                for ( i = 0; i < nelems; i++ ) 
    180217                { 
    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) 
    185220                        { 
    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 ) 
    187225                                { 
    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; 
    202226                                        if ( curgeom == geoms_size ) 
    203227                                        { 
    204228                                                geoms_size *= 2; 
    205229                                                geoms = repalloc( geoms, sizeof(GEOSGeom) * geoms_size ); 
    206230                                        } 
    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++ ) 
    212239                                        { 
    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++; 
    214257                                        } 
    215                                         lwpoly_release(lwpoly); 
    216                                         curgeom++; 
    217258                                } 
    218259                        } 
    219260 
     261                        /* Advance NULL bitmap */ 
     262                        if (bitmap) 
     263                        { 
     264                                bitmask <<= 1; 
     265                                if (bitmask == 0x100) 
     266                                { 
     267                                        bitmap++; 
     268                                        bitmask = 1; 
     269                                } 
     270                        } 
    220271                } 
    221272                /* 
    222273                ** Take our GEOS Polygons and turn them into a GEOS MultiPolygon, 
    223274                ** then pass that into cascaded union. 
    224275                */ 
    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                } 
    232291        } 
    233292        else 
    234293        { 
     
    237296                ** Heterogeneous result, let's slog through this one union at a time. 
    238297                */ 
    239298                offset = 0; 
     299                bitmap = ARR_NULLBITMAP(array); 
     300                bitmask = 1; 
    240301                for (i=0; i<nelems; i++) 
    241302                { 
    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) 
    254305                        { 
    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                                } 
    259343                        } 
    260                         else 
    261                         { 
    262                                 errorIfSRIDMismatch(SRID, pglwgeom_getSRID(geom)); 
    263                         } 
    264344 
    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) 
    272347                        { 
    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                                } 
    276354                        } 
    277                         GEOSGeom_destroy((GEOSGeometry *)g1); 
    278                         GEOSGeom_destroy((GEOSGeometry *)geos_result); 
    279                         geos_result = g2; 
     355                         
    280356                } 
    281357 
    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                } 
    285370 
    286371#if POSTGIS_GEOS_VERSION >= 31 
    287372        } 
     
    289374 
    290375        if ( result == NULL ) 
    291376        { 
    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(); 
    294379        } 
    295380 
    296381        PG_RETURN_POINTER(result); 
  • postgis/lwgeom_accum.c

     
    2121#include "lwgeom_pg.h" 
    2222 
    2323/* Local prototypes */ 
     24Datum PGISDirectFunctionCall1(PGFunction func, Datum arg1); 
    2425Datum pgis_geometry_accum_transfn(PG_FUNCTION_ARGS); 
    2526Datum pgis_geometry_accum_finalfn(PG_FUNCTION_ARGS); 
    2627Datum pgis_geometry_union_finalfn(PG_FUNCTION_ARGS); 
     
    210211        p = (pgis_abs*) PG_GETARG_POINTER(0); 
    211212 
    212213        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(); 
    214217 
    215218        PG_RETURN_DATUM(result); 
    216219} 
     
    284287        PG_RETURN_DATUM(result); 
    285288} 
    286289 
     290/** 
     291* A modified version of PostgreSQL's DirectFunctionCall1 which allows NULL results; this 
     292* is required for aggregates that return NULL. 
     293*/ 
     294Datum 
     295PGISDirectFunctionCall1(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}