1 /***********************************************************************
2 * pc_pgsql.c
3 *
4 * Utility functions to bind pc_api.h functions to PgSQL, including
5 * memory management and serialization/deserializations.
6 *
7 * PgSQL Pointcloud is free and open source software provided
8 * by the Government of Canada
9 * Copyright (c) 2013 Natural Resources Canada
10 *
11 ***********************************************************************/
12
13 #include <assert.h>
14 #include "pc_pgsql.h"
15 #include "executor/spi.h"
16 #include "access/hash.h"
17 #include "utils/hsearch.h"
18
19 PG_MODULE_MAGIC;
20
21 /**********************************************************************************
22 * POSTGRESQL MEMORY MANAGEMENT HOOKS
23 */
24
25 static void *
pgsql_alloc(size_t size)26 pgsql_alloc(size_t size)
27 {
28 void * result;
29 result = palloc(size);
30
31 if ( ! result )
32 {
33 ereport(ERROR,
34 (errcode(ERRCODE_OUT_OF_MEMORY),
35 errmsg("Out of virtual memory")));
36 }
37
38 return result;
39 }
40
41 static void *
pgsql_realloc(void * mem,size_t size)42 pgsql_realloc(void *mem, size_t size)
43 {
44 void * result;
45 result = repalloc(mem, size);
46 if ( ! result )
47 {
48 ereport(ERROR,
49 (errcode(ERRCODE_OUT_OF_MEMORY),
50 errmsg("Out of virtual memory")));
51 }
52 return result;
53 }
54
55 static void
pgsql_free(void * ptr)56 pgsql_free(void *ptr)
57 {
58 pfree(ptr);
59 }
60
61 static void
62 pgsql_msg_handler(int sig, const char *fmt, va_list ap)
63 __attribute__ (( format (printf, 2, 0) ));
64
65 static void
pgsql_msg_handler(int sig,const char * fmt,va_list ap)66 pgsql_msg_handler(int sig, const char *fmt, va_list ap)
67 {
68 #define MSG_MAXLEN 1024
69 char msg[MSG_MAXLEN] = {0};
70 vsnprintf(msg, MSG_MAXLEN, fmt, ap);
71 msg[MSG_MAXLEN-1] = '\0';
72 ereport(sig, (errmsg_internal("%s", msg)));
73 }
74
75 static void
76 pgsql_error(const char *fmt, va_list ap) __attribute__ (( format (printf, 1, 0) ));
77
78 static void
pgsql_error(const char * fmt,va_list ap)79 pgsql_error(const char *fmt, va_list ap)
80 {
81 pgsql_msg_handler(ERROR, fmt, ap);
82 }
83
84 static void
85 pgsql_warn(const char *fmt, va_list ap) __attribute__ (( format (printf, 1, 0) ));
86
87 static void
pgsql_warn(const char * fmt,va_list ap)88 pgsql_warn(const char *fmt, va_list ap)
89 {
90 pgsql_msg_handler(WARNING, fmt, ap);
91 }
92
93 static void
94 pgsql_info(const char *fmt, va_list ap) __attribute__ (( format (printf, 1, 0) ));
95
96 static void
pgsql_info(const char * fmt,va_list ap)97 pgsql_info(const char *fmt, va_list ap)
98 {
99 pgsql_msg_handler(NOTICE, fmt, ap);
100 }
101
102 /**********************************************************************************
103 * POINTCLOUD START-UP/SHUT-DOWN CALLBACKS
104 */
105
106 /**
107 * On module load we want to hook the message writing and memory allocation
108 * functions of libpc to the PostgreSQL ones.
109 * TODO: also hook the libxml2 hooks into PostgreSQL.
110 */
111 void _PG_init(void);
112 void
_PG_init(void)113 _PG_init(void)
114 {
115 elog(LOG, "Pointcloud (%s) module loaded", POINTCLOUD_VERSION);
116 pc_set_handlers(
117 pgsql_alloc, pgsql_realloc,
118 pgsql_free, pgsql_error,
119 pgsql_info, pgsql_warn
120 );
121
122 }
123
124 /* Module unload callback */
125 void _PG_fini(void);
126 void
_PG_fini(void)127 _PG_fini(void)
128 {
129 elog(LOG, "Pointcloud (%s) module unloaded", POINTCLOUD_VERSION);
130 }
131
132 /* Mask pcid from bottom of typmod */
pcid_from_typmod(const int32 typmod)133 uint32 pcid_from_typmod(const int32 typmod)
134 {
135 if ( typmod == -1 )
136 return 0;
137 else
138 return (typmod & 0x0000FFFF);
139 }
140
141 /**********************************************************************************
142 * PCPOINT WKB Handling
143 */
144
145 PCPOINT *
146 #if PGSQL_VERSION < 120
pc_point_from_hexwkb(const char * hexwkb,size_t hexlen,FunctionCallInfoData * fcinfo)147 pc_point_from_hexwkb(const char *hexwkb, size_t hexlen, FunctionCallInfoData *fcinfo)
148 #else
149 pc_point_from_hexwkb(const char *hexwkb, size_t hexlen, FunctionCallInfo fcinfo)
150 #endif
151 {
152 PCPOINT *pt;
153 PCSCHEMA *schema;
154 uint32 pcid;
155 uint8 *wkb = pc_bytes_from_hexbytes(hexwkb, hexlen);
156 size_t wkblen = hexlen/2;
157 pcid = pc_wkb_get_pcid(wkb);
158 schema = pc_schema_from_pcid(pcid, fcinfo);
159 pt = pc_point_from_wkb(schema, wkb, wkblen);
160 pfree(wkb);
161 return pt;
162 }
163
164 char *
pc_point_to_hexwkb(const PCPOINT * pt)165 pc_point_to_hexwkb(const PCPOINT *pt)
166 {
167 uint8 *wkb;
168 size_t wkb_size;
169 char *hexwkb;
170
171 wkb = pc_point_to_wkb(pt, &wkb_size);
172 hexwkb = pc_hexbytes_from_bytes(wkb, wkb_size);
173 pfree(wkb);
174 return hexwkb;
175 }
176
177
178 /**********************************************************************************
179 * PCPATCH WKB Handling
180 */
181
182 PCPATCH *
183 #if PGSQL_VERSION < 120
pc_patch_from_hexwkb(const char * hexwkb,size_t hexlen,FunctionCallInfoData * fcinfo)184 pc_patch_from_hexwkb(const char *hexwkb, size_t hexlen, FunctionCallInfoData *fcinfo)
185 #else
186 pc_patch_from_hexwkb(const char *hexwkb, size_t hexlen, FunctionCallInfo fcinfo)
187 #endif
188 {
189 PCPATCH *patch;
190 PCSCHEMA *schema;
191 uint32 pcid;
192 uint8 *wkb = pc_bytes_from_hexbytes(hexwkb, hexlen);
193 size_t wkblen = hexlen/2;
194 pcid = pc_wkb_get_pcid(wkb);
195 if ( ! pcid )
196 elog(ERROR, "%s: pcid is zero", __func__);
197
198 schema = pc_schema_from_pcid(pcid, fcinfo);
199 if ( ! schema )
200 elog(ERROR, "%s: unable to look up schema entry", __func__);
201
202 patch = pc_patch_from_wkb(schema, wkb, wkblen);
203 pfree(wkb);
204 return patch;
205 }
206
207 char *
pc_patch_to_hexwkb(const PCPATCH * patch)208 pc_patch_to_hexwkb(const PCPATCH *patch)
209 {
210 uint8 *wkb;
211 size_t wkb_size;
212 char *hexwkb;
213
214 wkb = pc_patch_to_wkb(patch, &wkb_size);
215 hexwkb = pc_hexbytes_from_bytes(wkb, wkb_size);
216 pfree(wkb);
217 return hexwkb;
218 }
219
220
221 /**********************************************************************************
222 * PCID <=> PCSCHEMA translation via POINTCLOUD_FORMATS
223 */
224
pcid_from_datum(Datum d)225 uint32 pcid_from_datum(Datum d)
226 {
227 SERIALIZED_POINT *serpart;
228 if ( ! d )
229 return 0;
230 /* Serializations are int32_t <size> uint32_t <pcid> == 8 bytes */
231 /* Cast to SERIALIZED_POINT for convenience, SERIALIZED_PATCH shares same header */
232 serpart = (SERIALIZED_POINT*)PG_DETOAST_DATUM_SLICE(d, 0, 8);
233 return serpart->pcid;
234 }
235
236 PCSCHEMA *
pc_schema_from_pcid_uncached(uint32 pcid)237 pc_schema_from_pcid_uncached(uint32 pcid)
238 {
239 char sql[256];
240 char *xml, *xml_spi, *srid_spi;
241 int err, srid;
242 size_t size;
243 PCSCHEMA *schema;
244
245 if (SPI_OK_CONNECT != SPI_connect ())
246 {
247 SPI_finish();
248 elog(ERROR, "%s: could not connect to SPI manager", __func__);
249 return NULL;
250 }
251
252 sprintf(sql, "select %s, %s from %s where pcid = %d",
253 POINTCLOUD_FORMATS_XML, POINTCLOUD_FORMATS_SRID, POINTCLOUD_FORMATS, pcid);
254 err = SPI_exec(sql, 1);
255
256 if ( err < 0 )
257 {
258 SPI_finish();
259 elog(ERROR, "%s: error (%d) executing query: %s", __func__, err, sql);
260 return NULL;
261 }
262
263 /* No entry in POINTCLOUD_FORMATS */
264 if (SPI_processed <= 0)
265 {
266 SPI_finish();
267 elog(ERROR, "no entry in \"%s\" for pcid = %d", POINTCLOUD_FORMATS, pcid);
268 return NULL;
269 }
270
271 /* Result */
272 xml_spi = SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1);
273 srid_spi = SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 2);
274
275 /* NULL result */
276 if ( ! ( xml_spi && srid_spi ) )
277 {
278 SPI_finish();
279 elog(ERROR, "unable to read row from \"%s\" for pcid = %d", POINTCLOUD_FORMATS, pcid);
280 return NULL;
281 }
282
283 /* Copy result to upper executor context */
284 size = strlen(xml_spi) + 1;
285 xml = SPI_palloc(size);
286 memcpy(xml, xml_spi, size);
287
288 /* Parse the SRID string into the function stack */
289 srid = atoi(srid_spi);
290
291 /* Disconnect from SPI, losing all our SPI-allocated memory now... */
292 SPI_finish();
293
294 /* Build the schema object */
295 schema = pc_schema_from_xml(xml);
296
297 if ( !schema )
298 {
299 ereport(ERROR,
300 (errcode(ERRCODE_NOT_AN_XML_DOCUMENT),
301 errmsg("unable to parse XML for pcid = %d in \"%s\"", pcid, POINTCLOUD_FORMATS)));
302 }
303
304 schema->pcid = pcid;
305 schema->srid = srid;
306
307 return schema;
308 }
309
310
311 /**
312 * Hold the schema references in a list.
313 * We'll just search them linearly, because
314 * usually we'll have only one per statement
315 */
316 #define SchemaCacheSize 16
317
318 typedef struct
319 {
320 int next_slot;
321 int pcids[SchemaCacheSize];
322 PCSCHEMA* schemas[SchemaCacheSize];
323 } SchemaCache;
324
325
326 /**
327 * Get the schema entry from the schema cache if one exists.
328 * If it doesn't exist, make a new empty one, cache it, and
329 * return it.
330 */
331 static SchemaCache *
332 #if PGSQL_VERSION < 120
GetSchemaCache(FunctionCallInfoData * fcinfo)333 GetSchemaCache(FunctionCallInfoData* fcinfo)
334 #else
335 GetSchemaCache(FunctionCallInfo fcinfo)
336 #endif
337 {
338 SchemaCache *cache = fcinfo->flinfo->fn_extra;
339 if ( ! cache )
340 {
341 cache = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(SchemaCache));
342 memset(cache, 0, sizeof(SchemaCache));
343 fcinfo->flinfo->fn_extra = cache;
344 }
345 return cache;
346 }
347
348
349 PCSCHEMA *
350 #if PGSQL_VERSION < 120
pc_schema_from_pcid(uint32 pcid,FunctionCallInfoData * fcinfo)351 pc_schema_from_pcid(uint32 pcid, FunctionCallInfoData *fcinfo)
352 #else
353 pc_schema_from_pcid(uint32 pcid, FunctionCallInfo fcinfo)
354 #endif
355 {
356 SchemaCache *schema_cache = GetSchemaCache(fcinfo);
357 int i;
358 PCSCHEMA *schema;
359 MemoryContext oldcontext;
360
361 /* Unable to find/make a schema cache? Odd. */
362 if ( ! schema_cache )
363 {
364 ereport(ERROR,
365 (errcode(ERRCODE_INTERNAL_ERROR),
366 errmsg("unable to create/load statement level schema cache")));
367 }
368
369 /* Find our PCID if it's in there (usually it will be first) */
370 for ( i = 0; i < SchemaCacheSize; i++ )
371 {
372 if ( schema_cache->pcids[i] == pcid )
373 {
374 return schema_cache->schemas[i];
375 }
376 }
377
378 elog(DEBUG1, "schema cache miss, use pc_schema_from_pcid_uncached");
379
380 /* Not in there, load one the old-fashioned way. */
381 oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
382 schema = pc_schema_from_pcid_uncached(pcid);
383 MemoryContextSwitchTo(oldcontext);
384
385 /* Failed to load the XML? Odd. */
386 if ( ! schema )
387 {
388 ereport(ERROR,
389 (errcode(ERRCODE_INTERNAL_ERROR),
390 errmsg("unable to load schema for pcid %u", pcid)));
391 }
392
393 /* Save the XML in the next unused slot */
394 schema_cache->schemas[schema_cache->next_slot] = schema;
395 schema_cache->pcids[schema_cache->next_slot] = pcid;
396 schema_cache->next_slot = (schema_cache->next_slot + 1) % SchemaCacheSize;
397 return schema;
398 }
399
400
401
402 /**********************************************************************************
403 * SERIALIZATION/DESERIALIZATION UTILITIES
404 */
405
406 SERIALIZED_POINT *
pc_point_serialize(const PCPOINT * pcpt)407 pc_point_serialize(const PCPOINT *pcpt)
408 {
409 size_t serpt_size = sizeof(SERIALIZED_POINT) - 1 + pcpt->schema->size;
410 SERIALIZED_POINT *serpt = palloc(serpt_size);
411 serpt->pcid = pcpt->schema->pcid;
412 memcpy(serpt->data, pcpt->data, pcpt->schema->size);
413 SET_VARSIZE(serpt, serpt_size);
414 return serpt;
415 }
416
417 PCPOINT *
pc_point_deserialize(const SERIALIZED_POINT * serpt,const PCSCHEMA * schema)418 pc_point_deserialize(const SERIALIZED_POINT *serpt, const PCSCHEMA *schema)
419 {
420 PCPOINT *pcpt;
421 size_t pgsize = VARSIZE(serpt) + 1 - sizeof(SERIALIZED_POINT);
422 /*
423 * Big problem, the size on disk doesn't match what we expect,
424 * so we cannot reliably interpret the contents.
425 */
426 if ( schema->size != pgsize )
427 {
428 elog(ERROR, "schema size and disk size mismatch, repair the schema");
429 return NULL;
430 }
431 pcpt = pc_point_from_data(schema, serpt->data);
432 return pcpt;
433 }
434
435
436 size_t
pc_patch_serialized_size(const PCPATCH * patch)437 pc_patch_serialized_size(const PCPATCH *patch)
438 {
439 size_t stats_size = pc_stats_size(patch->schema);
440 size_t common_size = sizeof(SERIALIZED_PATCH) - 1;
441 switch( patch->type )
442 {
443 case PC_NONE:
444 {
445 PCPATCH_UNCOMPRESSED *pu = (PCPATCH_UNCOMPRESSED*)patch;
446 return common_size + stats_size + pu->datasize;
447 }
448 case PC_DIMENSIONAL:
449 {
450 return common_size + stats_size + pc_patch_dimensional_serialized_size((PCPATCH_DIMENSIONAL*)patch);
451 }
452 case PC_LAZPERF:
453 {
454 static size_t lazsize_size = 4;
455 PCPATCH_LAZPERF *pg = (PCPATCH_LAZPERF*)patch;
456 return common_size + stats_size + lazsize_size + pg->lazperfsize;
457 }
458 default:
459 {
460 pcerror("%s: unknown compresed %d", __func__, patch->type);
461 }
462 }
463 return -1;
464 }
465
466 static size_t
pc_patch_stats_serialize(uint8_t * buf,const PCSCHEMA * schema,const PCSTATS * stats)467 pc_patch_stats_serialize(uint8_t *buf, const PCSCHEMA *schema, const PCSTATS *stats)
468 {
469 size_t sz = schema->size;
470 /* Copy min */
471 memcpy(buf, stats->min.data, sz);
472 /* Copy max */
473 memcpy(buf + sz, stats->max.data, sz);
474 /* Copy avg */
475 memcpy(buf + 2*sz, stats->avg.data, sz);
476
477 return sz*3;
478 }
479
480 /**
481 * Stats are always three PCPOINT serializations in a row,
482 * min, max, avg. Their size is the uncompressed buffer size for
483 * a point, the schema->size.
484 */
485 PCSTATS *
pc_patch_stats_deserialize(const PCSCHEMA * schema,const uint8_t * buf)486 pc_patch_stats_deserialize(const PCSCHEMA *schema, const uint8_t *buf)
487 {
488 size_t sz = schema->size;
489 const uint8_t *buf_min = buf;
490 const uint8_t *buf_max = buf + sz;
491 const uint8_t *buf_avg = buf + 2*sz;
492
493 return pc_stats_new_from_data(schema, buf_min, buf_max, buf_avg);
494 }
495
496 static SERIALIZED_PATCH *
pc_patch_dimensional_serialize(const PCPATCH * patch_in)497 pc_patch_dimensional_serialize(const PCPATCH *patch_in)
498 {
499 // uint32_t size;
500 // uint32_t pcid;
501 // uint32_t compression;
502 // uint32_t npoints;
503 // double xmin, xmax, ymin, ymax;
504 // data:
505 // pcpoint[3] stats;
506 // serialized_pcbytes[ndims] dimensions;
507
508 int i;
509 uint8_t *buf;
510 size_t serpch_size = pc_patch_serialized_size(patch_in);
511 SERIALIZED_PATCH *serpch = pcalloc(serpch_size);
512 const PCPATCH_DIMENSIONAL *patch = (PCPATCH_DIMENSIONAL*)patch_in;
513
514 assert(patch_in);
515 assert(patch_in->type == PC_DIMENSIONAL);
516
517 /* Copy basics */
518 serpch->pcid = patch->schema->pcid;
519 serpch->npoints = patch->npoints;
520 serpch->bounds = patch->bounds;
521 serpch->compression = patch->type;
522
523 /* Get a pointer to the data area */
524 buf = serpch->data;
525
526 /* Write stats into the buffer */
527 if ( patch->stats )
528 {
529 buf += pc_patch_stats_serialize(buf, patch->schema, patch->stats);
530 }
531 else
532 {
533 pcerror("%s: stats missing!", __func__);
534 }
535
536 /* Write each dimension in after the stats */
537 for ( i = 0; i < patch->schema->ndims; i++ )
538 {
539 size_t bsize = 0;
540 PCBYTES *pcb = &(patch->bytes[i]);
541 pc_bytes_serialize(pcb, buf, &bsize);
542 buf += bsize;
543 }
544
545 SET_VARSIZE(serpch, serpch_size);
546 return serpch;
547 }
548
549
550 static SERIALIZED_PATCH *
pc_patch_lazperf_serialize(const PCPATCH * patch_in)551 pc_patch_lazperf_serialize(const PCPATCH *patch_in)
552 {
553 size_t serpch_size = pc_patch_serialized_size(patch_in);
554 SERIALIZED_PATCH *serpch = pcalloc(serpch_size);
555 const PCPATCH_LAZPERF *patch = (PCPATCH_LAZPERF*)patch_in;
556 uint32_t lazsize = patch->lazperfsize;
557 uint8_t *buf = serpch->data;
558
559 assert(patch);
560 assert(patch->type == PC_LAZPERF);
561
562 /* Copy basics */
563 serpch->pcid = patch->schema->pcid;
564 serpch->npoints = patch->npoints;
565 serpch->bounds = patch->bounds;
566 serpch->compression = patch->type;
567
568 /* Write stats into the buffer first */
569 if ( patch->stats )
570 {
571 buf += pc_patch_stats_serialize(buf, patch->schema, patch->stats);
572 }
573 else
574 {
575 pcerror("%s: stats missing!", __func__);
576 }
577
578 /* Write buffer size */
579 memcpy(buf, &(lazsize), 4);
580 buf += 4;
581
582 /* Write buffer */
583 memcpy(buf, patch->lazperf, patch->lazperfsize);
584 SET_VARSIZE(serpch, serpch_size);
585
586 return serpch;
587 }
588
589 static SERIALIZED_PATCH *
pc_patch_uncompressed_serialize(const PCPATCH * patch_in)590 pc_patch_uncompressed_serialize(const PCPATCH *patch_in)
591 {
592 // uint32_t size;
593 // uint32_t pcid;
594 // uint32_t compression;
595 // uint32_t npoints;
596 // double xmin, xmax, ymin, ymax;
597 // data:
598 // pcpoint [];
599
600 uint8_t *buf;
601 size_t serpch_size;
602 SERIALIZED_PATCH *serpch;
603 const PCPATCH_UNCOMPRESSED *patch = (PCPATCH_UNCOMPRESSED *)patch_in;
604
605 serpch_size = pc_patch_serialized_size(patch_in);
606 serpch = pcalloc(serpch_size);
607
608 /* Copy basic */
609 serpch->compression = patch->type;
610 serpch->pcid = patch->schema->pcid;
611 serpch->npoints = patch->npoints;
612 serpch->bounds = patch->bounds;
613
614 /* Write stats into the buffer first */
615 buf = serpch->data;
616 if ( patch->stats )
617 {
618 buf += pc_patch_stats_serialize(buf, patch->schema, patch->stats);
619 }
620 else
621 {
622 pcerror("%s: stats missing!", __func__);
623 }
624
625 /* Copy point list into data buffer */
626 memcpy(buf, patch->data, patch->datasize);
627 SET_VARSIZE(serpch, serpch_size);
628 return serpch;
629 }
630
631
632 /**
633 * Convert struct to byte array.
634 * Userdata is currently only PCDIMSTATS, hopefully updated across
635 * a number of iterations and saved.
636 */
637 SERIALIZED_PATCH *
pc_patch_serialize(const PCPATCH * patch_in,void * userdata)638 pc_patch_serialize(const PCPATCH *patch_in, void *userdata)
639 {
640 PCPATCH *patch = (PCPATCH*)patch_in;
641 SERIALIZED_PATCH *serpatch = NULL;
642 /*
643 * Ensure the patch has stats calculated before going on
644 */
645 if ( ! patch->stats )
646 {
647 pcerror("%s: patch is missing stats", __func__);
648 return NULL;
649 }
650 /*
651 * Convert the patch to the final target compression,
652 * which is the one in the schema.
653 */
654 if ( patch->type != patch->schema->compression )
655 {
656 patch = pc_patch_compress(patch_in, userdata);
657 }
658
659 switch( patch->type )
660 {
661 case PC_NONE:
662 {
663 serpatch = pc_patch_uncompressed_serialize(patch);
664 break;
665 }
666 case PC_DIMENSIONAL:
667 {
668 serpatch = pc_patch_dimensional_serialize(patch);
669 break;
670 }
671 case PC_LAZPERF:
672 {
673 serpatch = pc_patch_lazperf_serialize(patch);
674 break;
675 }
676 default:
677 {
678 pcerror("%s: unsupported compression type %d", __func__, patch->type);
679 }
680 }
681
682 if ( patch != patch_in )
683 pc_patch_free(patch);
684
685 return serpatch;
686 }
687
688
689
690
691 /**
692 * Convert struct to byte array.
693 * Userdata is currently only PCDIMSTATS, hopefully updated across
694 * a number of iterations and saved.
695 */
696 SERIALIZED_PATCH *
pc_patch_serialize_to_uncompressed(const PCPATCH * patch_in)697 pc_patch_serialize_to_uncompressed(const PCPATCH *patch_in)
698 {
699 PCPATCH *patch = (PCPATCH*)patch_in;
700 SERIALIZED_PATCH *serpatch;
701
702 /* Convert the patch to uncompressed, if necessary */
703 if ( patch->type != PC_NONE )
704 {
705 patch = pc_patch_uncompress(patch_in);
706 }
707
708 serpatch = pc_patch_uncompressed_serialize(patch);
709
710 /* An uncompressed input won't result in a copy */
711 if ( patch != patch_in )
712 pc_patch_free(patch);
713
714 return serpatch;
715 }
716
717 static PCPATCH *
pc_patch_uncompressed_deserialize(const SERIALIZED_PATCH * serpatch,const PCSCHEMA * schema)718 pc_patch_uncompressed_deserialize(const SERIALIZED_PATCH *serpatch, const PCSCHEMA *schema)
719 {
720 // typedef struct
721 // {
722 // uint32_t size;
723 // uint32_t pcid;
724 // uint32_t compression;
725 // uint32_t npoints;
726 // double xmin, xmax, ymin, ymax;
727 // data:
728 // pcpoint[3] pcstats(min, max, avg)
729 // pcpoint[npoints]
730 // }
731 // SERIALIZED_PATCH;
732
733 uint8_t *buf;
734 size_t stats_size = pc_stats_size(schema); // 3 pcpoints worth of stats
735 PCPATCH_UNCOMPRESSED *patch = pcalloc(sizeof(PCPATCH_UNCOMPRESSED));
736
737 /* Set up basic info */
738 patch->type = serpatch->compression;
739 patch->schema = schema;
740 patch->readonly = true;
741 patch->npoints = serpatch->npoints;
742 patch->maxpoints = 0;
743 patch->bounds = serpatch->bounds;
744
745 buf = (uint8_t*)serpatch->data;
746
747 /* Point into the stats area */
748 patch->stats = pc_patch_stats_deserialize(schema, buf);
749
750 /* Advance data pointer past the stats serialization */
751 patch->data = buf + stats_size;
752
753 /* Calculate the point data buffer size */
754 patch->datasize = VARSIZE(serpatch) - sizeof(SERIALIZED_PATCH) + 1 - stats_size;
755 if ( patch->datasize != patch->npoints * schema->size )
756 pcerror("%s: calculated patch data sizes don't match (%d != %d)", __func__, patch->datasize, patch->npoints * schema->size);
757
758 return (PCPATCH*)patch;
759 }
760
761 static PCPATCH *
pc_patch_dimensional_deserialize(const SERIALIZED_PATCH * serpatch,const PCSCHEMA * schema)762 pc_patch_dimensional_deserialize(const SERIALIZED_PATCH *serpatch, const PCSCHEMA *schema)
763 {
764 // typedef struct
765 // {
766 // uint32_t size;
767 // uint32_t pcid;
768 // uint32_t compression;
769 // uint32_t npoints;
770 // double xmin, xmax, ymin, ymax;
771 // data:
772 // pcpoint[3] pcstats(min, max, avg)
773 // pcbytes[ndims];
774 // }
775 // SERIALIZED_PATCH;
776
777 PCPATCH_DIMENSIONAL *patch;
778 int i;
779 const uint8_t *buf;
780 int ndims = schema->ndims;
781 int npoints = serpatch->npoints;
782 size_t stats_size = pc_stats_size(schema); // 3 pcpoints worth of stats
783
784 /* Reference the external data */
785 patch = pcalloc(sizeof(PCPATCH_DIMENSIONAL));
786
787 /* Set up basic info */
788 patch->type = serpatch->compression;
789 patch->schema = schema;
790 patch->readonly = true;
791 patch->npoints = npoints;
792 patch->bounds = serpatch->bounds;
793
794 /* Point into the stats area */
795 patch->stats = pc_patch_stats_deserialize(schema, serpatch->data);
796
797 /* Set up dimensions */
798 patch->bytes = pcalloc(ndims * sizeof(PCBYTES));
799 buf = serpatch->data + stats_size;
800
801 for ( i = 0; i < ndims; i++ )
802 {
803 PCBYTES *pcb = &(patch->bytes[i]);
804 PCDIMENSION *dim = schema->dims[i];
805 pc_bytes_deserialize(buf, dim, pcb, true /*readonly*/, false /*flipendian*/);
806 pcb->npoints = npoints;
807 buf += pc_bytes_serialized_size(pcb);
808 }
809
810 return (PCPATCH*)patch;
811 }
812
813 /*
814 * We don't do any radical deserialization here. Don't build out the tree, just
815 * set up pointers to the start of the buffer, so we can build it out later
816 * if necessary.
817 */
818 static PCPATCH *
pc_patch_lazperf_deserialize(const SERIALIZED_PATCH * serpatch,const PCSCHEMA * schema)819 pc_patch_lazperf_deserialize(const SERIALIZED_PATCH *serpatch, const PCSCHEMA *schema)
820 {
821 PCPATCH_LAZPERF *patch;
822 uint32_t lazperfsize;
823 int npoints = serpatch->npoints;
824 size_t stats_size = pc_stats_size(schema);
825 uint8_t *buf = (uint8_t*)serpatch->data + stats_size;
826
827 /* Reference the external data */
828 patch = pcalloc(sizeof(PCPATCH_LAZPERF));
829
830 /* Set up basic info */
831 patch->type = serpatch->compression;
832 patch->schema = schema;
833 patch->readonly = true;
834 patch->npoints = npoints;
835 patch->bounds = serpatch->bounds;
836
837 /* Point into the stats area */
838 patch->stats = pc_patch_stats_deserialize(schema, serpatch->data);
839
840 /* Set up buffer */
841 memcpy(&lazperfsize, buf, 4);
842 patch->lazperfsize = lazperfsize;
843 buf += 4;
844
845 patch->lazperf = pcalloc( patch->lazperfsize );
846 memcpy(patch->lazperf, buf, patch->lazperfsize);
847
848 return (PCPATCH*)patch;
849 }
850
851 PCPATCH *
pc_patch_deserialize(const SERIALIZED_PATCH * serpatch,const PCSCHEMA * schema)852 pc_patch_deserialize(const SERIALIZED_PATCH *serpatch, const PCSCHEMA *schema)
853 {
854 switch(serpatch->compression)
855 {
856 case PC_NONE:
857 return pc_patch_uncompressed_deserialize(serpatch, schema);
858 case PC_DIMENSIONAL:
859 return pc_patch_dimensional_deserialize(serpatch, schema);
860 case PC_LAZPERF:
861 return pc_patch_lazperf_deserialize(serpatch, schema);
862 }
863 pcerror("%s: unsupported compression type", __func__);
864 return NULL;
865 }
866
867
868 static uint8_t *
pc_patch_wkb_set_double(uint8_t * wkb,double d)869 pc_patch_wkb_set_double(uint8_t *wkb, double d)
870 {
871 memcpy(wkb, &d, 8);
872 wkb += 8;
873 return wkb;
874 }
875
876 static uint8_t *
pc_patch_wkb_set_int32(uint8_t * wkb,uint32_t i)877 pc_patch_wkb_set_int32(uint8_t *wkb, uint32_t i)
878 {
879 memcpy(wkb, &i, 4);
880 wkb += 4;
881 return wkb;
882 }
883
884 static uint8_t *
pc_patch_wkb_set_char(uint8_t * wkb,char c)885 pc_patch_wkb_set_char(uint8_t *wkb, char c)
886 {
887 memcpy(wkb, &c, 1);
888 wkb += 1;
889 return wkb;
890 }
891
892 /* 0 = xdr | big endian */
893 /* 1 = ndr | little endian */
894 static char
machine_endian(void)895 machine_endian(void)
896 {
897 static int check_int = 1; /* dont modify this!!! */
898 return *((char *) &check_int);
899 }
900
901 uint8_t *
pc_patch_to_geometry_wkb_envelope(const SERIALIZED_PATCH * pa,const PCSCHEMA * schema,size_t * wkbsize)902 pc_patch_to_geometry_wkb_envelope(const SERIALIZED_PATCH *pa, const PCSCHEMA *schema, size_t *wkbsize)
903 {
904 static uint32_t srid_mask = 0x20000000;
905 static uint32_t nrings = 1;
906 static uint32_t npoints = 5;
907 uint32_t wkbtype = 3; /* WKB POLYGON */
908 uint8_t *wkb, *ptr;
909 int has_srid = false;
910 size_t size = 1 + 4 + 4 + 4 + 2*npoints*8; /* endian + type + nrings + npoints + 5 dbl pts */
911
912 /* Bounds! */
913 double xmin = pa->bounds.xmin;
914 double ymin = pa->bounds.ymin;
915 double xmax = pa->bounds.xmax;
916 double ymax = pa->bounds.ymax;
917
918 /* Make sure they're slightly bigger than a point */
919 if ( xmin == xmax ) xmax += xmax * 0.0000001;
920 if ( ymin == ymax ) ymax += ymax * 0.0000001;
921
922 if ( schema->srid > 0 )
923 {
924 has_srid = true;
925 wkbtype |= srid_mask;
926 size += 4;
927 }
928
929 wkb = palloc(size);
930 ptr = wkb;
931
932 ptr = pc_patch_wkb_set_char(ptr, machine_endian()); /* Endian flag */
933
934 ptr = pc_patch_wkb_set_int32(ptr, wkbtype); /* TYPE = Polygon */
935
936 if ( has_srid )
937 {
938 ptr = pc_patch_wkb_set_int32(ptr, schema->srid); /* SRID */
939 }
940
941 ptr = pc_patch_wkb_set_int32(ptr, nrings); /* NRINGS = 1 */
942 ptr = pc_patch_wkb_set_int32(ptr, npoints); /* NPOINTS = 5 */
943
944 /* Point 0 */
945 ptr = pc_patch_wkb_set_double(ptr, pa->bounds.xmin);
946 ptr = pc_patch_wkb_set_double(ptr, pa->bounds.ymin);
947
948 /* Point 1 */
949 ptr = pc_patch_wkb_set_double(ptr, pa->bounds.xmin);
950 ptr = pc_patch_wkb_set_double(ptr, pa->bounds.ymax);
951
952 /* Point 2 */
953 ptr = pc_patch_wkb_set_double(ptr, pa->bounds.xmax);
954 ptr = pc_patch_wkb_set_double(ptr, pa->bounds.ymax);
955
956 /* Point 3 */
957 ptr = pc_patch_wkb_set_double(ptr, pa->bounds.xmax);
958 ptr = pc_patch_wkb_set_double(ptr, pa->bounds.ymin);
959
960 /* Point 4 */
961 ptr = pc_patch_wkb_set_double(ptr, pa->bounds.xmin);
962 ptr = pc_patch_wkb_set_double(ptr, pa->bounds.ymin);
963
964 if ( wkbsize ) *wkbsize = size;
965 return wkb;
966 }
967