]> granicus.if.org Git - postgis/commitdiff
Refactored ST_MapAlgebraFctNgb to cache userfunction lookup, and accept NULL input...
authorDavid Zwarg <dzwarg@azavea.com>
Fri, 18 Nov 2011 20:53:46 +0000 (20:53 +0000)
committerDavid Zwarg <dzwarg@azavea.com>
Fri, 18 Nov 2011 20:53:46 +0000 (20:53 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@8183 b70326c6-7e19-0410-871a-916f4a2858ee

raster/rt_pg/rt_pg.c
raster/test/regress/create_rt_mapalgebrafctngb_test.sql
raster/test/regress/rt_mapalgebrafctngb.sql
raster/test/regress/rt_mapalgebrafctngb_expected

index 22ba772c9fe28bad9a0d65f6c7a3658c9e2339f1..0e8d07044a7332286f61699a33fcdf00d0f878d1 100644 (file)
@@ -8938,7 +8938,8 @@ Datum RASTER_mapAlgebraFctNgb(PG_FUNCTION_ARGS)
     rt_pixtype newpixeltype;
     int ret = -1;
     Oid oid;
-    Datum extraargs;
+    FmgrInfo cbinfo;
+    FunctionCallInfoData cbdata;
     Datum tmpnewval;
     ArrayType * neighborDatum;
     char * strFromText = NULL;
@@ -9126,6 +9127,10 @@ Datum RASTER_mapAlgebraFctNgb(PG_FUNCTION_ARGS)
     
     if (newpixeltype == PT_END) {
         elog(ERROR, "RASTER_mapAlgebraFctNgb: Invalid pixeltype. Returning NULL");
+
+        rt_raster_destroy(raster);
+        rt_raster_destroy(newrast);
+
         PG_RETURN_NULL();
     }    
     
@@ -9135,12 +9140,73 @@ Datum RASTER_mapAlgebraFctNgb(PG_FUNCTION_ARGS)
     /* Get the name of the callback userfunction */
     if (PG_ARGISNULL(5)) {
         elog(ERROR, "RASTER_mapAlgebraFctNgb: Required function is missing. Returning NULL");
+
+        rt_raster_destroy(raster);
+        rt_raster_destroy(newrast);
+
         PG_RETURN_NULL();
     }
 
     oid = PG_GETARG_OID(5);
+    if (oid == InvalidOid) {
+        elog(ERROR, "RASTER_mapAlgebraFctNgb: Got invalid function object id. Returning NULL");
+
+        rt_raster_destroy(raster);
+        rt_raster_destroy(newrast);
+
+        PG_RETURN_NULL();
+    }
 
-    POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFctNgb: Got function oid: %d", oid);
+    fmgr_info(oid, &cbinfo);
+
+    /* function cannot return set */
+    if (cbinfo.fn_retset) {
+        elog(ERROR, "RASTER_mapAlgebraFctNgb: Function provided must return double precision not resultset. Returning NULL");
+
+        rt_raster_destroy(raster);
+        rt_raster_destroy(newrast);
+
+        PG_RETURN_NULL();
+    }
+    /* function should have correct # of args */
+    else if (cbinfo.fn_nargs != 3) {
+        elog(ERROR, "RASTER_mapAlgebraFctNgb: Function does not have three input parameters. Returning NULL");
+
+        rt_raster_destroy(raster);
+        rt_raster_destroy(newrast);
+
+        PG_RETURN_NULL();
+    }
+
+    if (func_volatile(oid) == 'v') {
+        elog(NOTICE, "Function provided is VOLATILE. Unless required and for best performance, function should be IMMUTABLE or STABLE");
+    }
+
+    /* prep function call data */
+#if POSTGIS_PGSQL_VERSION <= 90
+    InitFunctionCallInfoData(cbdata, &cbinfo, 3, InvalidOid, NULL);
+#else
+    InitFunctionCallInfoData(cbdata, &cbinfo, 3, InvalidOid, NULL, NULL);
+#endif
+    memset(cbdata.argnull, FALSE, 3);
+
+    /* check that the function isn't strict if the args are null. */
+    if (PG_ARGISNULL(7)) {
+        if (cbinfo.fn_strict) {
+            elog(ERROR, "RASTER_mapAlgebraFctNgb: Strict callback functions cannot have null parameters. Returning NULL");
+
+            rt_raster_destroy(raster);
+            rt_raster_destroy(newrast);
+
+            PG_RETURN_NULL();
+        }
+
+        cbdata.arg[2] = (Datum)NULL;
+        cbdata.argnull[2] = TRUE;
+    }
+    else {
+        cbdata.arg[2] = PG_GETARG_DATUM(7);
+    }
 
     /**
      * Optimization: If the raster is only filled with nodata values return
@@ -9281,6 +9347,9 @@ Datum RASTER_mapAlgebraFctNgb(PG_FUNCTION_ARGS)
     SET_VARSIZE(txtCallbackParam, VARSIZE(txtNodataMode));
     memcpy((void *)VARDATA(txtCallbackParam), (void *)VARDATA(txtNodataMode), VARSIZE(txtNodataMode) - VARHDRSZ);
 
+    /* pass the nodata mode into the user function */
+    cbdata.arg[1] = CStringGetDatum(txtCallbackParam);
+
     strFromText = text_to_cstring(txtNodataMode);
     strFromText = strtoupper(strFromText);
 
@@ -9322,8 +9391,6 @@ Datum RASTER_mapAlgebraFctNgb(PG_FUNCTION_ARGS)
     POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFctNgb: Main computing loop (%d x %d)",
             width, height);
 
