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