Add in XML function to pgsql

Set up XML validation tests
This commit is contained in:
Paul Ramsey 2013-01-18 16:15:39 -08:00
parent 7de51b4267
commit e8a7788d00
10 changed files with 198 additions and 32 deletions

View File

@ -5,7 +5,6 @@ CPPFLAGS = $(XML2_CPPFLAGS)
LDFLAGS = $(XML2_LDFLAGS)
OBJS = \
pc_core.o \
pc_mem.o \
pc_patch.o \
pc_point.o \

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<pc:PointCloudSchema xmlns:pc="http://pointcloud.org/schemas/PC/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<pc:dimension>
<pc:position>1</pc:position>
<pc:size>4</pc:size>
<pc:description>X coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>X</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>2</pc:position>
<pc:size>4</pc:size>
<pc:description>Y coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>Y</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>2</pc:position>
<pc:size>4</pc:size>
<pc:description>Z coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>Z</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>4</pc:position>
<pc:size>2</pc:size>
<pc:description>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.</pc:description>
<pc:name>Intensity</pc:name>
<pc:interpretation>uint16_t</pc:interpretation>
<pc:scale>1</pc:scale>
</pc:dimension>
<pc:metadata>
<Metadata name="compression"></Metadata>
<Metadata name="ght_xmin"></Metadata>
<Metadata name="ght_ymin"></Metadata>
<Metadata name="ght_xmax"></Metadata>
<Metadata name="ght_ymax"></Metadata>
<Metadata name="ght_keylength"></Metadata>
<Metadata name="ght_depth"></Metadata>
<Metadata name="spatialreference" type="id">4326</Metadata>
</pc:metadata>
</pc:PointCloudSchema>

View File

@ -14,6 +14,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdarg.h>
#include "hashtable.h"
@ -21,6 +22,8 @@
* DATA STRUCTURES
*/
#define POINTCLOUD_VERSION "1.0"
/**
* Compression types for PCPOINTS in a PCPATCH
*/

View File

@ -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 */

View File

@ -1,12 +0,0 @@
/***********************************************************************
* pc_core.c
*
* Core routines for point clouds
*
* Portions Copyright (c) 2012, OpenGeo
*
***********************************************************************/
#include "pc_api_internal.h"

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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.
*/

View File

@ -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', '');