mirror of
https://github.com/pgpointcloud/pointcloud.git
synced 2025-12-08 20:36:04 +00:00
Add in XML function to pgsql
Set up XML validation tests
This commit is contained in:
parent
7de51b4267
commit
e8a7788d00
@ -5,7 +5,6 @@ CPPFLAGS = $(XML2_CPPFLAGS)
|
||||
LDFLAGS = $(XML2_LDFLAGS)
|
||||
|
||||
OBJS = \
|
||||
pc_core.o \
|
||||
pc_mem.o \
|
||||
pc_patch.o \
|
||||
pc_point.o \
|
||||
|
||||
45
libpc/cunit/data/simple-schema.xml
Normal file
45
libpc/cunit/data/simple-schema.xml
Normal 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>
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -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 */
|
||||
@ -1,12 +0,0 @@
|
||||
/***********************************************************************
|
||||
* pc_core.c
|
||||
*
|
||||
* Core routines for point clouds
|
||||
*
|
||||
* Portions Copyright (c) 2012, OpenGeo
|
||||
*
|
||||
***********************************************************************/
|
||||
|
||||
#include "pc_api_internal.h"
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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.
|
||||
*/
|
||||
|
||||
@ -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', '');
|
||||
Loading…
x
Reference in New Issue
Block a user