diff --git a/postgis/brin_2d.c b/postgis/brin_2d.c index 4ba7f5ba239..edfde4296bd 100644 --- a/postgis/brin_2d.c +++ b/postgis/brin_2d.c @@ -11,14 +11,23 @@ * FunctionCallInvoke machinery for each heap tuple. */ +static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, +uint16 procnum); + +static bool isempty(Datum the_datum); + PG_FUNCTION_INFO_V1(geom2d_brin_inclusion_add_value); Datum geom2d_brin_inclusion_add_value(PG_FUNCTION_ARGS) { + BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - Datum newval = PG_GETARG_DATUM(2); - bool isnull = PG_GETARG_BOOL(3); - BOX2DF box_geom, *box_key; + Datum newval = PG_GETARG_DATUM(2); + bool isnull = PG_GETARG_BOOL(3); + Oid colloid = PG_GET_COLLATION(); + FmgrInfo *finfo; + AttrNumber attno; + BOX2DF box_geom, *box_key; /* * If the new value is null, we record that we saw it if it's the first @@ -33,8 +42,32 @@ geom2d_brin_inclusion_add_value(PG_FUNCTION_ARGS) PG_RETURN_BOOL(true); } - if (gserialized_datum_get_box2df_p(newval, &box_geom) == LW_FAILURE) - elog(ERROR, "Error while extracting the box2df from the geom"); + + /* + * If the opclass supports the concept of empty values, test the passed + * new value for emptiness; if it returns true, we need to set the + * "contains empty" flag in the element (unless already set). + */ + if (gserialized_datum_get_box2df_p(newval, &box_geom) == LW_FAILURE) { + if (isempty(newval)) { + attno = column->bv_attno; + + finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_EMPTY); + if (finfo != NULL && DatumGetBool(FunctionCall1Coll(finfo, colloid, newval))) + { + if (!DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY])) + { + column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true); + PG_RETURN_BOOL(true); + } + + PG_RETURN_BOOL(false); + } + } else { + /* check other cases where it is not possible to retrieve a box*/ + elog(ERROR, "Error while extracting the box2df from the geom"); + } + } /* if the recorded value is null, we just need to store the box2df */ if (column->bv_allnulls) @@ -61,3 +94,58 @@ geom2d_brin_inclusion_add_value(PG_FUNCTION_ARGS) PG_RETURN_BOOL(false); } + +/* + * Cache and return inclusion opclass support procedure + * + * Return the procedure corresponding to the given function support number + * or null if it is not exists. + */ +static FmgrInfo * +inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) +{ + InclusionOpaque *opaque; + uint16 basenum = procnum - PROCNUM_BASE; + + /* + * We cache these in the opaque struct, to avoid repetitive syscache + * lookups. + */ + opaque = (InclusionOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; + + /* + * If we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + if (opaque->extra_procinfos[basenum].fn_oid == InvalidOid) + { + if (RegProcedureIsValid(index_getprocid(bdesc->bd_index, attno, + procnum))) + { + fmgr_info_copy(&opaque->extra_procinfos[basenum], + index_getprocinfo(bdesc->bd_index, attno, procnum), + bdesc->bd_context); + } + else + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + return &opaque->extra_procinfos[basenum]; +} + +static bool +isempty(Datum the_datum) +{ + GSERIALIZED *geom = (GSERIALIZED*)PG_DETOAST_DATUM(the_datum); + LWGEOM *lwgeom = lwgeom_from_gserialized(geom); + bool empty = lwgeom_is_empty(lwgeom); + + lwgeom_free(lwgeom); + return empty; +} diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in index 7c51550fc58..6de717d7dcd 100644 --- a/postgis/postgis.sql.in +++ b/postgis/postgis.sql.in @@ -5768,6 +5768,7 @@ CREATE OPERATOR CLASS brin_geometry_inclusion_ops_2d FUNCTION 2 geom2d_brin_inclusion_add_value(internal, internal, internal, internal) , FUNCTION 3 brin_inclusion_consistent(internal, internal, internal) , FUNCTION 4 brin_inclusion_union(internal, internal, internal) , + FUNCTION 14 st_isempty(geometry) , STORAGE box2df; ALTER OPERATOR FAMILY brin_geometry_inclusion_family_2d USING brin ADD diff --git a/postgis/postgis_brin.h b/postgis/postgis_brin.h index e6109704be4..a618b27eaf7 100644 --- a/postgis/postgis_brin.h +++ b/postgis/postgis_brin.h @@ -12,11 +12,24 @@ #include #include #include +#include "access/genam.h" +#include "access/stratnum.h" #include "access/brin_tuple.h" #include "utils/datum.h" #include "gserialized_gist.h" -#define INCLUSION_UNION 0 +#define INCLUSION_MAX_PROCNUMS 4 /* maximum support procs we need */ +#define PROCNUM_BASE 11 + +#define INCLUSION_UNION 0 #define INCLUSION_UNMERGEABLE 1 #define INCLUSION_CONTAINS_EMPTY 2 +#define PROCNUM_EMPTY 14 /* optional support function to manage empty elements */ +typedef struct InclusionOpaque +{ + FmgrInfo extra_procinfos[INCLUSION_MAX_PROCNUMS]; + bool extra_proc_missing[INCLUSION_MAX_PROCNUMS]; + Oid cached_subtype; + FmgrInfo strategy_procinfos[RTMaxStrategyNumber]; +} InclusionOpaque;