diff --git a/libpc/pc_dimensional.c b/libpc/pc_dimensional.c new file mode 100644 index 0000000..966a3fd --- /dev/null +++ b/libpc/pc_dimensional.c @@ -0,0 +1,21 @@ +/*********************************************************************** +* pc_dimensional.c +* +* Support for "dimensional compression", which is a catch-all +* term for applying compression separately on each dimension +* of a PCPATCH collection of PCPOINTS. +* +* Depending on the character of the data, one of these schemes +* will be used: +* +* - run-length encoding +* - significant-bit removal +* - deflate +* +* Portions Copyright (c) 2012, OpenGeo +* +***********************************************************************/ + +#include +#include "pc_api_internal.h" + diff --git a/libpc/pc_patch.c b/libpc/pc_patch.c index 6217e97..d981ed7 100644 --- a/libpc/pc_patch.c +++ b/libpc/pc_patch.c @@ -223,7 +223,7 @@ pc_patch_from_points(const PCPOINTLIST *pl) { if ( pl->points[i] ) { - if ( pl->points[i]->schema != s ) + if ( pl->points[i]->schema->pcid != s->pcid ) { pcerror("pc_patch_from_points: points do not share a schema"); return NULL; diff --git a/pgsql/pc_access.c b/pgsql/pc_access.c index 77bde7d..2e050db 100644 --- a/pgsql/pc_access.c +++ b/pgsql/pc_access.c @@ -9,18 +9,25 @@ #include "pc_pgsql.h" /* Common PgSQL support for our type */ #include "utils/numeric.h" +#include "funcapi.h" /* Other SQL functions */ -Datum PC_Get(PG_FUNCTION_ARGS); - +Datum pcpoint_get_value(PG_FUNCTION_ARGS); +Datum pcpatch_from_pcpoint_array(PG_FUNCTION_ARGS); +/* Aggregation functions */ +Datum pcpoint_agg_final_pcpatch(PG_FUNCTION_ARGS); +Datum pcpoint_agg_final_array(PG_FUNCTION_ARGS); +Datum pcpoint_agg_transfn(PG_FUNCTION_ARGS); +Datum pcpoint_abs_in(PG_FUNCTION_ARGS); +Datum pcpoint_abs_out(PG_FUNCTION_ARGS); /** * Read a named dimension from a PCPOINT * PC_Get(point pcpoint, dimname text) returns Numeric */ -PG_FUNCTION_INFO_V1(PC_Get); -Datum PC_Get(PG_FUNCTION_ARGS) +PG_FUNCTION_INFO_V1(pcpoint_get_value); +Datum pcpoint_get_value(PG_FUNCTION_ARGS) { SERIALIZED_POINT *serpt = PG_GETARG_SERPOINT_P(0); text *dim_name = PG_GETARG_TEXT_P(1); @@ -42,3 +49,209 @@ Datum PC_Get(PG_FUNCTION_ARGS) PG_RETURN_DATUM(DirectFunctionCall1(float8_numeric, Float8GetDatum(double_result))); } +PG_FUNCTION_INFO_V1(pcpatch_from_pcpoint_array); +Datum pcpatch_from_pcpoint_array(PG_FUNCTION_ARGS) +{ + ArrayType *array; + Datum datum = PG_GETARG_DATUM(0); + int nelems; + bits8 *bitmap; + int bitmask; + size_t offset = 0; + int i; + PCPOINTLIST *pl; + PCPATCH *pa; + SERIALIZED_PATCH *serpa; + uint32_t pcid = 0; + + /* Null array, null geometry (should be empty?) */ + if ( (Pointer *)datum == NULL ) PG_RETURN_NULL(); + + array = DatumGetArrayTypeP(datum); + + /* How many things in our array? */ + nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array)); + + /* PgSQL supplies a bitmap of which array entries are null */ + bitmap = ARR_NULLBITMAP(array); + + /* Empty array? Null return */ + if ( nelems == 0 ) + PG_RETURN_NULL(); + + /* Make our holder */ + pl = pc_pointlist_make(nelems); + + offset = 0; + bitmap = ARR_NULLBITMAP(array); + bitmask = 1; + for ( i = 0; i < nelems; i++ ) + { + /* Only work on non-NULL entries in the array */ + if ( (bitmap && (*bitmap & bitmask)) || !bitmap ) + { + SERIALIZED_POINT *serpt = (SERIALIZED_POINT *)(ARR_DATA_PTR(array)+offset); + PCPOINT *pt; + + if ( ! pcid ) + { + pcid = serpt->pcid; + } + else if ( pcid != serpt->pcid ) + { + elog(ERROR, "pcpatch_from_pcpoint_array: pcid mismatch (%d != %d)", serpt->pcid, pcid); + } + + pt = pc_point_deserialize(serpt); + if ( ! pt ) + { + elog(ERROR, "pcpatch_from_pcpoint_array: point deserialization failed"); + } + + pc_pointlist_add_point(pl, pt); + + offset += INTALIGN(VARSIZE(serpt)); + } + + /* Advance NULL bitmap */ + if (bitmap) + { + bitmask <<= 1; + if (bitmask == 0x100) + { + bitmap++; + bitmask = 1; + } + } + } + + if ( pl->npoints == 0 ) + PG_RETURN_NULL(); + + pa = pc_patch_from_points(pl); + pc_pointlist_free(pl); + serpa = pc_patch_serialize(pa); + pc_patch_free(pa); + PG_RETURN_POINTER(serpa); +} + +typedef struct +{ + ArrayBuildState *s; +} abs_trans; + +PG_FUNCTION_INFO_V1(pcpoint_abs_in); +Datum pcpoint_abs_in(PG_FUNCTION_ARGS) +{ + ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function pcpoint_abs_in not implemented"))); + PG_RETURN_POINTER(NULL); +} + +PG_FUNCTION_INFO_V1(pcpoint_abs_out); +Datum pcpoint_abs_out(PG_FUNCTION_ARGS) +{ + ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function pcpoint_abs_out not implemented"))); + PG_RETURN_POINTER(NULL); +} + + +PG_FUNCTION_INFO_V1(pcpoint_agg_transfn); +Datum pcpoint_agg_transfn(PG_FUNCTION_ARGS) +{ + Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1); + MemoryContext aggcontext; + abs_trans *a; + ArrayBuildState *state; + Datum elem; + + if (arg1_typeid == InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("could not determine input data type"))); + + if (fcinfo->context && IsA(fcinfo->context, AggState)) + { + aggcontext = ((AggState *) fcinfo->context)->aggcontext; + } + else if (fcinfo->context && IsA(fcinfo->context, WindowAggState)) + { + aggcontext = ((WindowAggState *) fcinfo->context)->aggcontext; + } + else + { + /* cannot be called directly because of dummy-type argument */ + elog(ERROR, "pcpoint_agg_transfn called in non-aggregate context"); + aggcontext = NULL; /* keep compiler quiet */ + } + + if ( PG_ARGISNULL(0) ) + { + a = (abs_trans*) palloc(sizeof(abs_trans)); + a->s = NULL; + } + else + { + a = (abs_trans*) PG_GETARG_POINTER(0); + } + state = a->s; + elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1); + state = accumArrayResult(state, + elem, + PG_ARGISNULL(1), + arg1_typeid, + aggcontext); + a->s = state; + + PG_RETURN_POINTER(a); +} + + + + +static Datum +pcpoint_agg_final(abs_trans *a, MemoryContext mctx, FunctionCallInfo fcinfo) +{ + ArrayBuildState *state; + int dims[1]; + int lbs[1]; + state = a->s; + dims[0] = state->nelems; + lbs[0] = 1; + return makeMdArrayResult(state, 1, dims, lbs, mctx, false); +} + +PG_FUNCTION_INFO_V1(pcpoint_agg_final_array); +Datum pcpoint_agg_final_array(PG_FUNCTION_ARGS) +{ + abs_trans *a; + Datum result = 0; + + if (PG_ARGISNULL(0)) + PG_RETURN_NULL(); /* returns null iff no input values */ + + a = (abs_trans*) PG_GETARG_POINTER(0); + + result = pcpoint_agg_final(a, CurrentMemoryContext, fcinfo); + PG_RETURN_DATUM(result); +} + +PG_FUNCTION_INFO_V1(pcpoint_agg_final_pcpatch); +Datum pcpoint_agg_final_pcpatch(PG_FUNCTION_ARGS) +{ + abs_trans *a; + Datum result = 0; + Datum result_final = 0; + + if (PG_ARGISNULL(0)) + PG_RETURN_NULL(); /* returns null iff no input values */ + + a = (abs_trans*) PG_GETARG_POINTER(0); + + result = pcpoint_agg_final(a, CurrentMemoryContext, fcinfo); + result_final = DirectFunctionCall1(pcpatch_from_pcpoint_array, result); + PG_RETURN_DATUM(result_final); +} + + diff --git a/pgsql/pc_inout.c b/pgsql/pc_inout.c index 4e4dbd0..a4918f6 100644 --- a/pgsql/pc_inout.c +++ b/pgsql/pc_inout.c @@ -16,13 +16,13 @@ Datum pcpatch_in(PG_FUNCTION_ARGS); Datum pcpatch_out(PG_FUNCTION_ARGS); /* Other SQL functions */ -Datum PC_SchemaIsValid(PG_FUNCTION_ARGS); -Datum PC_SchemaGetNDims(PG_FUNCTION_ARGS); -Datum PC_MakePointFromArray(PG_FUNCTION_ARGS); -Datum PC_PointAsText(PG_FUNCTION_ARGS); -Datum PC_PatchAsText(PG_FUNCTION_ARGS); -Datum PC_PointAsByteA(PG_FUNCTION_ARGS); -Datum PC_PatchEnvelopeAsByteA(PG_FUNCTION_ARGS); +Datum pcschema_is_valid(PG_FUNCTION_ARGS); +Datum pcschema_get_ndims(PG_FUNCTION_ARGS); +Datum pcpoint_from_double_array(PG_FUNCTION_ARGS); +Datum pcpoint_as_text(PG_FUNCTION_ARGS); +Datum pcpatch_as_text(PG_FUNCTION_ARGS); +Datum pcpoint_as_bytea(PG_FUNCTION_ARGS); +Datum pcpatch_bytea_envelope(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(pcpoint_in); Datum pcpoint_in(PG_FUNCTION_ARGS) @@ -125,8 +125,8 @@ Datum pcpatch_out(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(hexwkb); } -PG_FUNCTION_INFO_V1(PC_SchemaIsValid); -Datum PC_SchemaIsValid(PG_FUNCTION_ARGS) +PG_FUNCTION_INFO_V1(pcschema_is_valid); +Datum pcschema_is_valid(PG_FUNCTION_ARGS) { bool valid; text *xml = PG_GETARG_TEXT_P(0); @@ -145,8 +145,8 @@ Datum PC_SchemaIsValid(PG_FUNCTION_ARGS) PG_RETURN_BOOL(valid); } -PG_FUNCTION_INFO_V1(PC_SchemaGetNDims); -Datum PC_SchemaGetNDims(PG_FUNCTION_ARGS) +PG_FUNCTION_INFO_V1(pcschema_get_ndims); +Datum pcschema_get_ndims(PG_FUNCTION_ARGS) { int ndims; uint32 pcid = PG_GETARG_INT32(0); @@ -161,10 +161,10 @@ Datum PC_SchemaGetNDims(PG_FUNCTION_ARGS) } /** -* PC_MakePointFromArray(integer pcid, float8[] returns PcPoint +* pcpoint_from_double_array(integer pcid, float8[] returns PcPoint */ -PG_FUNCTION_INFO_V1(PC_MakePointFromArray); -Datum PC_MakePointFromArray(PG_FUNCTION_ARGS) +PG_FUNCTION_INFO_V1(pcpoint_from_double_array); +Datum pcpoint_from_double_array(PG_FUNCTION_ARGS) { uint32 pcid = PG_GETARG_INT32(0); ArrayType *arrptr = PG_GETARG_ARRAYTYPE_P(1); @@ -208,8 +208,8 @@ Datum PC_MakePointFromArray(PG_FUNCTION_ARGS) PG_RETURN_POINTER(serpt); } -PG_FUNCTION_INFO_V1(PC_PointAsText); -Datum PC_PointAsText(PG_FUNCTION_ARGS) +PG_FUNCTION_INFO_V1(pcpoint_as_text); +Datum pcpoint_as_text(PG_FUNCTION_ARGS) { SERIALIZED_POINT *serpt = PG_GETARG_SERPOINT_P(0); text *txt; @@ -225,8 +225,8 @@ Datum PC_PointAsText(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(txt); } -PG_FUNCTION_INFO_V1(PC_PatchAsText); -Datum PC_PatchAsText(PG_FUNCTION_ARGS) +PG_FUNCTION_INFO_V1(pcpatch_as_text); +Datum pcpatch_as_text(PG_FUNCTION_ARGS) { SERIALIZED_PATCH *serpatch = PG_GETARG_SERPATCH_P(0); text *txt; @@ -242,8 +242,8 @@ Datum PC_PatchAsText(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(txt); } -PG_FUNCTION_INFO_V1(PC_PointAsByteA); -Datum PC_PointAsByteA(PG_FUNCTION_ARGS) +PG_FUNCTION_INFO_V1(pcpoint_as_bytea); +Datum pcpoint_as_bytea(PG_FUNCTION_ARGS) { SERIALIZED_POINT *serpt = PG_GETARG_SERPOINT_P(0); uint8_t *bytes; @@ -267,8 +267,8 @@ Datum PC_PointAsByteA(PG_FUNCTION_ARGS) PG_RETURN_BYTEA_P(wkb); } -PG_FUNCTION_INFO_V1(PC_PatchEnvelopeAsByteA); -Datum PC_PatchEnvelopeAsByteA(PG_FUNCTION_ARGS) +PG_FUNCTION_INFO_V1(pcpatch_bytea_envelope); +Datum pcpatch_bytea_envelope(PG_FUNCTION_ARGS) { SERIALIZED_PATCH *serpatch = PG_GETARG_SERPATCH_P(0); uint8_t *bytes; diff --git a/pgsql/pointcloud--1.0.sql b/pgsql/pointcloud--1.0.sql index 1fc9a70..85201d7 100644 --- a/pgsql/pointcloud--1.0.sql +++ b/pgsql/pointcloud--1.0.sql @@ -9,7 +9,7 @@ -- Confirm the XML representation of a schema has everything we need CREATE OR REPLACE FUNCTION PC_SchemaIsValid(xml text) - RETURNS boolean AS 'MODULE_PATHNAME','PC_SchemaIsValid' + RETURNS boolean AS 'MODULE_PATHNAME','pcschema_is_valid' LANGUAGE 'c' IMMUTABLE STRICT; -- Metadata table describing contents of pcpoints @@ -24,7 +24,7 @@ CREATE TABLE pointcloud_formats ( SELECT pg_catalog.pg_extension_config_dump('pointcloud_formats', ''); CREATE OR REPLACE FUNCTION PC_SchemaGetNDims(pcid integer) - RETURNS integer AS 'MODULE_PATHNAME','PC_SchemaGetNDims' + RETURNS integer AS 'MODULE_PATHNAME','pcschema_get_ndims' LANGUAGE 'c' IMMUTABLE STRICT; @@ -56,19 +56,19 @@ CREATE TYPE pcpoint ( ); CREATE OR REPLACE FUNCTION PC_Get(pt pcpoint, dimname text) - RETURNS numeric AS 'MODULE_PATHNAME', 'PC_Get' + RETURNS numeric AS 'MODULE_PATHNAME', 'pcpoint_get_value' LANGUAGE 'c' IMMUTABLE STRICT; CREATE OR REPLACE FUNCTION PC_MakePoint(pcid integer, vals float8[]) - RETURNS pcpoint AS 'MODULE_PATHNAME', 'PC_MakePointFromArray' + RETURNS pcpoint AS 'MODULE_PATHNAME', 'pcpoint_from_double_array' LANGUAGE 'c' IMMUTABLE STRICT; CREATE OR REPLACE FUNCTION PC_AsText(p pcpoint) - RETURNS text AS 'MODULE_PATHNAME', 'PC_PointAsText' + RETURNS text AS 'MODULE_PATHNAME', 'pcpoint_as_text' LANGUAGE 'c' IMMUTABLE STRICT; CREATE OR REPLACE FUNCTION PC_AsBinary(p pcpoint) - RETURNS bytea AS 'MODULE_PATHNAME', 'PC_PointAsByteA' + RETURNS bytea AS 'MODULE_PATHNAME', 'pcpoint_as_bytea' LANGUAGE 'c' IMMUTABLE STRICT; ------------------------------------------------------------------- @@ -98,11 +98,74 @@ CREATE TYPE pcpatch ( ); CREATE OR REPLACE FUNCTION PC_AsText(p pcpatch) - RETURNS text AS 'MODULE_PATHNAME', 'PC_PatchAsText' + RETURNS text AS 'MODULE_PATHNAME', 'pcpatch_as_text' LANGUAGE 'c' IMMUTABLE STRICT; CREATE OR REPLACE FUNCTION PC_Envelope(p pcpatch) - RETURNS bytea AS 'MODULE_PATHNAME', 'PC_PatchEnvelopeAsByteA' + RETURNS bytea AS 'MODULE_PATHNAME', 'pcpatch_bytea_envelope' LANGUAGE 'c' IMMUTABLE STRICT; + +------------------------------------------------------------------- +-- AGGREGATE / EXPLODE +------------------------------------------------------------------- + +CREATE OR REPLACE FUNCTION pcpoint_abs_in(cstring) + RETURNS pcpoint_abs AS 'MODULE_PATHNAME' + LANGUAGE 'c'; + +CREATE OR REPLACE FUNCTION pcpoint_abs_out(pcpoint_abs) + RETURNS cstring AS 'MODULE_PATHNAME' + LANGUAGE 'c'; + +CREATE TYPE pcpoint_abs ( + internallength = 8, + input = pcpoint_abs_in, + output = pcpoint_abs_out, + alignment = double +); + +CREATE FUNCTION PC_Patch(pcpoint[]) + RETURNS pcpatch AS 'MODULE_PATHNAME', 'pcpatch_from_pcpoint_array' + LANGUAGE 'c' IMMUTABLE STRICT; + + +CREATE FUNCTION pcpoint_agg_transfn (pcpoint_abs, pcpoint) + RETURNS pcpoint_abs AS'MODULE_PATHNAME', 'pcpoint_agg_transfn' + LANGUAGE 'c'; + +CREATE FUNCTION pcpoint_agg_final_array (pcpoint_abs) + RETURNS pcpoint[] AS 'MODULE_PATHNAME', 'pcpoint_agg_final_array' + LANGUAGE 'c'; + +CREATE FUNCTION pcpoint_agg_final_pcpatch (pcpoint_abs) + RETURNS pcpatch AS 'MODULE_PATHNAME', 'pcpoint_agg_final_pcpatch' + LANGUAGE 'c'; + +CREATE AGGREGATE PC_Patch ( + BASETYPE = pcpoint, + SFUNC = pcpoint_agg_transfn, + STYPE = pcpoint_abs, + FINALFUNC = pcpoint_agg_final_pcpatch +); + +CREATE AGGREGATE PC_Point_Agg ( + BASETYPE = pcpoint, + SFUNC = pcpoint_agg_transfn, + STYPE = pcpoint_abs, + 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; + +-- 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;