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