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