PostGIS  3.0.6dev-r@@SVN_REVISION@@
lwgeom_out_geojson.c
Go to the documentation of this file.
1 
2 #include "../postgis_config.h"
3 
4 /* PostgreSQL headers */
5 #include "postgres.h"
6 #include "funcapi.h"
7 #include "miscadmin.h"
8 #include "access/htup_details.h"
9 #include "access/transam.h"
10 #include "catalog/pg_type.h"
11 #include "executor/spi.h"
12 #include "lib/stringinfo.h"
13 #include "libpq/pqformat.h"
14 #include "mb/pg_wchar.h"
15 #include "parser/parse_coerce.h"
16 #include "utils/array.h"
17 #include "utils/builtins.h"
18 #include "utils/date.h"
19 #include "utils/datetime.h"
20 #include "utils/lsyscache.h"
21 #include "utils/json.h"
22 #if POSTGIS_PGSQL_VERSION < 130
23 #include "utils/jsonapi.h"
24 #else
25 #include "common/jsonapi.h"
26 #endif
27 #include "utils/typcache.h"
28 #include "utils/syscache.h"
29 
30 /* PostGIS headers */
31 #include "lwgeom_pg.h"
32 #include "lwgeom_log.h"
33 #include "liblwgeom.h"
34 
35 
36 typedef enum /* type categories for datum_to_json */
37 {
38  JSONTYPE_NULL, /* null, so we didn't bother to identify */
39  JSONTYPE_BOOL, /* boolean (built-in types only) */
40  JSONTYPE_NUMERIC, /* numeric (ditto) */
41  JSONTYPE_DATE, /* we use special formatting for datetimes */
44  JSONTYPE_JSON, /* JSON itself (and JSONB) */
45  JSONTYPE_ARRAY, /* array */
46  JSONTYPE_COMPOSITE, /* composite */
47  JSONTYPE_CAST, /* something with an explicit cast to JSON */
48  JSONTYPE_OTHER /* all else */
50 
51 static void array_dim_to_json(StringInfo result, int dim, int ndims, int *dims,
52  Datum *vals, bool *nulls, int *valcount,
53  JsonTypeCategory tcategory, Oid outfuncoid,
54  bool use_line_feeds);
55 static void array_to_json_internal(Datum array, StringInfo result,
56  bool use_line_feeds);
57 static void composite_to_geojson(Datum composite,
58  char *geom_column_name, int32 maxdecimaldigits,
59  StringInfo result, bool use_line_feeds,
60  Oid geom_oid, Oid geog_oid);
61 static void composite_to_json(Datum composite, StringInfo result,
62  bool use_line_feeds);
63 static void datum_to_json(Datum val, bool is_null, StringInfo result,
64  JsonTypeCategory tcategory, Oid outfuncoid,
65  bool key_scalar);
66 static void json_categorize_type(Oid typoid,
67  JsonTypeCategory *tcategory,
68  Oid *outfuncoid);
69 static char * postgis_JsonEncodeDateTime(char *buf, Datum value, Oid typid);
70 static int postgis_time2tm(TimeADT time, struct pg_tm *tm, fsec_t *fsec);
71 static int postgis_timetz2tm(TimeTzADT *time, struct pg_tm *tm, fsec_t *fsec, int *tzp);
72 
73 Datum row_to_geojson(PG_FUNCTION_ARGS);
74 extern Datum LWGEOM_asGeoJson(PG_FUNCTION_ARGS);
75 
76 /*
77  * SQL function row_to_geojson(row)
78  */
80 Datum
81 ST_AsGeoJsonRow(PG_FUNCTION_ARGS)
82 {
83  Datum array = PG_GETARG_DATUM(0);
84  text *geom_column_text = PG_GETARG_TEXT_P(1);
85  int32 maxdecimaldigits = PG_GETARG_INT32(2);
86  bool do_pretty = PG_GETARG_BOOL(3);
87  StringInfo result;
88  char *geom_column = text_to_cstring(geom_column_text);
89  Oid geom_oid = InvalidOid;
90  Oid geog_oid = InvalidOid;
91 
92  /* We need to initialize the internal cache to access it later via postgis_oid() */
93  postgis_initialize_cache(fcinfo);
94  geom_oid = postgis_oid(GEOMETRYOID);
95  geog_oid = postgis_oid(GEOGRAPHYOID);
96 
97  if (strlen(geom_column) == 0)
98  geom_column = NULL;
99 
100  result = makeStringInfo();
101 
102  composite_to_geojson(array, geom_column, maxdecimaldigits, result, do_pretty, geom_oid, geog_oid);
103 
104  PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
105 }
106 
107 /*
108  * Turn a composite / record into GEOJSON.
109  */
110 static void
111 composite_to_geojson(Datum composite, char *geom_column_name, int32 maxdecimaldigits,
112  StringInfo result, bool use_line_feeds, Oid geom_oid, Oid geog_oid)
113 {
114  HeapTupleHeader td;
115  Oid tupType;
116  int32 tupTypmod;
117  TupleDesc tupdesc;
118  HeapTupleData tmptup,
119  *tuple;
120  int i;
121  bool needsep = false;
122  const char *sep;
123  StringInfo props = makeStringInfo();
124  bool geom_column_found = false;
125 
126  sep = use_line_feeds ? ",\n " : ", ";
127 
128  td = DatumGetHeapTupleHeader(composite);
129 
130  /* Extract rowtype info and find a tupdesc */
131  tupType = HeapTupleHeaderGetTypeId(td);
132  tupTypmod = HeapTupleHeaderGetTypMod(td);
133  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
134 
135  /* Build a temporary HeapTuple control structure */
136  tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
137  tmptup.t_data = td;
138  tuple = &tmptup;
139 
140  appendStringInfoString(result, "{\"type\": \"Feature\", \"geometry\": ");
141 
142  for (i = 0; i < tupdesc->natts; i++)
143  {
144  Datum val;
145  bool isnull;
146  char *attname;
147  JsonTypeCategory tcategory;
148  Oid outfuncoid;
149  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
150  bool is_geom_column = false;
151 
152  if (att->attisdropped)
153  continue;
154 
155  attname = NameStr(att->attname);
156  /* Use the column name if provided, use the first geometry column otherwise */
157  if (geom_column_name)
158  is_geom_column = (strcmp(attname, geom_column_name) == 0);
159  else
160  is_geom_column = (att->atttypid == geom_oid || att->atttypid == geog_oid);
161 
162  if ((!geom_column_found) && is_geom_column)
163  {
164  /* this is our geom column */
165  geom_column_found = true;
166 
167  val = heap_getattr(tuple, i + 1, tupdesc, &isnull);
168  if (!isnull)
169  {
170  appendStringInfo(result, "%s",
171  TextDatumGetCString(DirectFunctionCall2(LWGEOM_asGeoJson, val, Int32GetDatum(maxdecimaldigits))));
172  }
173  else
174  {
175  appendStringInfoString(result, "{\"type\": null}");
176  }
177  }
178  else
179  {
180  if (needsep)
181  appendStringInfoString(props, sep);
182  needsep = true;
183 
184  escape_json(props, attname);
185  appendStringInfoString(props, ": ");
186 
187  val = heap_getattr(tuple, i + 1, tupdesc, &isnull);
188 
189  if (isnull)
190  {
191  tcategory = JSONTYPE_NULL;
192  outfuncoid = InvalidOid;
193  }
194  else
195  json_categorize_type(att->atttypid, &tcategory, &outfuncoid);
196 
197  datum_to_json(val, isnull, props, tcategory, outfuncoid, false);
198  }
199  }
200 
201  if (!geom_column_found)
202  ereport(ERROR,
203  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
204  errmsg("geometry column is missing")));
205 
206  appendStringInfoString(result, ", \"properties\": {");
207  appendStringInfo(result, "%s", props->data);
208 
209  appendStringInfoString(result, "}}");
210  ReleaseTupleDesc(tupdesc);
211 }
212 
213 /*
214  * The following code was all cut and pasted directly from
215  * json.c from the Postgres source tree as of 2019-03-28.
216  * It would be far better if these were exported from the
217  * backend so we could just use them here. Maybe someday.
218  */
219 
220 /*
221  * Determine how we want to print values of a given type in datum_to_json.
222  *
223  * Given the datatype OID, return its JsonTypeCategory, as well as the type's
224  * output function OID. If the returned category is JSONTYPE_CAST, we
225  * return the OID of the type->JSON cast function instead.
226  */
227 static void
229  JsonTypeCategory *tcategory,
230  Oid *outfuncoid)
231 {
232  bool typisvarlena;
233 
234  /* Look through any domain */
235  typoid = getBaseType(typoid);
236 
237  *outfuncoid = InvalidOid;
238 
239  /*
240  * We need to get the output function for everything except date and
241  * timestamp types, array and composite types, booleans, and non-builtin
242  * types where there's a cast to json.
243  */
244 
245  switch (typoid)
246  {
247  case BOOLOID:
248  *tcategory = JSONTYPE_BOOL;
249  break;
250 
251  case INT2OID:
252  case INT4OID:
253  case INT8OID:
254  case FLOAT4OID:
255  case FLOAT8OID:
256  case NUMERICOID:
257  getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
258  *tcategory = JSONTYPE_NUMERIC;
259  break;
260 
261  case DATEOID:
262  *tcategory = JSONTYPE_DATE;
263  break;
264 
265  case TIMESTAMPOID:
266  *tcategory = JSONTYPE_TIMESTAMP;
267  break;
268 
269  case TIMESTAMPTZOID:
270  *tcategory = JSONTYPE_TIMESTAMPTZ;
271  break;
272 
273  case JSONOID:
274  case JSONBOID:
275  getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
276  *tcategory = JSONTYPE_JSON;
277  break;
278 
279  default:
280  /* Check for arrays and composites */
281  if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID
282  || typoid == RECORDARRAYOID)
283  *tcategory = JSONTYPE_ARRAY;
284  else if (type_is_rowtype(typoid)) /* includes RECORDOID */
285  *tcategory = JSONTYPE_COMPOSITE;
286  else
287  {
288  /* It's probably the general case ... */
289  *tcategory = JSONTYPE_OTHER;
290  /* but let's look for a cast to json, if it's not built-in */
291  if (typoid >= FirstNormalObjectId)
292  {
293  Oid castfunc;
294  CoercionPathType ctype;
295 
296  ctype = find_coercion_pathway(JSONOID, typoid,
297  COERCION_EXPLICIT,
298  &castfunc);
299  if (ctype == COERCION_PATH_FUNC && OidIsValid(castfunc))
300  {
301  *tcategory = JSONTYPE_CAST;
302  *outfuncoid = castfunc;
303  }
304  else
305  {
306  /* non builtin type with no cast */
307  getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
308  }
309  }
310  else
311  {
312  /* any other builtin type */
313  getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
314  }
315  }
316  break;
317  }
318 }
319 
320 /*
321  * Turn a Datum into JSON text, appending the string to "result".
322  *
323  * tcategory and outfuncoid are from a previous call to json_categorize_type,
324  * except that if is_null is true then they can be invalid.
325  *
326  * If key_scalar is true, the value is being printed as a key, so insist
327  * it's of an acceptable type, and force it to be quoted.
328  */
329 static void
330 datum_to_json(Datum val, bool is_null, StringInfo result,
331  JsonTypeCategory tcategory, Oid outfuncoid,
332  bool key_scalar)
333 {
334  char *outputstr;
335  text *jsontext;
336 
337  check_stack_depth();
338 
339  /* callers are expected to ensure that null keys are not passed in */
340  Assert(!(key_scalar && is_null));
341 
342  if (is_null)
343  {
344  appendStringInfoString(result, "null");
345  return;
346  }
347 
348  if (key_scalar &&
349  (tcategory == JSONTYPE_ARRAY ||
350  tcategory == JSONTYPE_COMPOSITE ||
351  tcategory == JSONTYPE_JSON ||
352  tcategory == JSONTYPE_CAST))
353  ereport(ERROR,
354  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
355  errmsg("key value must be scalar, not array, composite, or json")));
356 
357  switch (tcategory)
358  {
359  case JSONTYPE_ARRAY:
360  array_to_json_internal(val, result, false);
361  break;
362  case JSONTYPE_COMPOSITE:
363  composite_to_json(val, result, false);
364  break;
365  case JSONTYPE_BOOL:
366  outputstr = DatumGetBool(val) ? "true" : "false";
367  if (key_scalar)
368  escape_json(result, outputstr);
369  else
370  appendStringInfoString(result, outputstr);
371  break;
372  case JSONTYPE_NUMERIC:
373  outputstr = OidOutputFunctionCall(outfuncoid, val);
374 
375  /*
376  * Don't call escape_json for a non-key if it's a valid JSON
377  * number.
378  */
379  if (!key_scalar && IsValidJsonNumber(outputstr, strlen(outputstr)))
380  appendStringInfoString(result, outputstr);
381  else
382  escape_json(result, outputstr);
383  pfree(outputstr);
384  break;
385  case JSONTYPE_DATE:
386  {
387  char buf[MAXDATELEN + 1];
388 
389  postgis_JsonEncodeDateTime(buf, val, DATEOID);
390  appendStringInfo(result, "\"%s\"", buf);
391  }
392  break;
393  case JSONTYPE_TIMESTAMP:
394  {
395  char buf[MAXDATELEN + 1];
396 
397  postgis_JsonEncodeDateTime(buf, val, TIMESTAMPOID);
398  appendStringInfo(result, "\"%s\"", buf);
399  }
400  break;
402  {
403  char buf[MAXDATELEN + 1];
404 
405  postgis_JsonEncodeDateTime(buf, val, TIMESTAMPTZOID);
406  appendStringInfo(result, "\"%s\"", buf);
407  }
408  break;
409  case JSONTYPE_JSON:
410  /* JSON and JSONB output will already be escaped */
411  outputstr = OidOutputFunctionCall(outfuncoid, val);
412  appendStringInfoString(result, outputstr);
413  pfree(outputstr);
414  break;
415  case JSONTYPE_CAST:
416  /* outfuncoid refers to a cast function, not an output function */
417  jsontext = DatumGetTextPP(OidFunctionCall1(outfuncoid, val));
418  outputstr = text_to_cstring(jsontext);
419  appendStringInfoString(result, outputstr);
420  pfree(outputstr);
421  pfree(jsontext);
422  break;
423  default:
424  outputstr = OidOutputFunctionCall(outfuncoid, val);
425  escape_json(result, outputstr);
426  pfree(outputstr);
427  break;
428  }
429 }
430 
431 /*
432  * Turn an array into JSON.
433  */
434 static void
435 array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
436 {
437  ArrayType *v = DatumGetArrayTypeP(array);
438  Oid element_type = ARR_ELEMTYPE(v);
439  int *dim;
440  int ndim;
441  int nitems;
442  int count = 0;
443  Datum *elements;
444  bool *nulls;
445  int16 typlen;
446  bool typbyval;
447  char typalign;
448  JsonTypeCategory tcategory;
449  Oid outfuncoid;
450 
451  ndim = ARR_NDIM(v);
452  dim = ARR_DIMS(v);
453  nitems = ArrayGetNItems(ndim, dim);
454 
455  if (nitems <= 0)
456  {
457  appendStringInfoString(result, "[]");
458  return;
459  }
460 
461  get_typlenbyvalalign(element_type,
462  &typlen, &typbyval, &typalign);
463 
464  json_categorize_type(element_type,
465  &tcategory, &outfuncoid);
466 
467  deconstruct_array(v, element_type, typlen, typbyval,
468  typalign, &elements, &nulls,
469  &nitems);
470 
471  array_dim_to_json(result, 0, ndim, dim, elements, nulls, &count, tcategory,
472  outfuncoid, use_line_feeds);
473 
474  pfree(elements);
475  pfree(nulls);
476 }
477 
478 /*
479  * Turn a composite / record into JSON.
480  */
481 static void
482 composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
483 {
484  HeapTupleHeader td;
485  Oid tupType;
486  int32 tupTypmod;
487  TupleDesc tupdesc;
488  HeapTupleData tmptup,
489  *tuple;
490  int i;
491  bool needsep = false;
492  const char *sep;
493 
494  sep = use_line_feeds ? ",\n " : ",";
495 
496  td = DatumGetHeapTupleHeader(composite);
497 
498  /* Extract rowtype info and find a tupdesc */
499  tupType = HeapTupleHeaderGetTypeId(td);
500  tupTypmod = HeapTupleHeaderGetTypMod(td);
501  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
502 
503  /* Build a temporary HeapTuple control structure */
504  tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
505  tmptup.t_data = td;
506  tuple = &tmptup;
507 
508  appendStringInfoChar(result, '{');
509 
510  for (i = 0; i < tupdesc->natts; i++)
511  {
512  Datum val;
513  bool isnull;
514  char *attname;
515  JsonTypeCategory tcategory;
516  Oid outfuncoid;
517  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
518 
519  if (att->attisdropped)
520  continue;
521 
522  if (needsep)
523  appendStringInfoString(result, sep);
524  needsep = true;
525 
526  attname = NameStr(att->attname);
527  escape_json(result, attname);
528  appendStringInfoChar(result, ':');
529 
530  val = heap_getattr(tuple, i + 1, tupdesc, &isnull);
531 
532  if (isnull)
533  {
534  tcategory = JSONTYPE_NULL;
535  outfuncoid = InvalidOid;
536  }
537  else
538  json_categorize_type(att->atttypid, &tcategory, &outfuncoid);
539 
540  datum_to_json(val, isnull, result, tcategory, outfuncoid, false);
541  }
542 
543  appendStringInfoChar(result, '}');
544  ReleaseTupleDesc(tupdesc);
545 }
546 
547 /*
548  * Process a single dimension of an array.
549  * If it's the innermost dimension, output the values, otherwise call
550  * ourselves recursively to process the next dimension.
551  */
552 static void
553 array_dim_to_json(StringInfo result, int dim, int ndims, int *dims, Datum *vals,
554  bool *nulls, int *valcount, JsonTypeCategory tcategory,
555  Oid outfuncoid, bool use_line_feeds)
556 {
557  int i;
558  const char *sep;
559 
560  Assert(dim < ndims);
561 
562  sep = use_line_feeds ? ",\n " : ",";
563 
564  appendStringInfoChar(result, '[');
565 
566  for (i = 1; i <= dims[dim]; i++)
567  {
568  if (i > 1)
569  appendStringInfoString(result, sep);
570 
571  if (dim + 1 == ndims)
572  {
573  datum_to_json(vals[*valcount], nulls[*valcount], result, tcategory,
574  outfuncoid, false);
575  (*valcount)++;
576  }
577  else
578  {
579  /*
580  * Do we want line feeds on inner dimensions of arrays? For now
581  * we'll say no.
582  */
583  array_dim_to_json(result, dim + 1, ndims, dims, vals, nulls,
584  valcount, tcategory, outfuncoid, false);
585  }
586  }
587 
588  appendStringInfoChar(result, ']');
589 }
590 
591 static int
592 postgis_time2tm(TimeADT time, struct pg_tm *tm, fsec_t *fsec)
593 {
594  tm->tm_hour = time / USECS_PER_HOUR;
595  time -= tm->tm_hour * USECS_PER_HOUR;
596  tm->tm_min = time / USECS_PER_MINUTE;
597  time -= tm->tm_min * USECS_PER_MINUTE;
598  tm->tm_sec = time / USECS_PER_SEC;
599  time -= tm->tm_sec * USECS_PER_SEC;
600  *fsec = time;
601  return 0;
602 }
603 
604 static int
605 postgis_timetz2tm(TimeTzADT *time, struct pg_tm *tm, fsec_t *fsec, int *tzp)
606 {
607  TimeOffset trem = time->time;
608 
609  tm->tm_hour = trem / USECS_PER_HOUR;
610  trem -= tm->tm_hour * USECS_PER_HOUR;
611  tm->tm_min = trem / USECS_PER_MINUTE;
612  trem -= tm->tm_min * USECS_PER_MINUTE;
613  tm->tm_sec = trem / USECS_PER_SEC;
614  *fsec = trem - tm->tm_sec * USECS_PER_SEC;
615 
616  if (tzp != NULL)
617  *tzp = time->zone;
618 
619  return 0;
620 }
621 
622 static char *
623 postgis_JsonEncodeDateTime(char *buf, Datum value, Oid typid)
624 {
625  if (!buf)
626  buf = palloc(MAXDATELEN + 1);
627 
628  switch (typid)
629  {
630  case DATEOID:
631  {
632  DateADT date;
633  struct pg_tm tm;
634 
635  date = DatumGetDateADT(value);
636 
637  /* Same as date_out(), but forcing DateStyle */
638  if (DATE_NOT_FINITE(date))
639  EncodeSpecialDate(date, buf);
640  else
641  {
642  j2date(date + POSTGRES_EPOCH_JDATE,
643  &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
644  EncodeDateOnly(&tm, USE_XSD_DATES, buf);
645  }
646  }
647  break;
648  case TIMEOID:
649  {
650  TimeADT time = DatumGetTimeADT(value);
651  struct pg_tm tt,
652  *tm = &tt;
653  fsec_t fsec;
654 
655  /* Same as time_out(), but forcing DateStyle */
656  postgis_time2tm(time, tm, &fsec);
657  EncodeTimeOnly(tm, fsec, false, 0, USE_XSD_DATES, buf);
658  }
659  break;
660  case TIMETZOID:
661  {
662  TimeTzADT *time = DatumGetTimeTzADTP(value);
663  struct pg_tm tt,
664  *tm = &tt;
665  fsec_t fsec;
666  int tz;
667 
668  /* Same as timetz_out(), but forcing DateStyle */
669  postgis_timetz2tm(time, tm, &fsec, &tz);
670  EncodeTimeOnly(tm, fsec, true, tz, USE_XSD_DATES, buf);
671  }
672  break;
673  case TIMESTAMPOID:
674  {
675  Timestamp timestamp;
676  struct pg_tm tm;
677  fsec_t fsec;
678 
679  timestamp = DatumGetTimestamp(value);
680  /* Same as timestamp_out(), but forcing DateStyle */
681  if (TIMESTAMP_NOT_FINITE(timestamp))
682  EncodeSpecialTimestamp(timestamp, buf);
683  else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
684  EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
685  else
686  ereport(ERROR,
687  (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
688  errmsg("timestamp out of range")));
689  }
690  break;
691  case TIMESTAMPTZOID:
692  {
693  TimestampTz timestamp;
694  struct pg_tm tm;
695  int tz;
696  fsec_t fsec;
697  const char *tzn = NULL;
698 
699  timestamp = DatumGetTimestampTz(value);
700  /* Same as timestamptz_out(), but forcing DateStyle */
701  if (TIMESTAMP_NOT_FINITE(timestamp))
702  EncodeSpecialTimestamp(timestamp, buf);
703  else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
704  EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
705  else
706  ereport(ERROR,
707  (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
708  errmsg("timestamp out of range")));
709  }
710  break;
711  default:
712  elog(ERROR, "unknown jsonb value datetime type oid %d", typid);
713  return NULL;
714  }
715 
716  return buf;
717 }
This library is the generic geometry handling section of PostGIS.
Datum row_to_geojson(PG_FUNCTION_ARGS)
static int postgis_timetz2tm(TimeTzADT *time, struct pg_tm *tm, fsec_t *fsec, int *tzp)
static int postgis_time2tm(TimeADT time, struct pg_tm *tm, fsec_t *fsec)
static void json_categorize_type(Oid typoid, JsonTypeCategory *tcategory, Oid *outfuncoid)
PG_FUNCTION_INFO_V1(ST_AsGeoJsonRow)
Datum LWGEOM_asGeoJson(PG_FUNCTION_ARGS)
JsonTypeCategory
@ JSONTYPE_JSON
@ JSONTYPE_NULL
@ JSONTYPE_TIMESTAMP
@ JSONTYPE_NUMERIC
@ JSONTYPE_DATE
@ JSONTYPE_BOOL
@ JSONTYPE_OTHER
@ JSONTYPE_CAST
@ JSONTYPE_COMPOSITE
@ JSONTYPE_ARRAY
@ JSONTYPE_TIMESTAMPTZ
static void composite_to_geojson(Datum composite, char *geom_column_name, int32 maxdecimaldigits, StringInfo result, bool use_line_feeds, Oid geom_oid, Oid geog_oid)
static void array_dim_to_json(StringInfo result, int dim, int ndims, int *dims, Datum *vals, bool *nulls, int *valcount, JsonTypeCategory tcategory, Oid outfuncoid, bool use_line_feeds)
Datum ST_AsGeoJsonRow(PG_FUNCTION_ARGS)
static char * postgis_JsonEncodeDateTime(char *buf, Datum value, Oid typid)
static void composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
static void array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
static void datum_to_json(Datum val, bool is_null, StringInfo result, JsonTypeCategory tcategory, Oid outfuncoid, bool key_scalar)
int value
Definition: genraster.py:62
int count
Definition: genraster.py:57
char * text_to_cstring(const text *textptr)
unsigned int int32
Definition: shpopen.c:273