PostGIS  3.4.0dev-r@@SVN_REVISION@@
flatgeobuf.c
Go to the documentation of this file.
1 /**********************************************************************
2  *
3  * PostGIS - Spatial Types for PostgreSQL
4  * http://postgis.net
5  *
6  * PostGIS is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * PostGIS is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
18  *
19  **********************************************************************
20  *
21  * Copyright (C) 2021 Björn Harrtell <bjorn@wololo.org>
22  *
23  **********************************************************************/
24 
25 #include <math.h>
26 #include "flatgeobuf.h"
27 #include "pgsql_compat.h"
28 #include "funcapi.h"
29 #include "parser/parse_type.h"
30 #include "pgtime.h"
31 #include "utils/timestamp.h"
32 #include "miscadmin.h"
33 #include "utils/date.h"
34 #include "utils/datetime.h"
35 #include "utils/jsonb.h"
36 
37 static uint8_t get_column_type(Oid typoid) {
38  switch (typoid)
39  {
40  case BOOLOID:
41  return flatgeobuf_column_type_bool;
42  case INT2OID:
43  return flatgeobuf_column_type_short;
44  case INT4OID:
45  return flatgeobuf_column_type_int;
46  case INT8OID:
47  return flatgeobuf_column_type_long;
48  case FLOAT4OID:
49  return flatgeobuf_column_type_float;
50  case FLOAT8OID:
51  return flatgeobuf_column_type_double;
52  case TEXTOID:
53  case VARCHAROID:
54  return flatgeobuf_column_type_string;
55  case JSONBOID:
56  return flatgeobuf_column_type_json;
57  case BYTEAOID:
58  return flatgeobuf_column_type_binary;
59  case DATEOID:
60  case TIMEOID:
61  case TIMESTAMPOID:
62  case TIMESTAMPTZOID:
63  return flatgeobuf_column_type_datetime;
64  }
65  elog(ERROR, "flatgeobuf: get_column_type: '%d' column type not supported",
66  typoid);
67 }
68 
69 static void inspect_table(struct flatgeobuf_agg_ctx *ctx)
70 {
71  flatgeobuf_column *c;
72  flatgeobuf_column **columns;
73  uint32_t columns_size = 0;
74  Oid tupType = HeapTupleHeaderGetTypeId(ctx->row);
75  int32 tupTypmod = HeapTupleHeaderGetTypMod(ctx->row);
76  TupleDesc tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
77  int natts = tupdesc->natts;
78  bool geom_found = false;
79 
80  POSTGIS_DEBUG(2, "calling inspect_table");
81 
82  columns = palloc(sizeof(flatgeobuf_column *) * natts);
83  ctx->tupdesc = tupdesc;
84 
85  // inspect columns
86  // NOTE: last element will be unused if geom attr is found
87  for (int i = 0; i < natts; i++) {
88  Oid typoid = getBaseType(TupleDescAttr(tupdesc, i)->atttypid);
89  const char *key = TupleDescAttr(tupdesc, i)->attname.data;
90  POSTGIS_DEBUGF(2, "inspecting column definition for %s with oid %d", key, typoid);
91  if (ctx->geom_name == NULL) {
92  if (!geom_found && typoid == postgis_oid(GEOMETRYOID)) {
93  ctx->geom_index = i;
94  geom_found = true;
95  continue;
96  }
97  } else {
98  if (!geom_found && strcmp(key, ctx->geom_name) == 0) {
99  ctx->geom_index = i;
100  geom_found = true;
101  continue;
102  }
103  }
104  POSTGIS_DEBUGF(2, "creating column definition for %s with oid %d", key, typoid);
105 
106  c = (flatgeobuf_column *) palloc0(sizeof(flatgeobuf_column));
107  c->name = pstrdup(key);
108  c->type = get_column_type(typoid);
109  columns[columns_size] = c;
110  columns_size++;
111  }
112 
113  if (!geom_found)
114  elog(ERROR, "no geom column found");
115 
116  if (columns_size > 0) {
117  ctx->ctx->columns = columns;
118  ctx->ctx->columns_size = columns_size;
119  }
120 }
121 
122 // ensure properties has room for at least size
123 static void ensure_properties_size(struct flatgeobuf_agg_ctx *ctx, size_t size)
124 {
125  if (ctx->ctx->properties_size == 0) {
126  ctx->ctx->properties_size = 1024 * 4;
127  POSTGIS_DEBUGF(2, "flatgeobuf: properties buffer to size %d", ctx->ctx->properties_size);
128  ctx->ctx->properties = palloc(ctx->ctx->properties_size);
129  }
130  if (ctx->ctx->properties_size < size) {
131  ctx->ctx->properties_size = ctx->ctx->properties_size * 2;
132  POSTGIS_DEBUGF(2, "flatgeobuf: reallocating properties buffer to size %d", ctx->ctx->properties_size);
133  ctx->ctx->properties = repalloc(ctx->ctx->properties, ctx->ctx->properties_size);
134  ensure_properties_size(ctx, size);
135  }
136 }
137 
138 // ensure items have room for at least ctx->ctx->features_count + 1
139 static void ensure_items_len(struct flatgeobuf_agg_ctx *ctx)
140 {
141  if (ctx->ctx->features_count == 0) {
142  ctx->ctx->items_len = 32;
143  ctx->ctx->items = palloc(sizeof(flatgeobuf_item *) * ctx->ctx->items_len);
144  }
145  if (ctx->ctx->items_len < (ctx->ctx->features_count + 1)) {
146  ctx->ctx->items_len = ctx->ctx->items_len * 2;
147  POSTGIS_DEBUGF(2, "flatgeobuf: reallocating items to len %ld", ctx->ctx->items_len);
148  ctx->ctx->items = repalloc(ctx->ctx->items, sizeof(flatgeobuf_item *) * ctx->ctx->items_len);
149  ensure_items_len(ctx);
150  }
151 }
152 
154 {
155  uint16_t ci = 0;
156  size_t offset = 0;
157  Datum datum;
158  bool isnull;
159  Oid typoid;
160  uint32_t len;
161  uint32_t i;
162 
163  uint8_t byte_value;
164  int16_t short_value;
165  int32_t int_value;
166  int64_t long_value;
167  float float_value;
168  double double_value;
169  char *string_value;
170 
171  //Jsonb *jb;
172 
173  for (i = 0; i < (uint32_t) ctx->tupdesc->natts; i++) {
174  if (ctx->geom_index == i)
175  continue;
176  datum = GetAttributeByNum(ctx->row, i + 1, &isnull);
177  if (isnull)
178  continue;
179  ensure_properties_size(ctx, offset + sizeof(ci));
180  memcpy(ctx->ctx->properties + offset, &ci, sizeof(ci));
181  offset += sizeof(ci);
182  typoid = getBaseType(TupleDescAttr(ctx->tupdesc, i)->atttypid);
183  switch (typoid) {
184  case BOOLOID:
185  byte_value = DatumGetBool(datum) ? 1 : 0;
186  ensure_properties_size(ctx, offset + sizeof(byte_value));
187  memcpy(ctx->ctx->properties + offset, &byte_value, sizeof(byte_value));
188  offset += sizeof(byte_value);
189  break;
190  case INT2OID:
191  short_value = DatumGetInt16(datum);
192  ensure_properties_size(ctx, offset + sizeof(short_value));
193  memcpy(ctx->ctx->properties + offset, &short_value, sizeof(short_value));
194  offset += sizeof(short_value);
195  break;
196  case INT4OID:
197  int_value = DatumGetInt32(datum);
198  ensure_properties_size(ctx, offset + sizeof(int_value));
199  memcpy(ctx->ctx->properties + offset, &int_value, sizeof(int_value));
200  offset += sizeof(int_value);
201  break;
202  case INT8OID:
203  long_value = DatumGetInt64(datum);
204  ensure_properties_size(ctx, offset + sizeof(long_value));
205  memcpy(ctx->ctx->properties + offset, &long_value, sizeof(long_value));
206  offset += sizeof(long_value);
207  break;
208  case FLOAT4OID:
209  float_value = DatumGetFloat4(datum);
210  ensure_properties_size(ctx, offset + sizeof(float_value));
211  memcpy(ctx->ctx->properties + offset, &float_value, sizeof(float_value));
212  offset += sizeof(float_value);
213  break;
214  case FLOAT8OID:
215  double_value = DatumGetFloat8(datum);
216  ensure_properties_size(ctx, offset + sizeof(double_value));
217  memcpy(ctx->ctx->properties + offset, &double_value, sizeof(double_value));
218  offset += sizeof(double_value);
219  break;
220  case TEXTOID:
221  string_value = text_to_cstring(DatumGetTextP(datum));
222  len = strlen(string_value);
223  ensure_properties_size(ctx, offset + sizeof(len));
224  memcpy(ctx->ctx->properties + offset, &len, sizeof(len));
225  offset += sizeof(len);
226  ensure_properties_size(ctx, offset + len);
227  memcpy(ctx->ctx->properties + offset, string_value, len);
228  offset += len;
229  break;
230  case TIMESTAMPTZOID: {
231  struct pg_tm tm;
232  fsec_t fsec;
233  int tz;
234  const char *tzn = NULL;
235  TimestampTz timestamp;
236  timestamp = DatumGetTimestampTz(datum);
237  timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL);
238  string_value = palloc(MAXDATELEN + 1);
239  EncodeDateTime(&tm, fsec, true, tz, tzn, USE_ISO_DATES, string_value);
240  len = strlen(string_value);
241  ensure_properties_size(ctx, offset + sizeof(len));
242  memcpy(ctx->ctx->properties + offset, &len, sizeof(len));
243  offset += sizeof(len);
244  ensure_properties_size(ctx, offset + len);
245  memcpy(ctx->ctx->properties + offset, string_value, len);
246  offset += len;
247  break;
248  }
249  // TODO: handle date/time types
250  // case JSONBOID:
251  // jb = DatumGetJsonbP(datum);
252  // string_value = JsonbToCString(NULL, &jb->root, VARSIZE(jb));
253  // len = strlen(string_value);
254  // memcpy(data + offset, &len, sizeof(len));
255  // offset += sizeof(len);
256  // memcpy(data + offset, string_value, len);
257  // offset += len;
258  // break;
259  }
260  ci++;
261  }
262 
263  if (offset > 0) {
264  POSTGIS_DEBUGF(3, "offset %ld", offset);
265  ctx->ctx->properties_len = offset;
266  }
267 }
268 
270 {
271  uint8_t *buf = ctx->ctx->buf + ctx->ctx->offset;
272  uint32_t i;
273 
274  for (i = 0; i < FLATGEOBUF_MAGICBYTES_SIZE / 2; i++)
275  if (buf[i] != flatgeobuf_magicbytes[i])
276  elog(ERROR, "Data is not FlatGeobuf");
277  ctx->ctx->offset += FLATGEOBUF_MAGICBYTES_SIZE;
278 }
279 
280 static void decode_properties(struct flatgeobuf_decode_ctx *ctx, Datum *values, bool *isnull)
281 {
282  uint16_t i, ci;
283  flatgeobuf_column *column;
284  uint8_t type;
285  uint32_t offset = 0;
286  uint8_t *data = ctx->ctx->properties;
287  uint32_t size = ctx->ctx->properties_len;
288 
289  POSTGIS_DEBUGF(3, "flatgeobuf: decode_properties from byte array with length %d at offset %d", size, offset);
290 
291  // TODO: init isnull
292 
293  if (size > 0 && size < (sizeof(uint16_t) + sizeof(uint8_t)))
294  elog(ERROR, "flatgeobuf: decode_properties: Unexpected properties data size %d", size);
295  while (offset + 1 < size) {
296  if (offset + sizeof(uint16_t) > size)
297  elog(ERROR, "flatgeobuf: decode_properties: Unexpected offset %d", offset);
298  memcpy(&i, data + offset, sizeof(uint16_t));
299  ci = i + 2;
300  offset += sizeof(uint16_t);
301  if (i >= ctx->ctx->columns_size)
302  elog(ERROR, "flatgeobuf: decode_properties: Column index %hu out of range", i);
303  column = ctx->ctx->columns[i];
304  type = column->type;
305  isnull[ci] = false;
306  switch (type) {
307  case flatgeobuf_column_type_bool: {
308  uint8_t value;
309  if (offset + sizeof(uint8_t) > size)
310  elog(ERROR, "flatgeobuf: decode_properties: Invalid size for bool value");
311  memcpy(&value, data + offset, sizeof(uint8_t));
312  values[ci] = BoolGetDatum(value);
313  offset += sizeof(uint8_t);
314  break;
315  }
316  case flatgeobuf_column_type_byte: {
317  int8_t value;
318  if (offset + sizeof(int8_t) > size)
319  elog(ERROR, "flatgeobuf: decode_properties: Invalid size for byte value");
320  memcpy(&value, data + offset, sizeof(int8_t));
321  values[ci] = Int8GetDatum(value);
322  offset += sizeof(int8_t);
323  break;
324  }
325  case flatgeobuf_column_type_ubyte: {
326  uint8_t value;
327  if (offset + sizeof(uint8_t) > size)
328  elog(ERROR, "flatgeobuf: decode_properties: Invalid size for ubyte value");
329  memcpy(&value, data + offset, sizeof(uint8_t));
330  values[ci] = UInt8GetDatum(value);
331  offset += sizeof(uint8_t);
332  break;
333  }
334  case flatgeobuf_column_type_short: {
335  int16_t value;
336  if (offset + sizeof(int16_t) > size)
337  elog(ERROR, "flatgeobuf: decode_properties: Invalid size for short value");
338  memcpy(&value, data + offset, sizeof(int16_t));
339  values[ci] = Int16GetDatum(value);
340  offset += sizeof(int16_t);
341  break;
342  }
343  case flatgeobuf_column_type_ushort: {
344  uint16_t value;
345  if (offset + sizeof(uint16_t) > size)
346  elog(ERROR, "flatgeobuf: decode_properties: Invalid size for ushort value");
347  memcpy(&value, data + offset, sizeof(uint16_t));
348  values[ci] = UInt16GetDatum(value);
349  offset += sizeof(uint16_t);
350  break;
351  }
352  case flatgeobuf_column_type_int: {
353  int32_t value;
354  if (offset + sizeof(int32_t) > size)
355  elog(ERROR, "flatgeobuf: decode_properties: Invalid size for int value");
356  memcpy(&value, data + offset, sizeof(int32_t));
357  values[ci] = Int32GetDatum(value);
358  offset += sizeof(int32_t);
359  break;
360  }
361  case flatgeobuf_column_type_uint: {
362  uint32_t value;
363  if (offset + sizeof(uint32_t) > size)
364  elog(ERROR, "flatgeobuf: decode_properties: Invalid size for uint value");
365  memcpy(&value, data + offset, sizeof(uint32_t));
366  values[ci] = UInt32GetDatum(value);
367  offset += sizeof(uint32_t);
368  break;
369  }
370  case flatgeobuf_column_type_long: {
371  int64_t value;
372  if (offset + sizeof(int64_t) > size)
373  elog(ERROR, "flatgeobuf: decode_properties: Invalid size for long value");
374  memcpy(&value, data + offset, sizeof(int64_t));
375  values[ci] = Int64GetDatum(value);
376  offset += sizeof(int64_t);
377  break;
378  }
379  case flatgeobuf_column_type_ulong: {
380  uint64_t value;
381  if (offset + sizeof(uint64_t) > size)
382  elog(ERROR, "flatgeobuf: decode_properties: Invalid size for ulong value");
383  memcpy(&value, data + offset, sizeof(uint64_t));
384  values[ci] = UInt64GetDatum(value);
385  offset += sizeof(uint64_t);
386  break;
387  }
388  case flatgeobuf_column_type_float: {
389  float value;
390  if (offset + sizeof(float) > size)
391  elog(ERROR, "flatgeobuf: decode_properties: Invalid size for float value");
392  memcpy(&value, data + offset, sizeof(float));
393  values[ci] = Float4GetDatum(value);
394  offset += sizeof(float);
395  break;
396  }
397  case flatgeobuf_column_type_double: {
398  double value;
399  if (offset + sizeof(double) > size)
400  elog(ERROR, "flatgeobuf: decode_properties: Invalid size for double value");
401  memcpy(&value, data + offset, sizeof(double));
402  values[ci] = Float8GetDatum(value);
403  offset += sizeof(double);
404  break;
405  }
406  case flatgeobuf_column_type_string: {
407  uint32_t len;
408  if (offset + sizeof(len) > size)
409  elog(ERROR, "flatgeobuf: decode_properties: Invalid size for string value");
410  memcpy(&len, data + offset, sizeof(uint32_t));
411  offset += sizeof(len);
412  values[ci] = PointerGetDatum(cstring_to_text_with_len((const char *) data + offset, len));
413  offset += len;
414  break;
415  }
416  case flatgeobuf_column_type_datetime: {
417  uint32_t len;
418  char *buf;
419  char workbuf[MAXDATELEN + MAXDATEFIELDS];
420  char *field[MAXDATEFIELDS];
421  int ftype[MAXDATEFIELDS];
422  int dtype;
423  int nf;
424  struct pg_tm tt, *tm = &tt;
425  fsec_t fsec;
426  int tzp;
427  TimestampTz dttz;
428 #if POSTGIS_PGSQL_VERSION >= 160
429  DateTimeErrorExtra extra;
430 #endif
431  if (offset + sizeof(len) > size)
432  elog(ERROR, "flatgeobuf: decode_properties: Invalid size for string value");
433  memcpy(&len, data + offset, sizeof(uint32_t));
434  offset += sizeof(len);
435  buf = palloc0(len + 1);
436  memcpy(buf, (const char *) data + offset, len);
437  ParseDateTime((const char *) buf, workbuf, sizeof(workbuf), field, ftype, MAXDATEFIELDS, &nf);
438 
439 #if POSTGIS_PGSQL_VERSION >= 160
440  DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp, &extra);
441 #else
442  DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp);
443 #endif
444  tm2timestamp(tm, fsec, &tzp, &dttz);
445  values[ci] = TimestampTzGetDatum(dttz);
446  offset += len;
447  break;
448  }
449  // TODO: find out how to handle string to jsonb Datum
450  // case FlatGeobuf_ColumnType_Json:
451  // if (offset + sizeof(len) > size)
452  // elog(ERROR, "flatgeobuf: decode_properties: Invalid size for json value");
453  // len = *((uint32_t *)(data + offset));
454  // offset += sizeof(len);
455  // values[ci] = jsonb_from_cstring((const char *) data + offset, len);
456  // offset += len;
457  // break;
458  default:
459  elog(ERROR, "flatgeobuf: decode_properties: Unknown type %d", type);
460  }
461  }
462 
463 }
464 
466 {
467  HeapTuple heapTuple;
468  uint32_t natts = ctx->tupdesc->natts;
469 
470  Datum *values = palloc0(natts * sizeof(Datum *));
471  bool *isnull = palloc0(natts * sizeof(bool *));
472 
473  values[0] = Int32GetDatum(ctx->fid);
474 
475  if (flatgeobuf_decode_feature(ctx->ctx))
476  elog(ERROR, "flatgeobuf_decode_feature: unsuccessful");
477 
478  if (ctx->ctx->lwgeom != NULL) {
479  values[1] = PointerGetDatum(geometry_serialize(ctx->ctx->lwgeom));
480  } else {
481  POSTGIS_DEBUG(3, "geometry is null");
482  isnull[1] = true;
483  }
484 
485  if (natts > 2 && ctx->ctx->properties_len > 0)
486  decode_properties(ctx, values, isnull);
487 
488  heapTuple = heap_form_tuple(ctx->tupdesc, values, isnull);
489  ctx->result = HeapTupleGetDatum(heapTuple);
490  ctx->fid++;
491 
492  POSTGIS_DEBUGF(3, "fid now %d", ctx->fid);
493 
494  if (ctx->ctx->offset == ctx->ctx->size) {
495  POSTGIS_DEBUGF(3, "reached end at %ld", ctx->ctx->offset);
496  ctx->done = true;
497  }
498 }
499 
503 struct flatgeobuf_agg_ctx *flatgeobuf_agg_ctx_init(const char *geom_name, const bool create_index)
504 {
505  struct flatgeobuf_agg_ctx *ctx;
506  size_t size = VARHDRSZ + FLATGEOBUF_MAGICBYTES_SIZE;
507  ctx = palloc0(sizeof(*ctx));
508  ctx->ctx = palloc0(sizeof(flatgeobuf_ctx));
509  ctx->ctx->buf = lwalloc(size);
510  memcpy(ctx->ctx->buf + VARHDRSZ, flatgeobuf_magicbytes, FLATGEOBUF_MAGICBYTES_SIZE);
511  ctx->geom_name = geom_name;
512  ctx->geom_index = 0;
513  ctx->ctx->features_count = 0;
514  ctx->ctx->offset = size;
515  ctx->tupdesc = NULL;
516  ctx->ctx->create_index = create_index;
517  return ctx;
518 }
519 
528 {
529  LWGEOM *lwgeom = NULL;
530  bool isnull = false;
531  Datum datum;
532  GSERIALIZED *gs;
533 
534  if (ctx->ctx->features_count == 0)
536 
537  datum = GetAttributeByNum(ctx->row, ctx->geom_index + 1, &isnull);
538  if (!isnull) {
539  gs = (GSERIALIZED *) PG_DETOAST_DATUM_COPY(datum);
540  lwgeom = lwgeom_from_gserialized(gs);
541  }
542  ctx->ctx->lwgeom = lwgeom;
543 
544  if (ctx->ctx->features_count == 0)
545  flatgeobuf_encode_header(ctx->ctx);
546 
548  if (ctx->ctx->create_index)
550  flatgeobuf_encode_feature(ctx->ctx);
551 }
552 
559 {
560  POSTGIS_DEBUGF(3, "called at offset %ld", ctx->ctx->offset);
561  if (ctx == NULL)
562  flatgeobuf_agg_ctx_init(NULL, false);
563  // header only result
564  if (ctx->ctx->features_count == 0) {
565  flatgeobuf_encode_header(ctx->ctx);
566  } else if (ctx->ctx->create_index) {
567  ctx->ctx->index_node_size = 16;
568  flatgeobuf_create_index(ctx->ctx);
569  }
570  if (ctx->tupdesc != NULL)
571  ReleaseTupleDesc(ctx->tupdesc);
572  SET_VARSIZE(ctx->ctx->buf, ctx->ctx->offset);
573  POSTGIS_DEBUGF(3, "returning at offset %ld", ctx->ctx->offset);
574  return ctx->ctx->buf;
575 }
uint8_t * flatgeobuf_agg_finalfn(struct flatgeobuf_agg_ctx *ctx)
Finalize aggregation.
Definition: flatgeobuf.c:558
void flatgeobuf_check_magicbytes(struct flatgeobuf_decode_ctx *ctx)
Definition: flatgeobuf.c:269
static void inspect_table(struct flatgeobuf_agg_ctx *ctx)
Definition: flatgeobuf.c:69
static void encode_properties(flatgeobuf_agg_ctx *ctx)
Definition: flatgeobuf.c:153
static uint8_t get_column_type(Oid typoid)
Definition: flatgeobuf.c:37
static void ensure_items_len(struct flatgeobuf_agg_ctx *ctx)
Definition: flatgeobuf.c:139
static void ensure_properties_size(struct flatgeobuf_agg_ctx *ctx, size_t size)
Definition: flatgeobuf.c:123
struct flatgeobuf_agg_ctx * flatgeobuf_agg_ctx_init(const char *geom_name, const bool create_index)
Initialize aggregation context.
Definition: flatgeobuf.c:503
void flatgeobuf_agg_transfn(struct flatgeobuf_agg_ctx *ctx)
Aggregation step.
Definition: flatgeobuf.c:527
static void decode_properties(struct flatgeobuf_decode_ctx *ctx, Datum *values, bool *isnull)
Definition: flatgeobuf.c:280
void flatgeobuf_decode_row(struct flatgeobuf_decode_ctx *ctx)
Definition: flatgeobuf.c:465
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
Definition: gserialized.c:239
void * lwalloc(size_t size)
Definition: lwutil.c:227
int value
Definition: genraster.py:62
type
Definition: ovdump.py:42
data
Definition: ovdump.py:104
unsigned int int32
Definition: shpopen.c:54
HeapTupleHeader row
Definition: flatgeobuf.h:52
const char * geom_name
Definition: flatgeobuf.h:49
flatgeobuf_ctx * ctx
Definition: flatgeobuf.h:48
uint32_t geom_index
Definition: flatgeobuf.h:50
TupleDesc tupdesc
Definition: flatgeobuf.h:51
flatgeobuf_ctx * ctx
Definition: flatgeobuf.h:62