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