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