diff --git a/libpc/Makefile b/libpc/Makefile
index 2543cac..c6a2461 100644
--- a/libpc/Makefile
+++ b/libpc/Makefile
@@ -5,7 +5,6 @@ CPPFLAGS = $(XML2_CPPFLAGS)
LDFLAGS = $(XML2_LDFLAGS)
OBJS = \
- pc_core.o \
pc_mem.o \
pc_patch.o \
pc_point.o \
diff --git a/libpc/cunit/data/simple-schema.xml b/libpc/cunit/data/simple-schema.xml
new file mode 100644
index 0000000..26a55a1
--- /dev/null
+++ b/libpc/cunit/data/simple-schema.xml
@@ -0,0 +1,45 @@
+
+
+
+ 1
+ 4
+ X coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.
+ X
+ int32_t
+ 0.01
+
+
+ 2
+ 4
+ Y coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.
+ Y
+ int32_t
+ 0.01
+
+
+ 2
+ 4
+ Z coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.
+ Z
+ int32_t
+ 0.01
+
+
+ 4
+ 2
+ The intensity value is the integer representation of the pulse return magnitude. This value is optional and system specific. However, it should always be included if available.
+ Intensity
+ uint16_t
+ 1
+
+
+
+
+
+
+
+
+
+ 4326
+
+
diff --git a/libpc/pc_api.h b/libpc/pc_api.h
index 470fb41..833ad23 100644
--- a/libpc/pc_api.h
+++ b/libpc/pc_api.h
@@ -14,6 +14,7 @@
#include
#include
#include
+#include
#include "hashtable.h"
@@ -21,6 +22,8 @@
* DATA STRUCTURES
*/
+#define POINTCLOUD_VERSION "1.0"
+
/**
* Compression types for PCPOINTS in a PCPATCH
*/
diff --git a/libpc/pc_api_internal.h b/libpc/pc_api_internal.h
index 2cb008f..a15cc7b 100644
--- a/libpc/pc_api_internal.h
+++ b/libpc/pc_api_internal.h
@@ -87,6 +87,8 @@ uint8_t* bytes_from_hexbytes(const char *hexbuf, size_t hexsize);
/** Turn a binary buffer into a hex string */
char* hexbytes_from_bytes(const uint8_t *bytebuf, size_t bytesize);
+/** Copy a string within the global memory management context */
+char* pcstrdup(const char *str);
#endif /* _PC_API_INTERNAL_H */
\ No newline at end of file
diff --git a/libpc/pc_core.c b/libpc/pc_core.c
deleted file mode 100644
index 94a6f1a..0000000
--- a/libpc/pc_core.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/***********************************************************************
-* pc_core.c
-*
-* Core routines for point clouds
-*
-* Portions Copyright (c) 2012, OpenGeo
-*
-***********************************************************************/
-
-#include "pc_api_internal.h"
-
-
diff --git a/libpc/pc_mem.c b/libpc/pc_mem.c
index e33f308..32394f1 100644
--- a/libpc/pc_mem.c
+++ b/libpc/pc_mem.c
@@ -62,7 +62,7 @@ default_msg_handler(const char *label, const char *fmt, va_list ap)
static void
default_info_handler(const char *fmt, va_list ap)
{
- // default_msg_handler("INFO: ", fmt, ap);
+ default_msg_handler("INFO: ", fmt, ap);
}
static void
@@ -111,6 +111,14 @@ pcalloc(size_t size)
return mem;
}
+char *
+pcstrdup(const char *str)
+{
+ size_t len = strlen(str);
+ char *newstr = pcalloc(len + 1);
+ memcpy(newstr, str, len + 1);
+ return newstr;
+}
void *
pcrealloc(void * mem, size_t size)
diff --git a/libpc/pc_schema.c b/libpc/pc_schema.c
index 7f82da9..1f2344e 100644
--- a/libpc/pc_schema.c
+++ b/libpc/pc_schema.c
@@ -206,6 +206,7 @@ PCSCHEMA* pc_schema_from_xml(const char *xml_str)
xmlXPathContextPtr xpath_ctx;
xmlXPathObjectPtr xpath_obj;
xmlNodeSetPtr nodes;
+ PCSCHEMA *s = NULL;
size_t xml_size = strlen(xml_str);
static xmlChar *xpath_str = "/pc:PointCloudSchema/pc:dimension";
@@ -214,7 +215,10 @@ PCSCHEMA* pc_schema_from_xml(const char *xml_str)
xmlInitParser();
xml_doc = xmlReadMemory(xml_str, xml_size, NULL, NULL, 0);
if ( ! xml_doc )
- pcerror("Unable to parse XML");
+ {
+ xmlCleanupParser();
+ pcerror("unable to parse schema XML");
+ }
/* Capture the namespace */
xml_root = xmlDocGetRootElement(xml_doc);
@@ -224,7 +228,11 @@ PCSCHEMA* pc_schema_from_xml(const char *xml_str)
/* Create xpath evaluation context */
xpath_ctx = xmlXPathNewContext(xml_doc);
if( ! xpath_ctx )
- pcerror("Unable to create new XPath context");
+ {
+ xmlFreeDoc(xml_doc);
+ xmlCleanupParser();
+ pcerror("unable to create new XPath context to read schema XML");
+ }
/* Register the root namespace if there is one */
if ( xml_ns )
@@ -233,14 +241,19 @@ PCSCHEMA* pc_schema_from_xml(const char *xml_str)
/* Evaluate xpath expression */
xpath_obj = xmlXPathEvalExpression(xpath_str, xpath_ctx);
if( ! xpath_obj )
- pcerror("Unable to evaluate xpath expression \"%s\"", xpath_str);
+ {
+ xmlXPathFreeContext(xpath_ctx);
+ xmlFreeDoc(xml_doc);
+ xmlCleanupParser();
+ pcerror("unable to evaluate xpath expression \"%s\" against schema XML", xpath_str);
+ }
/* Iterate on the dimensions we found */
if ( nodes = xpath_obj->nodesetval )
{
int ndims = nodes->nodeNr;
int i;
- PCSCHEMA *s = pc_schema_new(ndims);
+ s = pc_schema_new(ndims);
for ( i = 0; i < ndims; i++ )
{
@@ -249,7 +262,6 @@ PCSCHEMA* pc_schema_from_xml(const char *xml_str)
{
xmlNodePtr cur = nodes->nodeTab[i];
xmlNodePtr child;
- int position = -1;
PCDIMENSION *d = pc_dimension_new();
int is_x = 0, is_y = 0;
@@ -272,10 +284,10 @@ PCSCHEMA* pc_schema_from_xml(const char *xml_str)
{
is_y = 1;
}
- d->name = strdup(child->children->content);
+ d->name = pcstrdup(child->children->content);
}
else if ( strcmp(child->name, "description") == 0 )
- d->description = strdup(child->children->content);
+ d->description = pcstrdup(child->children->content);
else if ( strcmp(child->name, "size") == 0 )
d->size = atoi(child->children->content);
else if ( strcmp(child->name, "active") == 0 )
@@ -287,22 +299,32 @@ PCSCHEMA* pc_schema_from_xml(const char *xml_str)
else if ( strcmp(child->name, "scale") == 0 )
d->scale = atof(child->children->content);
else
- pcinfo("Unhandled schema type element \"%s\" encountered", child->name);
+ pcinfo("unhandled schema type element \"%s\" encountered", child->name);
}
}
/* Store the dimension in the schema */
if ( d->position >= 0 && d->position < ndims )
{
+ if ( s->dims[d->position] )
+ {
+ xmlXPathFreeObject(xpath_obj);
+ xmlXPathFreeContext(xpath_ctx);
+ xmlFreeDoc(xml_doc);
+ xmlCleanupParser();
+ pcerror("schema dimension at position \"%d\" is declared twice", d->position + 1, ndims);
+ }
s->dims[d->position] = d;
d->size = pc_interpretation_size(d->interpretation);
s->size += d->size;
if ( is_x ) { s->x_position = d->position; }
if ( is_y ) { s->y_position = d->position; }
- hashtable_insert(s->namehash, strdup(d->name), d);
+ hashtable_insert(s->namehash, pcstrdup(d->name), d);
}
else
- pcwarn("Dimension at position \"%d\" discarded", position);
+ {
+ pcwarn("schema dimension states position \"%d\", but number of XML dimensions is \"%d\"", d->position + 1, ndims);
+ }
}
}
@@ -310,7 +332,13 @@ PCSCHEMA* pc_schema_from_xml(const char *xml_str)
pc_schema_calculate_byteoffsets(s);
}
- return NULL;
+
+ xmlXPathFreeObject(xpath_obj);
+ xmlXPathFreeContext(xpath_ctx);
+ xmlFreeDoc(xml_doc);
+ xmlCleanupParser();
+
+ return s;
}
uint32_t
@@ -320,13 +348,13 @@ pc_schema_is_valid(const PCSCHEMA *s)
if ( s->x_position < 0 )
{
- pcwarn("schema does not include X coordinate");
+ pcwarn("schema does not include an X coordinate");
return PC_FALSE;
}
if ( s->y_position < 0 )
{
- pcwarn("schema does not include Y coordinate");
+ pcwarn("schema does not include a Y coordinate");
return PC_FALSE;
}
@@ -340,7 +368,7 @@ pc_schema_is_valid(const PCSCHEMA *s)
{
if ( ! s->dims[i] )
{
- pcwarn("schema has null dimension at position %d", i);
+ pcwarn("schema is missing a dimension at position %d", i);
return PC_FALSE;
}
}
diff --git a/pgsql/pc_inout.c b/pgsql/pc_inout.c
index a17b206..f3022d6 100644
--- a/pgsql/pc_inout.c
+++ b/pgsql/pc_inout.c
@@ -1,9 +1,12 @@
#include "pc_pgsql.h" /* Common PgSQL support for our type */
+/* In/out functions */
Datum pcpoint_in(PG_FUNCTION_ARGS);
Datum pcpoint_out(PG_FUNCTION_ARGS);
-Datum pcschema_is_valid(PG_FUNCTION_ARGS);
+
+/* Other SQL functions */
+Datum PC_SchemaIsValid(PG_FUNCTION_ARGS);
@@ -39,8 +42,8 @@ Datum pcschema_is_valid(PG_FUNCTION_ARGS);
// }
//
-PG_FUNCTION_INFO_V1(pcschema_is_valid);
-Datum pcschema_is_valid(PG_FUNCTION_ARGS)
+PG_FUNCTION_INFO_V1(PC_SchemaIsValid);
+Datum PC_SchemaIsValid(PG_FUNCTION_ARGS)
{
bool valid;
text *xml = PG_GETARG_TEXT_P(0);
@@ -49,7 +52,10 @@ Datum pcschema_is_valid(PG_FUNCTION_ARGS)
pfree(xmlstr);
if ( ! schema )
+ {
+ elog(NOTICE, "pc_schema_from_xml returned NULL");
PG_RETURN_BOOL(FALSE);
+ }
valid = pc_schema_is_valid(schema);
PG_RETURN_BOOL(valid);
diff --git a/pgsql/pc_pgsql.c b/pgsql/pc_pgsql.c
index 1b22966..91e32e2 100644
--- a/pgsql/pc_pgsql.c
+++ b/pgsql/pc_pgsql.c
@@ -2,6 +2,85 @@
#include "pc_pgsql.h"
#include "executor/spi.h"
+PG_MODULE_MAGIC;
+
+
+static void *
+pgsql_alloc(size_t size)
+{
+ void * result;
+ result = palloc(size);
+
+ if ( ! result )
+ {
+ ereport(ERROR, (errmsg_internal("Out of virtual memory")));
+ return NULL;
+ }
+ return result;
+}
+
+static void *
+pgsql_realloc(void *mem, size_t size)
+{
+ void * result;
+ result = repalloc(mem, size);
+ return result;
+}
+
+static void
+pgsql_free(void *ptr)
+{
+ pfree(ptr);
+}
+
+static void
+pgsql_msg_handler(int sig, const char *fmt, va_list ap)
+{
+#define MSG_MAXLEN 1024
+ char msg[MSG_MAXLEN] = {0};
+ vsnprintf(msg, MSG_MAXLEN, fmt, ap);
+ msg[MSG_MAXLEN-1] = '\0';
+ ereport(sig, (errmsg_internal("%s", msg)));
+}
+
+static void
+pgsql_error(const char *fmt, va_list ap)
+{
+ pgsql_msg_handler(ERROR, fmt, ap);
+}
+
+static void
+pgsql_warn(const char *fmt, va_list ap)
+{
+ pgsql_msg_handler(WARNING, fmt, ap);
+}
+
+static void
+pgsql_info(const char *fmt, va_list ap)
+{
+ pgsql_msg_handler(NOTICE, fmt, ap);
+}
+
+/* Module loading callback */
+void _PG_init(void);
+void
+_PG_init(void)
+{
+ elog(LOG, "Pointcloud (%s) module loaded", POINTCLOUD_VERSION);
+ pc_set_handlers(pgsql_alloc, pgsql_realloc,
+ pgsql_free, pgsql_error,
+ pgsql_info, pgsql_warn);
+}
+
+/* Module unload callback */
+void _PG_fini(void);
+void
+_PG_fini(void)
+{
+ elog(LOG, "Pointcloud (%s) module unloaded", POINTCLOUD_VERSION);
+}
+
+
/**
* TODO: Back this routine with a statement level memory cache.
*/
diff --git a/pgsql/pointcloud--1.0.sql b/pgsql/pointcloud--1.0.sql
index af45ab1..381dffb 100644
--- a/pgsql/pointcloud--1.0.sql
+++ b/pgsql/pointcloud--1.0.sql
@@ -2,11 +2,19 @@
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION pointcloud" to load this file. \quit
+
+-- 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'
+ LANGUAGE 'c' IMMUTABLE STRICT;
+
-- Metadata table describing contents of pcpoints
CREATE TABLE pointcloud_formats (
pcid INTEGER PRIMARY KEY,
srid INTEGER, -- REFERENCES spatial_ref_sys(srid)
- schema XML
+ schema TEXT
+ CHECK ( PC_SchemaIsValid(schema) )
);
+
-- Register pointcloud_formats table so the contents are included in pg_dump output
SELECT pg_catalog.pg_extension_config_dump('pointcloud_formats', '');
\ No newline at end of file