diff --git a/pgsql/expected/pointcloud.out b/pgsql/expected/pointcloud.out index ccdad3f..08367bf 100644 --- a/pgsql/expected/pointcloud.out +++ b/pgsql/expected/pointcloud.out @@ -96,5 +96,13 @@ SELECT PC_Envelope(pa) from pa_test; \x01030000000100000005000000b81e85eb51b8ae3fec51b81e85ebb13fb81e85eb51b8ae3f9a9999999999b93f0ad7a3703d0ab73f9a9999999999b93f0ad7a3703d0ab73fec51b81e85ebb13fb81e85eb51b8ae3fec51b81e85ebb13f (2 rows) +SELECT PC_AsText(PC_Explode(PC_Patch(pt))) FROM pt_test; + pc_astext +----------------------------- + ( 1 : 0.01, 0.02, 0.03, 4 ) + ( 1 : 0.02, 0.03, 0.03, 5 ) + ( 1 : 0.03, 0.04, 0.03, 6 ) +(3 rows) + --DROP TABLE pt_test; --DROP TABLE pa_test; diff --git a/pgsql/pc_access.c b/pgsql/pc_access.c index 2e050db..8214bdc 100644 --- a/pgsql/pc_access.c +++ b/pgsql/pc_access.c @@ -22,6 +22,9 @@ Datum pcpoint_agg_transfn(PG_FUNCTION_ARGS); Datum pcpoint_abs_in(PG_FUNCTION_ARGS); Datum pcpoint_abs_out(PG_FUNCTION_ARGS); +/* Deaggregation functions */ +Datum pcpatch_unnest(PG_FUNCTION_ARGS); + /** * Read a named dimension from a PCPOINT * PC_Get(point pcpoint, dimname text) returns Numeric @@ -49,6 +52,20 @@ Datum pcpoint_get_value(PG_FUNCTION_ARGS) PG_RETURN_DATUM(DirectFunctionCall1(float8_numeric, Float8GetDatum(double_result))); } +static bool +array_get_isnull(const bits8 *nullbitmap, int offset) +{ + if (nullbitmap == NULL) + { + return false; /* assume not null */ + } + if (nullbitmap[offset / 8] & (1 << (offset % 8))) + { + return false; /* not null */ + } + return true; +} + PG_FUNCTION_INFO_V1(pcpatch_from_pcpoint_array); Datum pcpatch_from_pcpoint_array(PG_FUNCTION_ARGS) { @@ -88,7 +105,7 @@ Datum pcpatch_from_pcpoint_array(PG_FUNCTION_ARGS) for ( i = 0; i < nelems; i++ ) { /* Only work on non-NULL entries in the array */ - if ( (bitmap && (*bitmap & bitmask)) || !bitmap ) + if ( ! array_get_isnull(bitmap, i) ) { SERIALIZED_POINT *serpt = (SERIALIZED_POINT *)(ARR_DATA_PTR(array)+offset); PCPOINT *pt; @@ -113,16 +130,6 @@ Datum pcpatch_from_pcpoint_array(PG_FUNCTION_ARGS) offset += INTALIGN(VARSIZE(serpt)); } - /* Advance NULL bitmap */ - if (bitmap) - { - bitmask <<= 1; - if (bitmask == 0x100) - { - bitmap++; - bitmask = 1; - } - } } if ( pl->npoints == 0 ) @@ -254,4 +261,75 @@ Datum pcpoint_agg_final_pcpatch(PG_FUNCTION_ARGS) PG_RETURN_DATUM(result_final); } +PG_FUNCTION_INFO_V1(pcpatch_unnest); +Datum pcpatch_unnest(PG_FUNCTION_ARGS) +{ + typedef struct + { + int nextelem; + int numelems; + PCPOINTLIST *pointlist; + } pcpatch_unnest_fctx; + + FuncCallContext *funcctx; + pcpatch_unnest_fctx *fctx; + MemoryContext oldcontext; + + /* stuff done only on the first call of the function */ + if (SRF_IS_FIRSTCALL()) + { + PCPATCH *patch; + SERIALIZED_PATCH *serpatch; + + /* create a function context for cross-call persistence */ + funcctx = SRF_FIRSTCALL_INIT(); + + /* + * switch to memory context appropriate for multiple function calls + */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + /* + * Get the patch value and detoast if needed. We can't do this + * earlier because if we have to detoast, we want the detoasted copy + * to be in multi_call_memory_ctx, so it will go away when we're done + * and not before. (If no detoast happens, we assume the originally + * passed array will stick around till then.) + */ + serpatch = PG_GETARG_SERPATCH_P(0); + patch = pc_patch_deserialize(serpatch); + + /* allocate memory for user context */ + fctx = (pcpatch_unnest_fctx *) palloc(sizeof(pcpatch_unnest_fctx)); + + /* initialize state */ + fctx->nextelem = 0; + fctx->numelems = patch->npoints; + fctx->pointlist = pc_patch_to_points(patch); + + /* save user context, switch back to function context */ + funcctx->user_fctx = fctx; + MemoryContextSwitchTo(oldcontext); + } + + /* stuff done on every call of the function */ + funcctx = SRF_PERCALL_SETUP(); + fctx = funcctx->user_fctx; + + if (fctx->nextelem < fctx->numelems) + { + Datum elem; + PCPOINT *pt = fctx->pointlist->points[fctx->nextelem]; + SERIALIZED_POINT *serpt = pc_point_serialize(pt); + fctx->nextelem++; + elem = PointerGetDatum(serpt); + SRF_RETURN_NEXT(funcctx, elem); + } + else + { + /* do when there is no more left */ + SRF_RETURN_DONE(funcctx); + } +} + diff --git a/pgsql/pointcloud--1.0.sql b/pgsql/pointcloud--1.0.sql index 85201d7..64ee171 100644 --- a/pgsql/pointcloud--1.0.sql +++ b/pgsql/pointcloud--1.0.sql @@ -14,7 +14,7 @@ CREATE OR REPLACE FUNCTION PC_SchemaIsValid(xml text) -- Metadata table describing contents of pcpoints CREATE TABLE pointcloud_formats ( - pcid INTEGER PRIMARY KEY, + pcid INTEGER PRIMARY KEY CHECK (pcid > 0), srid INTEGER, -- REFERENCES spatial_ref_sys(srid) schema TEXT CHECK ( PC_SchemaIsValid(schema) ) @@ -157,15 +157,8 @@ CREATE AGGREGATE PC_Point_Agg ( FINALFUNC = pcpoint_agg_final_array ); - --- CREATE FUNCTION pcpoint_array_from_pcpatch(pcpatch) --- RETURNS pcpoint[] AS 'MODULE_PATHNAME', 'pcpoint_array_from_pcpatch' --- LANGUAGE 'sql' IMMUTABLE STRICT; +CREATE FUNCTION PC_Explode(pcpatch) + RETURNS setof pcpoint AS 'MODULE_PATHNAME', 'pcpatch_unnest' + LANGUAGE 'c' IMMUTABLE STRICT; --- The enumeration function --- returns each element in a one dimensional integer array --- as a row. --- CREATE FUNCTION int_array_enum(int4[]) --- RETURNS setof integer --- AS 'array_unnest' --- LANGUAGE INTERNAL IMMUTABLE STRICT; + diff --git a/pgsql/sql/pointcloud.sql b/pgsql/sql/pointcloud.sql index 22ae1bb..d5dbb19 100644 --- a/pgsql/sql/pointcloud.sql +++ b/pgsql/sql/pointcloud.sql @@ -72,5 +72,7 @@ INSERT INTO pa_test (pa) VALUES ('0000000001000000000000000200000006000000070000 SELECT PC_AsText(pa) FROM pa_test; SELECT PC_Envelope(pa) from pa_test; +SELECT PC_AsText(PC_Explode(PC_Patch(pt))) FROM pt_test; + --DROP TABLE pt_test; --DROP TABLE pa_test;