-    extraargs = PG_GETARG_DATUM(7);
-
     /* Allocate room for the neighborhood. */
     neighborData = (Datum *)palloc(winwidth * winheight * sizeof(Datum));
     neighborNulls = (bool *)palloc(winwidth * winheight * sizeof(bool));
@@ -9399,9 +9466,19 @@ Datum RASTER_mapAlgebraFctNgb(PG_FUNCTION_ARGS)
                 neighborDatum = construct_md_array((void *)neighborData, neighborNulls, 2, neighborDims, neighborLbs, 
                     FLOAT8OID, typlen, typbyval, typalign);
 
-                /* convert neighborData to a datum for OidFunctionCall3 */
-                tmpnewval = OidFunctionCall3(oid, PointerGetDatum(neighborDatum), CStringGetDatum(txtCallbackParam), extraargs);
-                newval = DatumGetFloat8(tmpnewval);
+                /* Assign the neighbor matrix as the first argument to the user function */
+                cbdata.arg[0] = PointerGetDatum(neighborDatum);
+
+                /* Invoke the user function */
+                tmpnewval = FunctionCallInvoke(&cbdata);
+
+                /* Get the return value of the user function */
+                if (cbdata.isnull) {
+                    newval = newnodatavalue;
+                }
+                else {
+                    newval = DatumGetFloat8(tmpnewval);
+                }
 
                 POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFctNgb: new value = %f", 
                     newval);
@@ -9416,13 +9493,9 @@ Datum RASTER_mapAlgebraFctNgb(PG_FUNCTION_ARGS)
 
 
     /* clean up */
-    POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFctNgb: cleaning up memory (neighborNulls)");
     pfree(neighborNulls);
-    POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFctNgb: cleaning up memory (neighborData)");
     pfree(neighborData);
-    POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFctNgb: cleaning up memory (strFromText = '%s')", strFromText);
     pfree(strFromText);
-    POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFctNgb: cleaning up memory (txtCallbackFaram)", txtCallbackParam);
     pfree(txtCallbackParam);
     
     /* The newrast band has been modified */
index f77c20865f6a26928b9d85a95d957e23debd855c..aa2ec67c2a25654d96507045799a67e3139948fb 100644 (file)
@@ -29,7 +29,17 @@ CREATE OR REPLACE FUNCTION ST_Sum(matrix float[][], nodatamode text, variadic ar
         RETURN sum;
     END;
     $$
-    LANGUAGE 'plpgsql';
+    LANGUAGE 'plpgsql' IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION ST_Nullage(matrix float[][], nodatamode text, VARIADIC args text[])
+    RETURNS float AS
+    $$
+    BEGIN
+        RETURN NULL;
+    END;
+    $$
+    LANGUAGE 'plpgsql' IMMUTABLE;
+
 
 --
 --Test rasters
index 7258e95dc9129a74be108c5817dd2b4f173f8db0..977c5d4638fb9c940a1ef8bbc1a4f083e3f6de27 100644 (file)
@@ -165,3 +165,10 @@ SELECT
     )).*
    FROM ST_TestRasterNgb(5, 5, 1) AS rast) AS foo;
 
+-- test a user function that nullifies everything
+SELECT 
+  ST_Value(rast, 2, 2) = 2,
+  ST_Value(
+    ST_MapAlgebraFctNgb(rast, 1, NULL, 1, 1, 'ST_Nullage(float[][], text, text[])'::regprocedure, 'NULL', NULL), 2, 2
+  ) IS NULL
+ FROM ST_TestRasterNgb(3, 3, 2) AS rast;
index 49bd40c8ccb2af6d81d2b6a617f7427335af7ef3..f5675eca9926edb98bc82ca57ab6ce62a2d973d7 100644 (file)
@@ -52,3 +52,4 @@ t|t|t
 NOTICE:  SRID value -1 converted to the officially unknown SRID value 0
 NOTICE:  SRID value -1 converted to the officially unknown SRID value 0
 t|t|t
+t|t