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