From e086d63bb34115e4aaec17ad4698977c7e8d97ee Mon Sep 17 00:00:00 2001 From: Paul Ramsey Date: Wed, 6 Feb 2013 14:28:19 -0800 Subject: [PATCH] Add typmod support to pcpoint and pcpatch --- lib/cunit/cu_pc_patch.c | 43 ++++++++ lib/pc_api_internal.h | 8 ++ lib/pc_dimensional.c | 201 ++++++++++++++++++++++++++++++++++ pgsql/expected/pointcloud.out | 24 +++- pgsql/pc_access.c | 6 +- pgsql/pc_inout.c | 124 ++++++++++++++++++--- pgsql/pc_pgsql.c | 53 +++++---- pgsql/pc_pgsql.h | 11 +- pgsql/pointcloud--1.0.sql | 24 +++- pgsql/sql/pointcloud.sql | 9 +- 10 files changed, 450 insertions(+), 53 deletions(-) diff --git a/lib/cunit/cu_pc_patch.c b/lib/cunit/cu_pc_patch.c index 8cc2579..fd0af9f 100644 --- a/lib/cunit/cu_pc_patch.c +++ b/lib/cunit/cu_pc_patch.c @@ -264,6 +264,48 @@ test_run_length_encoding() } +static void +test_sigbits_encoding() +{ + char *bytes; + uint32_t count; + uint8_t common8; + uint16_t common16; + uint32_t common32; + + /* + 01100001 a + 01100010 b + 01100011 c + 01100000 ` + */ + bytes = "abc"; + common8 = pc_sigbits_8(bytes, strlen(bytes), &count); + CU_ASSERT_EQUAL(count, 6); + CU_ASSERT_EQUAL(common8, '`'); + + bytes = "abcdef"; + common8 = pc_sigbits_8(bytes, strlen(bytes), &count); + CU_ASSERT_EQUAL(count, 5); + CU_ASSERT_EQUAL(common8, '`'); + + /* + 0110000101100001 aa + 0110001001100010 bb + 0110001101100011 cc + 0110000000000000 24576 + */ + bytes = "aabbcc"; + common16 = pc_sigbits_16(bytes, strlen(bytes)/2, &count); + CU_ASSERT_EQUAL(count, 6); + CU_ASSERT_EQUAL(common16, 24576); + + bytes = "aaaabaaacaaadaaaeaaafaaa"; + common32 = pc_sigbits_32(bytes, strlen(bytes)/4, &count); + CU_ASSERT_EQUAL(count, 29); + CU_ASSERT_EQUAL(common32, 1633771872); + +} /* REGISTER ***********************************************************/ @@ -272,6 +314,7 @@ CU_TestInfo patch_tests[] = { PG_TEST(test_patch_hex_in), PG_TEST(test_patch_hex_out), PG_TEST(test_run_length_encoding), + PG_TEST(test_sigbits_encoding), CU_TEST_INFO_NULL }; diff --git a/lib/pc_api_internal.h b/lib/pc_api_internal.h index d197030..7275b0d 100644 --- a/lib/pc_api_internal.h +++ b/lib/pc_api_internal.h @@ -93,4 +93,12 @@ uint8_t* pc_bytes_run_length_encode(const uint8_t *bytes, uint32_t interpretatio /** Convert RLE bytes to value bytes */ uint8_t* pc_bytes_run_length_decode(const uint8_t *bytes_rle, size_t bytes_rle_size, uint32_t interpretation, uint32_t *bytes_nelems); +/** How many bits are shared by all elements of this array? */ +uint32_t pc_signbits_count(const uint8_t *bytes, uint32_t interpretation, uint32_t nelems); + +uint8_t pc_sigbits_8 (const uint8_t *bytes8, uint32_t nelems, uint32_t *nsigbits); +uint16_t pc_sigbits_16(const uint8_t *bytes8, uint32_t nelems, uint32_t *nsigbits); +uint32_t pc_sigbits_32(const uint8_t *bytes8, uint32_t nelems, uint32_t *nsigbits); +uint64_t pc_sigbits_64(const uint8_t *bytes8, uint32_t nelems, uint32_t *nsigbits); + #endif /* _PC_API_INTERNAL_H */ \ No newline at end of file diff --git a/lib/pc_dimensional.c b/lib/pc_dimensional.c index 018977e..dfbb3c4 100644 --- a/lib/pc_dimensional.c +++ b/lib/pc_dimensional.c @@ -20,6 +20,11 @@ #include "pc_api_internal.h" +/** +* How many distinct runs of values are there in this array? +* One? Two? Five? Great news for run-length encoding! +* N? Not so great news. +*/ uint32_t pc_bytes_run_count(const uint8_t *bytes, uint32_t interpretation, uint32_t nelems) { @@ -146,3 +151,199 @@ pc_bytes_run_length_decode(const uint8_t *bytes_rle, size_t bytes_rle_size, uint return bytes; } + + + + + + + + +uint8_t +pc_sigbits_8(const uint8_t *bytes, uint32_t nelems, uint32_t *nsigbits) +{ + static uint8_t nbits = 8; + uint8_t elem_and = bytes[0]; + uint8_t elem_or = bytes[0]; + uint32_t commonbits = nbits; + int i; + + for ( i = 0; i < nelems; i++ ) + { + elem_and &= bytes[i]; + elem_or |= bytes[i]; + } + + while ( elem_and != elem_or ) + { + elem_and >>= 1; + elem_or >>= 1; + commonbits -= 1; + } + elem_and <<= nbits - commonbits; + if ( nsigbits ) *nsigbits = commonbits; + return elem_and; +} + +uint16_t +pc_sigbits_16(const uint8_t *bytes8, uint32_t nelems, uint32_t *nsigbits) +{ + static int nbits = 16; + uint16_t *bytes = (uint16_t*)bytes8; + uint16_t elem_and = bytes[0]; + uint16_t elem_or = bytes[0]; + uint32_t commonbits = nbits; + int i; + + for ( i = 0; i < nelems; i++ ) + { + elem_and &= bytes[i]; + elem_or |= bytes[i]; + } + + while ( elem_and != elem_or ) + { + elem_and >>= 1; + elem_or >>= 1; + commonbits -= 1; + } + elem_and <<= nbits - commonbits; + if ( nsigbits ) *nsigbits = commonbits; + return elem_and; +} + +uint32_t +pc_sigbits_32(const uint8_t *bytes8, uint32_t nelems, uint32_t *nsigbits) +{ + static int nbits = 32; + uint32_t *bytes = (uint32_t*)bytes8; + uint32_t elem_and = bytes[0]; + uint32_t elem_or = bytes[0]; + uint32_t commonbits = nbits; + int i; + + for ( i = 0; i < nelems; i++ ) + { + elem_and &= bytes[i]; + elem_or |= bytes[i]; + } + + while ( elem_and != elem_or ) + { + elem_and >>= 1; + elem_or >>= 1; + commonbits -= 1; + } + elem_and <<= nbits - commonbits; + if ( nsigbits ) *nsigbits = commonbits; + return elem_and; +} + +uint64_t +pc_sigbits_64(const uint8_t *bytes8, uint32_t nelems, uint32_t *nsigbits) +{ + static int nbits = 64; + uint64_t *bytes = (uint64_t*)bytes8; + uint64_t elem_and = bytes[0]; + uint64_t elem_or = bytes[0]; + uint32_t commonbits = nbits; + int i; + + for ( i = 0; i < nelems; i++ ) + { + elem_and &= bytes[i]; + elem_or |= bytes[i]; + } + + while ( elem_and != elem_or ) + { + elem_and >>= 1; + elem_or >>= 1; + commonbits -= 1; + } + elem_and <<= nbits - commonbits; + if ( nsigbits ) *nsigbits = commonbits; + return elem_and; +} + + + +/** +* How many bits are shared by all elements of this array? +*/ +uint32_t +pc_sigbits_count(const uint8_t *bytes, uint32_t interpretation, uint32_t nelems) +{ + int i, j, start, end, incr; + const uint8_t *bytes_ptr; + uint8_t bytes_and[8]; + uint8_t bytes_or[8]; + uint8_t bytes_sig[8]; + size_t size = INTERPRETATION_SIZES[interpretation]; + uint32_t count = 0; + + memset(bytes_sig, 0, 8); + memset(bytes_and, 0, 8); + memset(bytes_or, 0, 8); + memcpy(bytes_and, bytes, size); + memcpy(bytes_or, bytes, size); + + /* Figure out the global "and" and "or" of all the values */ + for ( i = 1; i < nelems; i++ ) + { + bytes_ptr = bytes + i*size; + for ( j = 0; j < size; j++ ) + { + bytes_and[j] &= bytes_ptr[j]; + bytes_or[j] |= bytes_ptr[j]; + } + } + + /* Count down the bytes for little-endian, up for big */ + if ( machine_endian() == PC_NDR ) + { + start = size-1; + end = -1; + incr = -1; + } + else + { + start = 0; + end = size; + incr = 1; + } + + for ( i = start; i != end; i += incr ) + { + /* If bytes are the same, all 8 bits are shared! */ + if ( bytes_and[i] == bytes_or[i] ) + { + count += 8; + bytes_sig[i] = bytes_and[i]; + } + /* If bytes are different, find if they share any top bits */ + else + { + int commonbits = 8; + uint8_t b_and = bytes_and[i]; + uint8_t b_or = bytes_or[i]; + + /* Slide off bottom bits until the values match */ + while ( b_and != b_or ) + { + b_and >>= 1; + b_or >>= 1; + commonbits -= 1; + } + count += commonbits; + + /* Save the common bits to the significant bit value */ + b_and <<= 8 - commonbits; + bytes_sig[i] = b_and; + break; + } + } + + return count; +} + diff --git a/pgsql/expected/pointcloud.out b/pgsql/expected/pointcloud.out index 08367bf..0a50d3a 100644 --- a/pgsql/expected/pointcloud.out +++ b/pgsql/expected/pointcloud.out @@ -48,9 +48,19 @@ VALUES (1, 0, ' ); CREATE TABLE IF NOT EXISTS pt_test ( - pt PCPOINT + pt PCPOINT(1) ); +\d pt_test + Table "public.pt_test" + Column | Type | Modifiers +--------+------------+----------- + pt | pcpoint(1) | + DELETE FROM pt_test; +INSERT INTO pt_test (pt) VALUES ('00000000020000000100000002000000030004'); +ERROR: no entry in "pointcloud_formats" for pcid = 2 +LINE 1: INSERT INTO pt_test (pt) VALUES ('00000000020000000100000002... + ^ INSERT INTO pt_test (pt) VALUES ('00000000010000000100000002000000030004'); INSERT INTO pt_test (pt) VALUES ('00000000010000000200000003000000030005'); INSERT INTO pt_test (pt) VALUES ('00000000010000000300000004000000030006'); @@ -77,9 +87,19 @@ SELECT PC_AsText(pt) FROM pt_test; (3 rows) CREATE TABLE IF NOT EXISTS pa_test ( - pa PCPATCH + pa PCPATCH(1) ); +\d pa_test + Table "public.pa_test" + Column | Type | Modifiers +--------+------------+----------- + pa | pcpatch(1) | + DELETE FROM pa_test; +INSERT INTO pa_test (pa) VALUES ('0000000002000000000000000200000002000000030000000500060000000200000003000000050008'); +ERROR: no entry in "pointcloud_formats" for pcid = 2 +LINE 1: INSERT INTO pa_test (pa) VALUES ('00000000020000000000000002... + ^ INSERT INTO pa_test (pa) VALUES ('0000000001000000000000000200000002000000030000000500060000000200000003000000050008'); INSERT INTO pa_test (pa) VALUES ('000000000100000000000000020000000600000007000000050006000000090000000A00000005000A'); SELECT PC_AsText(pa) FROM pa_test; diff --git a/pgsql/pc_access.c b/pgsql/pc_access.c index 8214bdc..b5d1d7a 100644 --- a/pgsql/pc_access.c +++ b/pgsql/pc_access.c @@ -37,7 +37,7 @@ Datum pcpoint_get_value(PG_FUNCTION_ARGS) char *dim_str; float8 double_result; - PCPOINT *pt = pc_point_deserialize(serpt); + PCPOINT *pt = pc_point_deserialize(serpt, fcinfo); if ( ! pt ) PG_RETURN_NULL(); @@ -119,7 +119,7 @@ Datum pcpatch_from_pcpoint_array(PG_FUNCTION_ARGS) elog(ERROR, "pcpatch_from_pcpoint_array: pcid mismatch (%d != %d)", serpt->pcid, pcid); } - pt = pc_point_deserialize(serpt); + pt = pc_point_deserialize(serpt, fcinfo); if ( ! pt ) { elog(ERROR, "pcpatch_from_pcpoint_array: point deserialization failed"); @@ -297,7 +297,7 @@ Datum pcpatch_unnest(PG_FUNCTION_ARGS) * passed array will stick around till then.) */ serpatch = PG_GETARG_SERPATCH_P(0); - patch = pc_patch_deserialize(serpatch); + patch = pc_patch_deserialize(serpatch, fcinfo); /* allocate memory for user context */ fctx = (pcpatch_unnest_fctx *) palloc(sizeof(pcpatch_unnest_fctx)); diff --git a/pgsql/pc_inout.c b/pgsql/pc_inout.c index a4918f6..ca57833 100644 --- a/pgsql/pc_inout.c +++ b/pgsql/pc_inout.c @@ -15,6 +15,10 @@ Datum pcpoint_out(PG_FUNCTION_ARGS); Datum pcpatch_in(PG_FUNCTION_ARGS); Datum pcpatch_out(PG_FUNCTION_ARGS); +/* Typmod support */ +Datum pc_typmod_in(PG_FUNCTION_ARGS); +Datum pc_typmod_out(PG_FUNCTION_ARGS); + /* Other SQL functions */ Datum pcschema_is_valid(PG_FUNCTION_ARGS); Datum pcschema_get_ndims(PG_FUNCTION_ARGS); @@ -24,18 +28,34 @@ Datum pcpatch_as_text(PG_FUNCTION_ARGS); Datum pcpoint_as_bytea(PG_FUNCTION_ARGS); Datum pcpatch_bytea_envelope(PG_FUNCTION_ARGS); + +static void +pcid_consistent(const uint32 pcid, const uint32 column_pcid) +{ + if ( column_pcid && pcid != column_pcid ) + { + ereport(ERROR, ( + errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("point/patch pcid (%u) does not match column pcid (%d)", pcid, column_pcid) + )); + } +} + + PG_FUNCTION_INFO_V1(pcpoint_in); Datum pcpoint_in(PG_FUNCTION_ARGS) { char *str = PG_GETARG_CSTRING(0); - /* Datum geog_oid = PG_GETARG_OID(1); Not needed. */ - int32 pc_typmod = -1; + /* Datum pc_oid = PG_GETARG_OID(1); Not needed. */ + int32 typmod = 0; + uint32 pcid = 0; PCPOINT *pt; SERIALIZED_POINT *serpt; if ( (PG_NARGS()>2) && (!PG_ARGISNULL(2)) ) { - pc_typmod = PG_GETARG_INT32(2); + typmod = PG_GETARG_INT32(2); + pcid = pcid_from_typmod(typmod); } /* Empty string. */ @@ -48,7 +68,8 @@ Datum pcpoint_in(PG_FUNCTION_ARGS) if ( str[0] == '0' ) { /* Hex-encoded binary */ - pt = pc_point_from_hexwkb(str, strlen(str)); + pt = pc_point_from_hexwkb(str, strlen(str), fcinfo); + pcid_consistent(pt->schema->pcid, pcid); serpt = pc_point_serialize(pt); pc_point_free(pt); } @@ -68,7 +89,7 @@ Datum pcpoint_out(PG_FUNCTION_ARGS) char *hexwkb = NULL; serpt = (SERIALIZED_POINT*)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); - pcpt = pc_point_deserialize(serpt); + pcpt = pc_point_deserialize(serpt, fcinfo); hexwkb = pc_point_to_hexwkb(pcpt); pc_point_free(pcpt); PG_RETURN_CSTRING(hexwkb); @@ -80,13 +101,14 @@ Datum pcpatch_in(PG_FUNCTION_ARGS) { char *str = PG_GETARG_CSTRING(0); /* Datum geog_oid = PG_GETARG_OID(1); Not needed. */ - int32 pc_typmod = -1; + uint32 typmod = 0, pcid = 0; PCPATCH *patch; SERIALIZED_PATCH *serpatch; if ( (PG_NARGS()>2) && (!PG_ARGISNULL(2)) ) { - pc_typmod = PG_GETARG_INT32(2); + typmod = PG_GETARG_INT32(2); + pcid = pcid_from_typmod(typmod); } /* Empty string. */ @@ -99,7 +121,8 @@ Datum pcpatch_in(PG_FUNCTION_ARGS) if ( str[0] == '0' ) { /* Hex-encoded binary */ - patch = pc_patch_from_hexwkb(str, strlen(str)); + patch = pc_patch_from_hexwkb(str, strlen(str), fcinfo); + pcid_consistent(patch->schema->pcid, pcid); serpatch = pc_patch_serialize(patch); pc_patch_free(patch); } @@ -119,7 +142,7 @@ Datum pcpatch_out(PG_FUNCTION_ARGS) char *hexwkb = NULL; serpatch = (SERIALIZED_PATCH*)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); - patch = pc_patch_deserialize(serpatch); + patch = pc_patch_deserialize(serpatch, fcinfo); hexwkb = pc_patch_to_hexwkb(patch); pc_patch_free(patch); PG_RETURN_CSTRING(hexwkb); @@ -150,7 +173,7 @@ Datum pcschema_get_ndims(PG_FUNCTION_ARGS) { int ndims; uint32 pcid = PG_GETARG_INT32(0); - PCSCHEMA *schema = pc_schema_get_by_id(pcid); + PCSCHEMA *schema = pc_schema_from_pcid(pcid, fcinfo); if ( ! schema ) elog(ERROR, "unable to load schema for pcid = %d", pcid); @@ -172,7 +195,7 @@ Datum pcpoint_from_double_array(PG_FUNCTION_ARGS) int i; float8 *vals; PCPOINT *pt; - PCSCHEMA *schema = pc_schema_get_by_id(pcid); + PCSCHEMA *schema = pc_schema_from_pcid(pcid, fcinfo); SERIALIZED_POINT *serpt; if ( ! schema ) @@ -214,7 +237,7 @@ Datum pcpoint_as_text(PG_FUNCTION_ARGS) SERIALIZED_POINT *serpt = PG_GETARG_SERPOINT_P(0); text *txt; char *str; - PCPOINT *pt = pc_point_deserialize(serpt); + PCPOINT *pt = pc_point_deserialize(serpt, fcinfo); if ( ! pt ) PG_RETURN_NULL(); @@ -231,7 +254,7 @@ Datum pcpatch_as_text(PG_FUNCTION_ARGS) SERIALIZED_PATCH *serpatch = PG_GETARG_SERPATCH_P(0); text *txt; char *str; - PCPATCH *patch = pc_patch_deserialize(serpatch); + PCPATCH *patch = pc_patch_deserialize(serpatch, fcinfo); if ( ! patch ) PG_RETURN_NULL(); @@ -246,11 +269,11 @@ 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; + uint8 *bytes; size_t bytes_size; bytea *wkb; size_t wkb_size; - PCPOINT *pt = pc_point_deserialize(serpt); + PCPOINT *pt = pc_point_deserialize(serpt, fcinfo); if ( ! pt ) PG_RETURN_NULL(); @@ -271,11 +294,11 @@ 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; + uint8 *bytes; size_t bytes_size; bytea *wkb; size_t wkb_size; - PCPATCH *pa = pc_patch_deserialize(serpatch); + PCPATCH *pa = pc_patch_deserialize(serpatch, fcinfo); if ( ! pa ) PG_RETURN_NULL(); @@ -292,3 +315,70 @@ Datum pcpatch_bytea_envelope(PG_FUNCTION_ARGS) PG_RETURN_BYTEA_P(wkb); } +PG_FUNCTION_INFO_V1(pc_typmod_in); +Datum pc_typmod_in(PG_FUNCTION_ARGS) +{ + uint32 typmod = 0; + Datum *elem_values; + int n = 0; + int i = 0; + ArrayType *arr = (ArrayType *) DatumGetPointer(PG_GETARG_DATUM(0)); + + if (ARR_ELEMTYPE(arr) != CSTRINGOID) + ereport(ERROR, + (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), + errmsg("typmod array must be type cstring[]"))); + + if (ARR_NDIM(arr) != 1) + ereport(ERROR, + (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), + errmsg("typmod array must be one-dimensional"))); + + if (ARR_HASNULL(arr)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("typmod array must not contain nulls"))); + + if (ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr)) > 1) + ereport(ERROR, + (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), + errmsg("typmod array must have one element"))); + + deconstruct_array(arr, + CSTRINGOID, -2, false, 'c', /* hardwire cstring representation details */ + &elem_values, NULL, &n); + + for (i = 0; i < n; i++) + { + if ( i == 0 ) /* PCID */ + { + char *s = DatumGetCString(elem_values[i]); + typmod = pg_atoi(s, sizeof(int32), '\0'); + } + } + + PG_RETURN_INT32(typmod); +} + +PG_FUNCTION_INFO_V1(pc_typmod_out); +Datum pc_typmod_out(PG_FUNCTION_ARGS) +{ + char *str = (char*)palloc(64); + uint32 typmod = PG_GETARG_INT32(0); + uint32 pcid = pcid_from_typmod(typmod); + + + /* No PCID value? Then no typmod at all. Return empty string. */ + if ( ! pcid ) + { + str[0] = '\0'; + PG_RETURN_CSTRING(str); + } + else + { + sprintf(str, "(%u)", pcid); + PG_RETURN_CSTRING(str); + } +} + + diff --git a/pgsql/pc_pgsql.c b/pgsql/pc_pgsql.c index 5bd2575..040dd29 100644 --- a/pgsql/pc_pgsql.c +++ b/pgsql/pc_pgsql.c @@ -100,21 +100,29 @@ _PG_fini(void) elog(LOG, "Pointcloud (%s) module unloaded", POINTCLOUD_VERSION); } +/* Mask pcid from bottom of typmod */ +uint32 pcid_from_typmod(const int32 typmod) +{ + if ( typmod == -1 ) + return 0; + else + return (typmod & 0x0000FFFF); +} /********************************************************************************** * PCPOINT WKB Handling */ PCPOINT * -pc_point_from_hexwkb(const char *hexwkb, size_t hexlen) +pc_point_from_hexwkb(const char *hexwkb, size_t hexlen, FunctionCallInfoData *fcinfo) { PCPOINT *pt; PCSCHEMA *schema; - uint32_t pcid; - uint8_t *wkb = bytes_from_hexbytes(hexwkb, hexlen); + uint32 pcid; + uint8 *wkb = bytes_from_hexbytes(hexwkb, hexlen); size_t wkblen = hexlen/2; pcid = wkb_get_pcid(wkb); - schema = pc_schema_get_by_id(pcid); + schema = pc_schema_from_pcid(pcid, fcinfo); pt = pc_point_from_wkb(schema, wkb, wkblen); pfree(wkb); return pt; @@ -123,7 +131,7 @@ pc_point_from_hexwkb(const char *hexwkb, size_t hexlen) char * pc_point_to_hexwkb(const PCPOINT *pt) { - uint8_t *wkb; + uint8 *wkb; size_t wkb_size; char *hexwkb; @@ -139,15 +147,15 @@ pc_point_to_hexwkb(const PCPOINT *pt) */ PCPATCH * -pc_patch_from_hexwkb(const char *hexwkb, size_t hexlen) +pc_patch_from_hexwkb(const char *hexwkb, size_t hexlen, FunctionCallInfoData *fcinfo) { PCPATCH *patch; PCSCHEMA *schema; - uint32_t pcid; - uint8_t *wkb = bytes_from_hexbytes(hexwkb, hexlen); + uint32 pcid; + uint8 *wkb = bytes_from_hexbytes(hexwkb, hexlen); size_t wkblen = hexlen/2; pcid = wkb_get_pcid(wkb); - schema = pc_schema_get_by_id(pcid); + schema = pc_schema_from_pcid(pcid, fcinfo); patch = pc_patch_from_wkb(schema, wkb, wkblen); pfree(wkb); return patch; @@ -156,7 +164,7 @@ pc_patch_from_hexwkb(const char *hexwkb, size_t hexlen) char * pc_patch_to_hexwkb(const PCPATCH *patch) { - uint8_t *wkb; + uint8 *wkb; size_t wkb_size; char *hexwkb; @@ -174,8 +182,8 @@ pc_patch_to_hexwkb(const PCPATCH *patch) /** * TODO: Back this routine with a statement level memory cache. */ -PCSCHEMA * -pc_schema_get_by_id(uint32_t pcid) +static PCSCHEMA * +pc_schema_from_pcid_uncached(uint32 pcid) { char sql[256]; char *xml, *xml_spi, *srid_spi; @@ -186,7 +194,7 @@ pc_schema_get_by_id(uint32_t pcid) if (SPI_OK_CONNECT != SPI_connect ()) { SPI_finish(); - elog(ERROR, "pc_schema_get_by_id: could not connect to SPI manager"); + elog(ERROR, "pc_schema_from_pcid: could not connect to SPI manager"); return NULL; } @@ -197,7 +205,7 @@ pc_schema_get_by_id(uint32_t pcid) if ( err < 0 ) { SPI_finish(); - elog(ERROR, "pc_schema_get_by_id: error (%d) executing query: %s", err, sql); + elog(ERROR, "pc_schema_from_pcid: error (%d) executing query: %s", err, sql); return NULL; } @@ -247,6 +255,13 @@ pc_schema_get_by_id(uint32_t pcid) return schema; } +PCSCHEMA * +pc_schema_from_pcid(uint32 pcid, FunctionCallInfoData *fcinfo) +{ + return pc_schema_from_pcid_uncached(pcid); +} + + /********************************************************************************** * SERIALIZATION/DESERIALIZATION UTILITIES @@ -264,10 +279,10 @@ pc_point_serialize(const PCPOINT *pcpt) } PCPOINT * -pc_point_deserialize(const SERIALIZED_POINT *serpt) +pc_point_deserialize(const SERIALIZED_POINT *serpt, FunctionCallInfoData *fcinfo) { PCPOINT *pcpt; - PCSCHEMA *schema = pc_schema_get_by_id(serpt->pcid); + PCSCHEMA *schema = pc_schema_from_pcid(serpt->pcid, fcinfo); size_t pgsize = VARSIZE(serpt) + 1 - sizeof(SERIALIZED_POINT); /* * Big problem, the size on disk doesn't match what we expect, @@ -310,14 +325,14 @@ pc_patch_serialize(const PCPATCH *pcpch) } PCPATCH * -pc_patch_deserialize(const SERIALIZED_PATCH *serpatch) +pc_patch_deserialize(const SERIALIZED_PATCH *serpatch, FunctionCallInfoData *fcinfo) { PCPATCH *patch; - PCSCHEMA *schema = pc_schema_get_by_id(serpatch->pcid); + PCSCHEMA *schema = pc_schema_from_pcid(serpatch->pcid, fcinfo); /* Reference the external data */ patch = pcalloc(sizeof(PCPATCH)); - patch->data = (uint8_t*)serpatch->data; + patch->data = (uint8*)serpatch->data; patch->datasize = VARSIZE(serpatch) - sizeof(SERIALIZED_PATCH) + 1; /* Set up basic info */ diff --git a/pgsql/pc_pgsql.h b/pgsql/pc_pgsql.h index 82f2d24..1b9a562 100644 --- a/pgsql/pc_pgsql.h +++ b/pgsql/pc_pgsql.h @@ -63,18 +63,19 @@ SERIALIZED_PATCH; /* PGSQL / POINTCLOUD UTILITY FUNCTIONS */ +uint32 pcid_from_typmod(const int32 typmod); /** Look-up the PCID in the POINTCLOUD_FORMATS table, and construct a PC_SCHEMA from the XML therein */ -PCSCHEMA* pc_schema_get_by_id(uint32_t pcid); +PCSCHEMA* pc_schema_from_pcid(uint32_t pcid, FunctionCallInfoData *fcinfo); /** Turn a PCPOINT into a byte buffer suitable for saving in PgSQL */ SERIALIZED_POINT* pc_point_serialize(const PCPOINT *pcpt); /** Turn a byte buffer into a PCPOINT for processing */ -PCPOINT* pc_point_deserialize(const SERIALIZED_POINT *serpt); +PCPOINT* pc_point_deserialize(const SERIALIZED_POINT *serpt, FunctionCallInfoData *fcinfo); /** Create a new readwrite PCPOINT from a hex string */ -PCPOINT* pc_point_from_hexwkb(const char *hexwkb, size_t hexlen); +PCPOINT* pc_point_from_hexwkb(const char *hexwkb, size_t hexlen, FunctionCallInfoData *fcinfo); /** Create a hex representation of a PCPOINT */ char* pc_point_to_hexwkb(const PCPOINT *pt); @@ -84,10 +85,10 @@ char* pc_point_to_hexwkb(const PCPOINT *pt); SERIALIZED_PATCH* pc_patch_serialize(const PCPATCH *patch); /** Turn a byte buffer into a PCPATCH for processing */ -PCPATCH* pc_patch_deserialize(const SERIALIZED_PATCH *serpatch); +PCPATCH* pc_patch_deserialize(const SERIALIZED_PATCH *serpatch, FunctionCallInfoData *fcinfo); /** Create a new readwrite PCPATCH from a hex string */ -PCPATCH* pc_patch_from_hexwkb(const char *hexwkb, size_t hexlen); +PCPATCH* pc_patch_from_hexwkb(const char *hexwkb, size_t hexlen, FunctionCallInfoData *fcinfo); /** Create a hex representation of a PCPOINT */ char* pc_patch_to_hexwkb(const PCPATCH *patch); diff --git a/pgsql/pointcloud--1.0.sql b/pgsql/pointcloud--1.0.sql index 64ee171..8da99c9 100644 --- a/pgsql/pointcloud--1.0.sql +++ b/pgsql/pointcloud--1.0.sql @@ -14,7 +14,10 @@ CREATE OR REPLACE FUNCTION PC_SchemaIsValid(xml text) -- Metadata table describing contents of pcpoints CREATE TABLE pointcloud_formats ( - pcid INTEGER PRIMARY KEY CHECK (pcid > 0), + pcid INTEGER PRIMARY KEY + -- PCID == 0 is unknown + -- PCID > 2^16 is reserved to leave space in typmod + CHECK (pcid > 0 AND pcid < 65536), srid INTEGER, -- REFERENCES spatial_ref_sys(srid) schema TEXT CHECK ( PC_SchemaIsValid(schema) ) @@ -27,6 +30,17 @@ CREATE OR REPLACE FUNCTION PC_SchemaGetNDims(pcid integer) RETURNS integer AS 'MODULE_PATHNAME','pcschema_get_ndims' LANGUAGE 'c' IMMUTABLE STRICT; +-- Read typmod number from string +CREATE OR REPLACE FUNCTION pc_typmod_in(cstring[]) + RETURNS integer + AS 'MODULE_PATHNAME','pc_typmod_in' + LANGUAGE 'c' IMMUTABLE STRICT; + +-- Write typmod number to string +CREATE OR REPLACE FUNCTION pc_typmod_out(integer) + RETURNS cstring + AS 'MODULE_PATHNAME','pc_typmod_out' + LANGUAGE 'c' IMMUTABLE STRICT; ------------------------------------------------------------------- @@ -47,8 +61,8 @@ CREATE TYPE pcpoint ( output = pcpoint_out, -- send = geometry_send, -- receive = geometry_recv, - -- typmod_in = geometry_typmod_in, - -- typmod_out = geometry_typmod_out, + typmod_in = pc_typmod_in, + typmod_out = pc_typmod_out, -- delimiter = ':', -- alignment = double, -- analyze = geometry_analyze, @@ -89,8 +103,8 @@ CREATE TYPE pcpatch ( output = pcpatch_out, -- send = geometry_send, -- receive = geometry_recv, - -- typmod_in = geometry_typmod_in, - -- typmod_out = geometry_typmod_out, + typmod_in = pc_typmod_in, + typmod_out = pc_typmod_out, -- delimiter = ':', -- alignment = double, -- analyze = geometry_analyze, diff --git a/pgsql/sql/pointcloud.sql b/pgsql/sql/pointcloud.sql index d5dbb19..877ced4 100644 --- a/pgsql/sql/pointcloud.sql +++ b/pgsql/sql/pointcloud.sql @@ -50,10 +50,12 @@ VALUES (1, 0, ); CREATE TABLE IF NOT EXISTS pt_test ( - pt PCPOINT + pt PCPOINT(1) ); +\d pt_test DELETE FROM pt_test; +INSERT INTO pt_test (pt) VALUES ('00000000020000000100000002000000030004'); INSERT INTO pt_test (pt) VALUES ('00000000010000000100000002000000030004'); INSERT INTO pt_test (pt) VALUES ('00000000010000000200000003000000030005'); INSERT INTO pt_test (pt) VALUES ('00000000010000000300000004000000030006'); @@ -64,9 +66,12 @@ SELECT Sum(PC_Get(pt, 'y')) FROM pt_test; SELECT PC_AsText(pt) FROM pt_test; CREATE TABLE IF NOT EXISTS pa_test ( - pa PCPATCH + pa PCPATCH(1) ); +\d pa_test + DELETE FROM pa_test; +INSERT INTO pa_test (pa) VALUES ('0000000002000000000000000200000002000000030000000500060000000200000003000000050008'); INSERT INTO pa_test (pa) VALUES ('0000000001000000000000000200000002000000030000000500060000000200000003000000050008'); INSERT INTO pa_test (pa) VALUES ('000000000100000000000000020000000600000007000000050006000000090000000A00000005000A'); SELECT PC_AsText(pa) FROM pa_test;