mirror of
https://github.com/pgpointcloud/pointcloud.git
synced 2025-12-08 20:36:04 +00:00
1227 lines
32 KiB
C
1227 lines
32 KiB
C
/***********************************************************************
|
|
* pc_access.c
|
|
*
|
|
* Accessor/aggregate functions for points and patches in PgSQL.
|
|
*
|
|
* PgSQL Pointcloud is free and open source software provided
|
|
* by the Government of Canada
|
|
* Copyright (c) 2013 Natural Resources Canada
|
|
*
|
|
***********************************************************************/
|
|
|
|
#include "pc_pgsql.h" /* Common PgSQL support for our type */
|
|
#include "utils/numeric.h"
|
|
|
|
#include "funcapi.h"
|
|
#include "lib/stringinfo.h"
|
|
#include "pc_api_internal.h" /* for pcpatch_summary */
|
|
|
|
/* cstring array utility functions */
|
|
const char **array_to_cstring_array(ArrayType *array, int *size);
|
|
void pc_cstring_array_free(const char **array, int nelems);
|
|
|
|
/* General SQL functions */
|
|
Datum pcpoint_get_value(PG_FUNCTION_ARGS);
|
|
Datum pcpoint_get_values(PG_FUNCTION_ARGS);
|
|
Datum pcpatch_from_pcpoint_array(PG_FUNCTION_ARGS);
|
|
Datum pcpatch_from_float_array(PG_FUNCTION_ARGS);
|
|
Datum pcpatch_from_pcpatch_array(PG_FUNCTION_ARGS);
|
|
Datum pcpatch_uncompress(PG_FUNCTION_ARGS);
|
|
Datum pcpatch_compress(PG_FUNCTION_ARGS);
|
|
Datum pcpatch_numpoints(PG_FUNCTION_ARGS);
|
|
Datum pcpatch_pointn(PG_FUNCTION_ARGS);
|
|
Datum pcpatch_range(PG_FUNCTION_ARGS);
|
|
Datum pcpatch_pcid(PG_FUNCTION_ARGS);
|
|
Datum pcpatch_summary(PG_FUNCTION_ARGS);
|
|
Datum pcpatch_compression(PG_FUNCTION_ARGS);
|
|
Datum pcpatch_intersects(PG_FUNCTION_ARGS);
|
|
Datum pcpatch_get_stat(PG_FUNCTION_ARGS);
|
|
Datum pcpatch_filter(PG_FUNCTION_ARGS);
|
|
Datum pcpatch_sort(PG_FUNCTION_ARGS);
|
|
Datum pcpatch_is_sorted(PG_FUNCTION_ARGS);
|
|
Datum pcpatch_size(PG_FUNCTION_ARGS);
|
|
Datum pcpoint_size(PG_FUNCTION_ARGS);
|
|
Datum pcpoint_pcid(PG_FUNCTION_ARGS);
|
|
Datum pc_version(PG_FUNCTION_ARGS);
|
|
Datum pc_pgsql_version(PG_FUNCTION_ARGS);
|
|
Datum pc_libxml2_version(PG_FUNCTION_ARGS);
|
|
Datum pc_lazperf_enabled(PG_FUNCTION_ARGS);
|
|
|
|
/* Generic aggregation functions */
|
|
Datum pointcloud_agg_transfn(PG_FUNCTION_ARGS);
|
|
Datum pointcloud_abs_in(PG_FUNCTION_ARGS);
|
|
Datum pointcloud_abs_out(PG_FUNCTION_ARGS);
|
|
|
|
/* Point finalizers */
|
|
Datum pcpoint_agg_final_pcpatch(PG_FUNCTION_ARGS);
|
|
Datum pcpoint_agg_final_array(PG_FUNCTION_ARGS);
|
|
|
|
/* Patch finalizers */
|
|
Datum pcpatch_agg_final_array(PG_FUNCTION_ARGS);
|
|
Datum pcpatch_agg_final_pcpatch(PG_FUNCTION_ARGS);
|
|
|
|
/* Deaggregation functions */
|
|
Datum pcpatch_unnest(PG_FUNCTION_ARGS);
|
|
|
|
/**
|
|
* Read a named dimension from a PCPOINT
|
|
* PC_Get(point pcpoint, dimname text) returns Numeric
|
|
*/
|
|
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);
|
|
char *dim_str;
|
|
float8 double_result;
|
|
|
|
PCSCHEMA *schema = pc_schema_from_pcid(serpt->pcid, fcinfo);
|
|
PCPOINT *pt = pc_point_deserialize(serpt, schema);
|
|
if (!pt)
|
|
PG_RETURN_NULL();
|
|
|
|
dim_str = text_to_cstring(dim_name);
|
|
if (!pc_point_get_double_by_name(pt, dim_str, &double_result))
|
|
{
|
|
pc_point_free(pt);
|
|
elog(ERROR, "dimension \"%s\" does not exist in schema", dim_str);
|
|
}
|
|
pfree(dim_str);
|
|
pc_point_free(pt);
|
|
PG_RETURN_DATUM(
|
|
DirectFunctionCall1(float8_numeric, Float8GetDatum(double_result)));
|
|
}
|
|
|
|
/**
|
|
* Returns all the values of a point as a double precision array
|
|
* PC_Get(point pcpoint) returns Float8[]
|
|
*/
|
|
PG_FUNCTION_INFO_V1(pcpoint_get_values);
|
|
Datum pcpoint_get_values(PG_FUNCTION_ARGS)
|
|
{
|
|
SERIALIZED_POINT *serpt;
|
|
ArrayType *result;
|
|
PCSCHEMA *schema;
|
|
PCPOINT *pt;
|
|
Datum *elems;
|
|
int i;
|
|
double *vals;
|
|
|
|
serpt = PG_GETARG_SERPOINT_P(0);
|
|
schema = pc_schema_from_pcid(serpt->pcid, fcinfo);
|
|
pt = pc_point_deserialize(serpt, schema);
|
|
if (!pt)
|
|
PG_RETURN_NULL();
|
|
|
|
elems = (Datum *)palloc(schema->ndims * sizeof(Datum));
|
|
vals = pc_point_to_double_array(pt);
|
|
i = schema->ndims;
|
|
while (i--)
|
|
elems[i] = Float8GetDatum(vals[i]);
|
|
pcfree(vals);
|
|
result = construct_array(elems, schema->ndims, FLOAT8OID, sizeof(float8),
|
|
FLOAT8PASSBYVAL, 'd');
|
|
|
|
pc_point_free(pt);
|
|
PG_RETURN_ARRAYTYPE_P(result);
|
|
}
|
|
|
|
static inline bool array_get_isnull(const bits8 *nullbitmap, int offset)
|
|
{
|
|
if (nullbitmap == NULL)
|
|
{
|
|
return false; /* assume not null */
|
|
}
|
|
if (nullbitmap[offset / 8] & (1 << (offset % 8)))
|
|
{
|
|
return false; /* not null */
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static PCPATCH *
|
|
#if PGSQL_VERSION < 120
|
|
pcpatch_from_point_array(ArrayType *array, FunctionCallInfoData *fcinfo)
|
|
#else
|
|
pcpatch_from_point_array(ArrayType *array, FunctionCallInfo fcinfo)
|
|
#endif
|
|
{
|
|
int nelems;
|
|
bits8 *bitmap;
|
|
size_t offset = 0;
|
|
int i;
|
|
uint32 pcid = 0;
|
|
PCPATCH *pa;
|
|
PCPOINTLIST *pl;
|
|
PCSCHEMA *schema = 0;
|
|
|
|
/* 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)
|
|
return NULL;
|
|
|
|
/* Make our holder */
|
|
pl = pc_pointlist_make(nelems);
|
|
|
|
offset = 0;
|
|
bitmap = ARR_NULLBITMAP(array);
|
|
for (i = 0; i < nelems; i++)
|
|
{
|
|
/* Only work on non-NULL entries in the array */
|
|
if (!array_get_isnull(bitmap, i))
|
|
{
|
|
SERIALIZED_POINT *serpt =
|
|
(SERIALIZED_POINT *)(ARR_DATA_PTR(array) + offset);
|
|
PCPOINT *pt;
|
|
|
|
if (!schema)
|
|
{
|
|
schema = pc_schema_from_pcid(serpt->pcid, fcinfo);
|
|
}
|
|
|
|
if (!pcid)
|
|
{
|
|
pcid = serpt->pcid;
|
|
}
|
|
else if (pcid != serpt->pcid)
|
|
{
|
|
elog(ERROR, "pcpatch_from_point_array: pcid mismatch (%d != %d)",
|
|
serpt->pcid, pcid);
|
|
}
|
|
|
|
pt = pc_point_deserialize(serpt, schema);
|
|
if (!pt)
|
|
{
|
|
elog(ERROR, "pcpatch_from_point_array: point deserialization failed");
|
|
}
|
|
|
|
pc_pointlist_add_point(pl, pt);
|
|
|
|
offset += INTALIGN(VARSIZE(serpt));
|
|
}
|
|
}
|
|
|
|
if (pl->npoints == 0)
|
|
return NULL;
|
|
|
|
pa = pc_patch_from_pointlist(pl);
|
|
pc_pointlist_free(pl);
|
|
return pa;
|
|
}
|
|
|
|
static PCPATCH *
|
|
#if PGSQL_VERSION < 120
|
|
pcpatch_from_patch_array(ArrayType *array, FunctionCallInfoData *fcinfo)
|
|
#else
|
|
pcpatch_from_patch_array(ArrayType *array, FunctionCallInfo fcinfo)
|
|
#endif
|
|
{
|
|
int nelems;
|
|
bits8 *bitmap;
|
|
size_t offset = 0;
|
|
int i;
|
|
uint32 pcid = 0;
|
|
PCPATCH *pa;
|
|
PCPATCH **palist;
|
|
int numpatches = 0;
|
|
PCSCHEMA *schema = 0;
|
|
|
|
/* 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)
|
|
return NULL;
|
|
|
|
/* Make our temporary list of patches */
|
|
palist = pcalloc(nelems * sizeof(PCPATCH *));
|
|
|
|
/* Read the patches out of the array and deserialize */
|
|
offset = 0;
|
|
bitmap = ARR_NULLBITMAP(array);
|
|
for (i = 0; i < nelems; i++)
|
|
{
|
|
/* Only work on non-NULL entries in the array */
|
|
if (!array_get_isnull(bitmap, i))
|
|
{
|
|
SERIALIZED_PATCH *serpatch =
|
|
(SERIALIZED_PATCH *)(ARR_DATA_PTR(array) + offset);
|
|
|
|
if (!schema)
|
|
{
|
|
schema = pc_schema_from_pcid(serpatch->pcid, fcinfo);
|
|
}
|
|
|
|
if (!pcid)
|
|
{
|
|
pcid = serpatch->pcid;
|
|
}
|
|
else if (pcid != serpatch->pcid)
|
|
{
|
|
elog(ERROR, "pcpatch_from_patch_array: pcid mismatch (%d != %d)",
|
|
serpatch->pcid, pcid);
|
|
}
|
|
|
|
pa = pc_patch_deserialize(serpatch, schema);
|
|
if (!pa)
|
|
{
|
|
elog(ERROR, "pcpatch_from_patch_array: patch deserialization failed");
|
|
}
|
|
|
|
palist[numpatches++] = pa;
|
|
|
|
offset += INTALIGN(VARSIZE(serpatch));
|
|
}
|
|
}
|
|
|
|
/* Can't do anything w/ NULL */
|
|
if (numpatches == 0)
|
|
return NULL;
|
|
|
|
/* Pass to the lib to build the output patch from the list */
|
|
pa = pc_patch_from_patchlist(palist, numpatches);
|
|
|
|
/* Free the temporary patch list */
|
|
for (i = 0; i < numpatches; i++)
|
|
{
|
|
pc_patch_free(palist[i]);
|
|
}
|
|
pcfree(palist);
|
|
|
|
return pa;
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pcpatch_from_pcpatch_array);
|
|
Datum pcpatch_from_pcpatch_array(PG_FUNCTION_ARGS)
|
|
{
|
|
ArrayType *array;
|
|
PCPATCH *pa;
|
|
SERIALIZED_PATCH *serpa;
|
|
|
|
if (PG_ARGISNULL(0))
|
|
PG_RETURN_NULL();
|
|
|
|
array = DatumGetArrayTypeP(PG_GETARG_DATUM(0));
|
|
pa = pcpatch_from_patch_array(array, fcinfo);
|
|
if (!pa)
|
|
PG_RETURN_NULL();
|
|
|
|
serpa = pc_patch_serialize(pa, NULL);
|
|
pc_patch_free(pa);
|
|
PG_RETURN_POINTER(serpa);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pcpatch_from_pcpoint_array);
|
|
Datum pcpatch_from_pcpoint_array(PG_FUNCTION_ARGS)
|
|
{
|
|
ArrayType *array;
|
|
PCPATCH *pa;
|
|
SERIALIZED_PATCH *serpa;
|
|
|
|
if (PG_ARGISNULL(0))
|
|
PG_RETURN_NULL();
|
|
|
|
array = DatumGetArrayTypeP(PG_GETARG_DATUM(0));
|
|
pa = pcpatch_from_point_array(array, fcinfo);
|
|
if (!pa)
|
|
PG_RETURN_NULL();
|
|
|
|
serpa = pc_patch_serialize(pa, NULL);
|
|
pc_patch_free(pa);
|
|
PG_RETURN_POINTER(serpa);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pcpatch_from_float_array);
|
|
Datum pcpatch_from_float_array(PG_FUNCTION_ARGS)
|
|
{
|
|
int i, ndims, nelems, npoints;
|
|
float8 *vals;
|
|
PCPATCH *pa;
|
|
PCPOINTLIST *pl;
|
|
SERIALIZED_PATCH *serpa;
|
|
uint32 pcid = PG_GETARG_INT32(0);
|
|
ArrayType *arrptr = PG_GETARG_ARRAYTYPE_P(1);
|
|
PCSCHEMA *schema = pc_schema_from_pcid(pcid, fcinfo);
|
|
|
|
if (!schema)
|
|
elog(ERROR, "unable to load schema for pcid = %d", pcid);
|
|
|
|
if (ARR_ELEMTYPE(arrptr) != FLOAT8OID)
|
|
elog(ERROR, "array must be of float8[]");
|
|
|
|
if (ARR_NDIM(arrptr) != 1)
|
|
elog(ERROR, "float8[] must have one dimension");
|
|
|
|
if (ARR_HASNULL(arrptr))
|
|
elog(ERROR, "float8[] must not have null elements");
|
|
|
|
ndims = schema->ndims;
|
|
nelems = ARR_DIMS(arrptr)[0];
|
|
|
|
if (nelems % ndims != 0)
|
|
{
|
|
elog(ERROR, "array dimensions do not match schema dimensions of pcid = %d",
|
|
pcid);
|
|
}
|
|
|
|
npoints = nelems / ndims;
|
|
|
|
vals = (float8 *)ARR_DATA_PTR(arrptr);
|
|
pl = pc_pointlist_make(nelems);
|
|
|
|
for (i = 0; i < npoints; ++i)
|
|
{
|
|
|
|
PCPOINT *pt = pc_point_from_double_array(schema, vals, i * ndims, ndims);
|
|
pc_pointlist_add_point(pl, pt);
|
|
}
|
|
|
|
pa = pc_patch_from_pointlist(pl);
|
|
pc_pointlist_free(pl);
|
|
if (!pa)
|
|
PG_RETURN_NULL();
|
|
|
|
serpa = pc_patch_serialize(pa, NULL);
|
|
|
|
pc_patch_free(pa);
|
|
PG_RETURN_POINTER(serpa);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
ArrayBuildState *s;
|
|
} abs_trans;
|
|
|
|
PG_FUNCTION_INFO_V1(pointcloud_abs_in);
|
|
Datum pointcloud_abs_in(PG_FUNCTION_ARGS)
|
|
{
|
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("function pointcloud_abs_in not implemented")));
|
|
PG_RETURN_POINTER(NULL);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pointcloud_abs_out);
|
|
Datum pointcloud_abs_out(PG_FUNCTION_ARGS)
|
|
{
|
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("function pointcloud_abs_out not implemented")));
|
|
PG_RETURN_POINTER(NULL);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pointcloud_agg_transfn);
|
|
Datum pointcloud_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 (!AggCheckCallContext(fcinfo, &aggcontext))
|
|
{
|
|
/* cannot be called directly because of dummy-type argument */
|
|
elog(ERROR, "pointcloud_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 pointcloud_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 = pointcloud_agg_final(a, CurrentMemoryContext, fcinfo);
|
|
PG_RETURN_DATUM(result);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pcpatch_agg_final_array);
|
|
Datum pcpatch_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 = pointcloud_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)
|
|
{
|
|
ArrayType *array;
|
|
abs_trans *a;
|
|
PCPATCH *pa;
|
|
SERIALIZED_PATCH *serpa;
|
|
|
|
if (PG_ARGISNULL(0))
|
|
PG_RETURN_NULL(); /* returns null iff no input values */
|
|
|
|
a = (abs_trans *)PG_GETARG_POINTER(0);
|
|
|
|
array =
|
|
DatumGetArrayTypeP(pointcloud_agg_final(a, CurrentMemoryContext, fcinfo));
|
|
pa = pcpatch_from_point_array(array, fcinfo);
|
|
if (!pa)
|
|
PG_RETURN_NULL();
|
|
|
|
serpa = pc_patch_serialize(pa, NULL);
|
|
pc_patch_free(pa);
|
|
PG_RETURN_POINTER(serpa);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pcpatch_agg_final_pcpatch);
|
|
Datum pcpatch_agg_final_pcpatch(PG_FUNCTION_ARGS)
|
|
{
|
|
ArrayType *array;
|
|
abs_trans *a;
|
|
PCPATCH *pa;
|
|
SERIALIZED_PATCH *serpa;
|
|
|
|
if (PG_ARGISNULL(0))
|
|
PG_RETURN_NULL(); /* returns null iff no input values */
|
|
|
|
a = (abs_trans *)PG_GETARG_POINTER(0);
|
|
|
|
array =
|
|
DatumGetArrayTypeP(pointcloud_agg_final(a, CurrentMemoryContext, fcinfo));
|
|
pa = pcpatch_from_patch_array(array, fcinfo);
|
|
if (!pa)
|
|
PG_RETURN_NULL();
|
|
|
|
serpa = pc_patch_serialize(pa, NULL);
|
|
pc_patch_free(pa);
|
|
PG_RETURN_POINTER(serpa);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pcpatch_unnest);
|
|
Datum pcpatch_unnest(PG_FUNCTION_ARGS)
|
|
{
|
|
typedef struct
|
|
{
|
|
int nextelem;
|
|
int numelems;
|
|
PCPOINTLIST *pointlist;
|
|
} pcpatch_unnest_fctx;
|
|
|
|
FuncCallContext *funcctx;
|
|
pcpatch_unnest_fctx *fctx;
|
|
MemoryContext oldcontext;
|
|
|
|
/* stuff done only on the first call of the function */
|
|
if (SRF_IS_FIRSTCALL())
|
|
{
|
|
PCPATCH *patch;
|
|
SERIALIZED_PATCH *serpatch;
|
|
|
|
/* create a function context for cross-call persistence */
|
|
funcctx = SRF_FIRSTCALL_INIT();
|
|
|
|
/*
|
|
* switch to memory context appropriate for multiple function calls
|
|
*/
|
|
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
|
|
|
|
/*
|
|
* Get the patch value and detoast if needed. We can't do this
|
|
* earlier because if we have to detoast, we want the detoasted copy
|
|
* to be in multi_call_memory_ctx, so it will go away when we're done
|
|
* and not before. (If no detoast happens, we assume the originally
|
|
* passed array will stick around till then.)
|
|
*/
|
|
serpatch = PG_GETARG_SERPATCH_P(0);
|
|
|
|
/* The schema cache is not initialized at that time but we need the
|
|
* constants cache
|
|
*/
|
|
pointcloud_init_constants_cache();
|
|
patch = pc_patch_deserialize(serpatch,
|
|
pc_schema_from_pcid_uncached(serpatch->pcid));
|
|
|
|
/* allocate memory for user context */
|
|
fctx = (pcpatch_unnest_fctx *)palloc(sizeof(pcpatch_unnest_fctx));
|
|
|
|
/* initialize state */
|
|
fctx->nextelem = 0;
|
|
fctx->numelems = patch->npoints;
|
|
fctx->pointlist = pc_pointlist_from_patch(patch);
|
|
|
|
/* save user context, switch back to function context */
|
|
funcctx->user_fctx = fctx;
|
|
MemoryContextSwitchTo(oldcontext);
|
|
}
|
|
|
|
/* stuff done on every call of the function */
|
|
funcctx = SRF_PERCALL_SETUP();
|
|
fctx = funcctx->user_fctx;
|
|
|
|
if (fctx->nextelem < fctx->numelems)
|
|
{
|
|
Datum elem;
|
|
PCPOINT *pt = pc_pointlist_get_point(fctx->pointlist, fctx->nextelem);
|
|
SERIALIZED_POINT *serpt = pc_point_serialize(pt);
|
|
fctx->nextelem++;
|
|
elem = PointerGetDatum(serpt);
|
|
SRF_RETURN_NEXT(funcctx, elem);
|
|
}
|
|
else
|
|
{
|
|
/* do when there is no more left */
|
|
SRF_RETURN_DONE(funcctx);
|
|
}
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pcpatch_uncompress);
|
|
Datum pcpatch_uncompress(PG_FUNCTION_ARGS)
|
|
{
|
|
SERIALIZED_PATCH *serpa = PG_GETARG_SERPATCH_P(0);
|
|
PCSCHEMA *schema = pc_schema_from_pcid(serpa->pcid, fcinfo);
|
|
PCPATCH *patch = pc_patch_deserialize(serpa, schema);
|
|
SERIALIZED_PATCH *serpa_out = pc_patch_serialize_to_uncompressed(patch);
|
|
pc_patch_free(patch);
|
|
PG_RETURN_POINTER(serpa_out);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pcpatch_compress);
|
|
Datum pcpatch_compress(PG_FUNCTION_ARGS)
|
|
{
|
|
SERIALIZED_PATCH *serpa = PG_GETARG_SERPATCH_P(0);
|
|
text *compr_in_text = PG_GETARG_TEXT_P(1);
|
|
char *compr_in = text_to_cstring(compr_in_text);
|
|
text *config_in_text = PG_GETARG_TEXT_P(2);
|
|
char *config_in = text_to_cstring(config_in_text);
|
|
PCSCHEMA *schema = pc_schema_from_pcid(serpa->pcid, fcinfo);
|
|
PCPATCH *patch_in = pc_patch_deserialize(serpa, schema);
|
|
PCPATCH *pa = patch_in;
|
|
SERIALIZED_PATCH *serpa_out;
|
|
PCDIMSTATS *stats = NULL;
|
|
int i;
|
|
|
|
/* Uncompress first */
|
|
if (patch_in->type != PC_NONE)
|
|
{
|
|
pa = pc_patch_uncompress(patch_in);
|
|
}
|
|
|
|
schema = pc_schema_clone(schema); /* we're going to modify it */
|
|
|
|
/* Set compression scheme */
|
|
if (*compr_in == '\0' || strcasecmp(compr_in, "auto") == 0)
|
|
{
|
|
/* keep schema defined compression */
|
|
}
|
|
else if (strcmp(compr_in, "dimensional") == 0)
|
|
{
|
|
{
|
|
char *ptr = config_in;
|
|
PCPATCH_DIMENSIONAL *pdl =
|
|
pc_patch_dimensional_from_uncompressed((PCPATCH_UNCOMPRESSED *)pa);
|
|
schema->compression = PC_DIMENSIONAL;
|
|
stats = pc_dimstats_make(schema);
|
|
pc_dimstats_update(stats, pdl);
|
|
/* make sure to avoid stat updates (not sure if needed) */
|
|
stats->total_points = PCDIMSTATS_MIN_SAMPLE + 1;
|
|
|
|
/* Fill in per-dimension compression */
|
|
if (*ptr)
|
|
for (i = 0; i < stats->ndims; ++i)
|
|
{
|
|
PCDIMSTAT *stat = &(stats->stats[i]);
|
|
/*pcinfo("ptr: %s", ptr);*/
|
|
if (*ptr == ',' || strncmp(ptr, "auto", strlen("auto")) == 0)
|
|
{
|
|
/* leave auto-determined compression */
|
|
}
|
|
else if (strncmp(ptr, "rle", strlen("rle")) == 0)
|
|
{
|
|
stat->recommended_compression = PC_DIM_RLE;
|
|
}
|
|
else if (strncmp(ptr, "sigbits", strlen("sigbits")) == 0)
|
|
{
|
|
stat->recommended_compression = PC_DIM_SIGBITS;
|
|
}
|
|
else if (strncmp(ptr, "zlib", strlen("zlib")) == 0)
|
|
{
|
|
stat->recommended_compression = PC_DIM_ZLIB;
|
|
}
|
|
else
|
|
{
|
|
elog(ERROR,
|
|
"Unrecognized dimensional compression '%s'. Please specify "
|
|
"'auto', 'rle', 'sigbits' or 'zlib'",
|
|
ptr);
|
|
}
|
|
while (*ptr && *ptr != ',')
|
|
++ptr;
|
|
if (!*ptr)
|
|
break;
|
|
else
|
|
++ptr;
|
|
}
|
|
|
|
if (pa != patch_in)
|
|
pc_patch_free(pa);
|
|
pa = (PCPATCH *)pc_patch_dimensional_compress(pdl, stats);
|
|
pc_patch_dimensional_free(pdl);
|
|
}
|
|
}
|
|
else if (strcmp(compr_in, "laz") == 0)
|
|
{
|
|
schema->compression = PC_LAZPERF;
|
|
}
|
|
else
|
|
{
|
|
elog(ERROR,
|
|
"Unrecognized compression '%s'. Please specify 'auto','dimensional' "
|
|
"or 'laz'",
|
|
compr_in);
|
|
}
|
|
|
|
pa->schema = schema; /* install overridden schema */
|
|
serpa_out = pc_patch_serialize(pa, stats);
|
|
|
|
if (pa != patch_in)
|
|
pc_patch_free(pa);
|
|
pc_patch_free(patch_in);
|
|
pc_schema_free(schema);
|
|
|
|
PG_RETURN_POINTER(serpa_out);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pcpatch_numpoints);
|
|
Datum pcpatch_numpoints(PG_FUNCTION_ARGS)
|
|
{
|
|
SERIALIZED_PATCH *serpa = PG_GETHEADER_SERPATCH_P(0);
|
|
PG_RETURN_INT32(serpa->npoints);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pcpatch_pointn);
|
|
Datum pcpatch_pointn(PG_FUNCTION_ARGS)
|
|
{
|
|
SERIALIZED_POINT *serpt;
|
|
SERIALIZED_PATCH *serpa = PG_GETARG_SERPATCH_P(0);
|
|
int32 n = PG_GETARG_INT32(1);
|
|
PCSCHEMA *schema = pc_schema_from_pcid(serpa->pcid, fcinfo);
|
|
PCPATCH *patch = pc_patch_deserialize(serpa, schema);
|
|
PCPOINT *pt = NULL;
|
|
if (patch)
|
|
{
|
|
pt = pc_patch_pointn(patch, n);
|
|
pc_patch_free(patch);
|
|
}
|
|
if (!pt)
|
|
PG_RETURN_NULL();
|
|
serpt = pc_point_serialize(pt);
|
|
pc_point_free(pt);
|
|
PG_RETURN_POINTER(serpt);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pcpatch_range);
|
|
Datum pcpatch_range(PG_FUNCTION_ARGS)
|
|
{
|
|
SERIALIZED_PATCH *serpaout;
|
|
SERIALIZED_PATCH *serpa = PG_GETARG_SERPATCH_P(0);
|
|
int32 first = PG_GETARG_INT32(1);
|
|
int32 count = PG_GETARG_INT32(2);
|
|
PCSCHEMA *schema = pc_schema_from_pcid(serpa->pcid, fcinfo);
|
|
PCPATCH *patch = pc_patch_deserialize(serpa, schema);
|
|
PCPATCH *patchout = NULL;
|
|
if (patch)
|
|
{
|
|
patchout = pc_patch_range(patch, first, count);
|
|
if (patchout != patch)
|
|
pc_patch_free(patch);
|
|
}
|
|
if (!patchout)
|
|
PG_RETURN_NULL();
|
|
serpaout = pc_patch_serialize(patchout, NULL);
|
|
pc_patch_free(patchout);
|
|
PG_RETURN_POINTER(serpaout);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pcpatch_pcid);
|
|
Datum pcpatch_pcid(PG_FUNCTION_ARGS)
|
|
{
|
|
SERIALIZED_PATCH *serpa = PG_GETHEADER_SERPATCH_P(0);
|
|
PG_RETURN_INT32(serpa->pcid);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pcpatch_summary);
|
|
Datum pcpatch_summary(PG_FUNCTION_ARGS)
|
|
{
|
|
const int stats_size_guess = 400;
|
|
SERIALIZED_PATCH *serpa;
|
|
PCSCHEMA *schema;
|
|
PCSTATS *stats;
|
|
PCPATCH *patch = NULL;
|
|
StringInfoData strdata;
|
|
text *ret;
|
|
const char *comma = "";
|
|
int i;
|
|
|
|
serpa = PG_GETHEADERX_SERPATCH_P(0, stats_size_guess);
|
|
schema = pc_schema_from_pcid(serpa->pcid, fcinfo);
|
|
if (serpa->compression == PC_DIMENSIONAL)
|
|
{
|
|
/* need full data to inspect per-dimension compression */
|
|
/* NOTE: memory usage could be optimized to only fetch slices
|
|
* at specific offsets, but doesn't seem worth at this time
|
|
* See
|
|
* https://github.com/pgpointcloud/pointcloud/pull/51#issuecomment-83592363
|
|
*/
|
|
serpa = PG_GETARG_SERPATCH_P(0);
|
|
patch = pc_patch_deserialize(serpa, schema);
|
|
}
|
|
else if (stats_size_guess < pc_stats_size(schema))
|
|
{
|
|
/* only need stats here */
|
|
serpa = PG_GETHEADERX_SERPATCH_P(0, pc_stats_size(schema));
|
|
}
|
|
stats = pc_patch_stats_deserialize(schema, serpa->data);
|
|
|
|
initStringInfo(&strdata);
|
|
/* Make space for VARSIZ, see SET_VARSIZE below */
|
|
appendStringInfoSpaces(&strdata, VARHDRSZ);
|
|
|
|
appendStringInfo(&strdata,
|
|
"{"
|
|
"\"pcid\":%d, \"npts\":%d, \"srid\":%d, "
|
|
"\"compr\":\"%s\",\"dims\":[",
|
|
serpa->pcid, serpa->npoints, schema->srid,
|
|
pc_compression_name(serpa->compression));
|
|
|
|
for (i = 0; i < schema->ndims; ++i)
|
|
{
|
|
PCDIMENSION *dim = schema->dims[i];
|
|
PCBYTES bytes;
|
|
double val;
|
|
appendStringInfo(&strdata,
|
|
"%s{\"pos\":%d,\"name\":\"%s\",\"size\":%d"
|
|
",\"type\":\"%s\"",
|
|
comma, dim->position, dim->name, dim->size,
|
|
pc_interpretation_string(dim->interpretation));
|
|
|
|
/* Print per-dimension compression (if dimensional) */
|
|
if (serpa->compression == PC_DIMENSIONAL)
|
|
{
|
|
bytes = ((PCPATCH_DIMENSIONAL *)patch)->bytes[i];
|
|
switch (bytes.compression)
|
|
{
|
|
case PC_DIM_RLE:
|
|
appendStringInfoString(&strdata, ",\"compr\":\"rle\"");
|
|
break;
|
|
case PC_DIM_SIGBITS:
|
|
appendStringInfoString(&strdata, ",\"compr\":\"sigbits\"");
|
|
break;
|
|
case PC_DIM_ZLIB:
|
|
appendStringInfoString(&strdata, ",\"compr\":\"zlib\"");
|
|
break;
|
|
case PC_DIM_NONE:
|
|
appendStringInfoString(&strdata, ",\"compr\":\"none\"");
|
|
break;
|
|
default:
|
|
appendStringInfo(&strdata, ",\"compr\":\"unknown(%d)\"",
|
|
bytes.compression);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (stats)
|
|
{
|
|
pc_point_get_double_by_name(&(stats->min), dim->name, &val);
|
|
appendStringInfo(&strdata, ",\"stats\":{\"min\":%g", val);
|
|
pc_point_get_double_by_name(&(stats->max), dim->name, &val);
|
|
appendStringInfo(&strdata, ",\"max\":%g", val);
|
|
pc_point_get_double_by_name(&(stats->avg), dim->name, &val);
|
|
appendStringInfo(&strdata, ",\"avg\":%g}", val);
|
|
}
|
|
appendStringInfoString(&strdata, "}");
|
|
comma = ",";
|
|
}
|
|
|
|
appendStringInfoString(&strdata, "]}");
|
|
|
|
ret = (text *)strdata.data;
|
|
SET_VARSIZE(ret, strdata.len);
|
|
PG_RETURN_TEXT_P(ret);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pcpatch_compression);
|
|
Datum pcpatch_compression(PG_FUNCTION_ARGS)
|
|
{
|
|
SERIALIZED_PATCH *serpa = PG_GETHEADER_SERPATCH_P(0);
|
|
PG_RETURN_INT32(serpa->compression);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pcpatch_intersects);
|
|
Datum pcpatch_intersects(PG_FUNCTION_ARGS)
|
|
{
|
|
SERIALIZED_PATCH *serpa1 = PG_GETHEADER_SERPATCH_P(0);
|
|
SERIALIZED_PATCH *serpa2 = PG_GETHEADER_SERPATCH_P(1);
|
|
|
|
if (serpa1->pcid != serpa2->pcid)
|
|
elog(ERROR, "%s: pcid mismatch (%d != %d)", __func__, serpa1->pcid,
|
|
serpa2->pcid);
|
|
|
|
if (pc_bounds_intersects(&(serpa1->bounds), &(serpa2->bounds)))
|
|
{
|
|
PG_RETURN_BOOL(true);
|
|
}
|
|
PG_RETURN_BOOL(false);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pcpatch_size);
|
|
Datum pcpatch_size(PG_FUNCTION_ARGS)
|
|
{
|
|
SERIALIZED_PATCH *serpa = PG_GETARG_SERPATCH_P(0);
|
|
PG_RETURN_INT32(VARSIZE(serpa));
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pcpoint_size);
|
|
Datum pcpoint_size(PG_FUNCTION_ARGS)
|
|
{
|
|
SERIALIZED_POINT *serpt = PG_GETARG_SERPOINT_P(0);
|
|
PG_RETURN_INT32(VARSIZE(serpt));
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pcpoint_pcid);
|
|
Datum pcpoint_pcid(PG_FUNCTION_ARGS)
|
|
{
|
|
SERIALIZED_POINT *serpt = PG_GETARG_SERPOINT_P(0);
|
|
PG_RETURN_INT32(serpt->pcid);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pc_version);
|
|
Datum pc_version(PG_FUNCTION_ARGS)
|
|
{
|
|
text *version_text;
|
|
char version[64];
|
|
snprintf(version, 64, "%s", POINTCLOUD_VERSION);
|
|
version_text = cstring_to_text(version);
|
|
PG_RETURN_TEXT_P(version_text);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pc_pgsql_version);
|
|
Datum pc_pgsql_version(PG_FUNCTION_ARGS)
|
|
{
|
|
text *version_text;
|
|
char version[12];
|
|
snprintf(version, 12, "%d", PGSQL_VERSION);
|
|
version_text = cstring_to_text(version);
|
|
PG_RETURN_TEXT_P(version_text);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pc_libxml2_version);
|
|
Datum pc_libxml2_version(PG_FUNCTION_ARGS)
|
|
{
|
|
text *version_text;
|
|
char version[64];
|
|
snprintf(version, 64, "%s", LIBXML2_VERSION);
|
|
version_text = cstring_to_text(version);
|
|
PG_RETURN_TEXT_P(version_text);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pc_lazperf_enabled);
|
|
Datum pc_lazperf_enabled(PG_FUNCTION_ARGS)
|
|
{
|
|
#ifdef HAVE_LAZPERF
|
|
PG_RETURN_BOOL(true);
|
|
#else
|
|
PG_RETURN_BOOL(false);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Read a named dimension statistic from a PCPATCH
|
|
* PC_PatchMax(patch pcpatch, dimname text) returns Numeric
|
|
* PC_PatchMin(patch pcpatch, dimname text) returns Numeric
|
|
* PC_PatchAvg(patch pcpatch, dimname text) returns Numeric
|
|
* PC_PatchMax(patch pcpatch) returns PcPoint
|
|
* PC_PatchMin(patch pcpatch) returns PcPoint
|
|
* PC_PatchAvg(patch pcpatch) returns PcPoint
|
|
*/
|
|
PG_FUNCTION_INFO_V1(pcpatch_get_stat);
|
|
Datum pcpatch_get_stat(PG_FUNCTION_ARGS)
|
|
{
|
|
static int stats_size_guess = 400;
|
|
SERIALIZED_PATCH *serpa = PG_GETHEADERX_SERPATCH_P(0, stats_size_guess);
|
|
PCSCHEMA *schema = pc_schema_from_pcid(serpa->pcid, fcinfo);
|
|
int32 statno = PG_GETARG_INT32(1);
|
|
char *dim_str = 0;
|
|
PCSTATS *stats;
|
|
const PCPOINT *pt;
|
|
SERIALIZED_POINT *serpt = NULL;
|
|
float8 double_result;
|
|
int rv = 1;
|
|
|
|
if (PG_NARGS() > 2)
|
|
{
|
|
/* TODO: only get small slice ? */
|
|
dim_str = text_to_cstring(PG_GETARG_TEXT_P(2));
|
|
}
|
|
|
|
if (stats_size_guess < pc_stats_size(schema))
|
|
{
|
|
serpa = PG_GETHEADERX_SERPATCH_P(0, pc_stats_size(schema));
|
|
}
|
|
|
|
stats = pc_patch_stats_deserialize(schema, serpa->data);
|
|
|
|
if (!stats)
|
|
PG_RETURN_NULL();
|
|
|
|
/* Min */
|
|
if (0 == statno)
|
|
pt = &(stats->min);
|
|
/* Max */
|
|
else if (1 == statno)
|
|
pt = &(stats->max);
|
|
/* Avg */
|
|
else if (2 == statno)
|
|
pt = &(stats->avg);
|
|
/* Unsupported */
|
|
else
|
|
elog(ERROR, "stat number \"%d\" is not supported", statno);
|
|
|
|
/* empty dim string means we want the whole point */
|
|
if (!dim_str)
|
|
{
|
|
serpt = pc_point_serialize(pt);
|
|
pc_stats_free(stats);
|
|
PG_RETURN_POINTER(serpt);
|
|
}
|
|
else
|
|
{
|
|
rv = pc_point_get_double_by_name(pt, dim_str, &double_result);
|
|
pc_stats_free(stats);
|
|
if (!rv)
|
|
{
|
|
elog(ERROR, "dimension \"%s\" does not exist in schema", dim_str);
|
|
PG_RETURN_NULL();
|
|
}
|
|
pfree(dim_str);
|
|
PG_RETURN_DATUM(
|
|
DirectFunctionCall1(float8_numeric, Float8GetDatum(double_result)));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* PC_FilterLessThan(patch pcpatch, dimname text, value) returns PcPatch
|
|
* PC_FilterGreaterThan(patch pcpatch, dimname text, value) returns PcPatch
|
|
* PC_FilterEquals(patch pcpatch, dimname text, value) returns PcPatch
|
|
* PC_FilterBetween(patch pcpatch, dimname text, value1, value2) returns PcPatch
|
|
*/
|
|
PG_FUNCTION_INFO_V1(pcpatch_filter);
|
|
Datum pcpatch_filter(PG_FUNCTION_ARGS)
|
|
{
|
|
SERIALIZED_PATCH *serpatch = PG_GETARG_SERPATCH_P(0);
|
|
PCSCHEMA *schema = pc_schema_from_pcid(serpatch->pcid, fcinfo);
|
|
char *dim_name = text_to_cstring(PG_GETARG_TEXT_P(1));
|
|
float8 value1 = PG_GETARG_FLOAT8(2);
|
|
float8 value2 = PG_GETARG_FLOAT8(3);
|
|
int32 mode = PG_GETARG_INT32(4);
|
|
PCPATCH *patch;
|
|
PCPATCH *patch_filtered = NULL;
|
|
SERIALIZED_PATCH *serpatch_filtered;
|
|
|
|
patch = pc_patch_deserialize(serpatch, schema);
|
|
if (!patch)
|
|
{
|
|
elog(ERROR, "failed to deserialize patch");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
switch (mode)
|
|
{
|
|
case 0:
|
|
patch_filtered = pc_patch_filter_lt_by_name(patch, dim_name, value1);
|
|
break;
|
|
case 1:
|
|
patch_filtered = pc_patch_filter_gt_by_name(patch, dim_name, value1);
|
|
break;
|
|
case 2:
|
|
patch_filtered = pc_patch_filter_equal_by_name(patch, dim_name, value1);
|
|
break;
|
|
case 3:
|
|
patch_filtered =
|
|
pc_patch_filter_between_by_name(patch, dim_name, value1, value2);
|
|
break;
|
|
default:
|
|
elog(ERROR, "unknown mode \"%d\"", mode);
|
|
}
|
|
|
|
pc_patch_free(patch);
|
|
PG_FREE_IF_COPY(serpatch, 0);
|
|
|
|
if (!patch_filtered)
|
|
{
|
|
elog(ERROR, "dimension \"%s\" does not exist", dim_name);
|
|
}
|
|
pfree(dim_name);
|
|
|
|
/* Always treat zero-point patches as SQL NULL */
|
|
if (patch_filtered->npoints <= 0)
|
|
{
|
|
pc_patch_free(patch_filtered);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
serpatch_filtered = pc_patch_serialize(patch_filtered, NULL);
|
|
pc_patch_free(patch_filtered);
|
|
|
|
PG_RETURN_POINTER(serpatch_filtered);
|
|
}
|
|
|
|
const char **array_to_cstring_array(ArrayType *array, int *size)
|
|
{
|
|
int i, j, offset = 0;
|
|
int nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
|
|
const char **cstring = nelems ? pcalloc(nelems * sizeof(char *)) : NULL;
|
|
bits8 *bitmap = ARR_NULLBITMAP(array);
|
|
for (i = j = 0; i < nelems; ++i)
|
|
{
|
|
text *array_text;
|
|
if (array_get_isnull(bitmap, i))
|
|
continue;
|
|
|
|
array_text = (text *)(ARR_DATA_PTR(array) + offset);
|
|
cstring[j++] = text_to_cstring(array_text);
|
|
offset += INTALIGN(VARSIZE(array_text));
|
|
}
|
|
if (size)
|
|
*size = j;
|
|
return cstring;
|
|
}
|
|
|
|
void pc_cstring_array_free(const char **array, int nelems)
|
|
{
|
|
int i;
|
|
if (!array)
|
|
return;
|
|
for (i = 0; i < nelems; ++i)
|
|
pfree((void *)array[i]);
|
|
pcfree((void *)array);
|
|
}
|
|
|
|
/**
|
|
* PC_Sort(patch pcpatch, dimname text[]) returns PcPatch
|
|
*/
|
|
PG_FUNCTION_INFO_V1(pcpatch_sort);
|
|
Datum pcpatch_sort(PG_FUNCTION_ARGS)
|
|
{
|
|
SERIALIZED_PATCH *serpatch = PG_GETARG_SERPATCH_P(0);
|
|
ArrayType *array = PG_GETARG_ARRAYTYPE_P(1);
|
|
PCSCHEMA *schema = NULL;
|
|
PCPATCH *patch = NULL;
|
|
PCPATCH *patch_sorted = NULL;
|
|
SERIALIZED_PATCH *serpatch_sorted = NULL;
|
|
|
|
int ndims;
|
|
const char **dim_name = array_to_cstring_array(array, &ndims);
|
|
if (!ndims)
|
|
{
|
|
pc_cstring_array_free(dim_name, ndims);
|
|
PG_RETURN_POINTER(serpatch);
|
|
}
|
|
|
|
schema = pc_schema_from_pcid(serpatch->pcid, fcinfo);
|
|
|
|
patch = pc_patch_deserialize(serpatch, schema);
|
|
if (patch)
|
|
patch_sorted = pc_patch_sort(patch, dim_name, ndims);
|
|
|
|
pc_cstring_array_free(dim_name, ndims);
|
|
if (patch)
|
|
pc_patch_free(patch);
|
|
PG_FREE_IF_COPY(serpatch, 0);
|
|
|
|
if (!patch_sorted)
|
|
PG_RETURN_NULL();
|
|
|
|
serpatch_sorted = pc_patch_serialize(patch_sorted, NULL);
|
|
pc_patch_free(patch_sorted);
|
|
PG_RETURN_POINTER(serpatch_sorted);
|
|
}
|
|
|
|
/** True/false if the patch is sorted on dimension */
|
|
PG_FUNCTION_INFO_V1(pcpatch_is_sorted);
|
|
Datum pcpatch_is_sorted(PG_FUNCTION_ARGS)
|
|
{
|
|
ArrayType *array = PG_GETARG_ARRAYTYPE_P(1);
|
|
bool strict = PG_GETARG_BOOL(2);
|
|
PCSCHEMA *schema = NULL;
|
|
SERIALIZED_PATCH *serpatch = NULL;
|
|
PCPATCH *patch = NULL;
|
|
int ndims;
|
|
uint32_t res;
|
|
const char **dim_name = array_to_cstring_array(array, &ndims);
|
|
if (!ndims)
|
|
{
|
|
pc_cstring_array_free(dim_name, ndims);
|
|
PG_RETURN_BOOL(PC_TRUE);
|
|
}
|
|
serpatch = PG_GETARG_SERPATCH_P(0);
|
|
schema = pc_schema_from_pcid(serpatch->pcid, fcinfo);
|
|
patch = pc_patch_deserialize(serpatch, schema);
|
|
|
|
res = pc_patch_is_sorted(patch, dim_name, ndims, strict);
|
|
|
|
pc_cstring_array_free(dim_name, ndims);
|
|
pc_patch_free(patch);
|
|
|
|
if (res == PC_FAILURE - 1)
|
|
elog(ERROR, "PC_IsSorted failed");
|
|
|
|
PG_RETURN_BOOL(res);
|
|
}
|