PostGIS 3.6.2dev-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#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
35typedef 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
50static 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);
54static void array_to_json_internal(Datum array, StringInfo result,
55 bool use_line_feeds);
56static 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);
65static void composite_to_json(Datum composite, StringInfo result,
66 bool use_line_feeds);
67static void datum_to_json(Datum val, bool is_null, StringInfo result,
68 JsonTypeCategory tcategory, Oid outfuncoid,
69 bool key_scalar);
70static void json_categorize_type(Oid typoid,
71 JsonTypeCategory *tcategory,
72 Oid *outfuncoid);
73static char * postgis_JsonEncodeDateTime(char *buf, Datum value, Oid typid);
74static int postgis_time2tm(TimeADT time, struct pg_tm *tm, fsec_t *fsec);
75static int postgis_timetz2tm(TimeTzADT *time, struct pg_tm *tm, fsec_t *fsec, int *tzp);
76
77Datum row_to_geojson(PG_FUNCTION_ARGS);
78extern Datum LWGEOM_asGeoJson(PG_FUNCTION_ARGS);
79
80/*
81 * SQL function row_to_geojson(row)
82 */
84Datum
85ST_AsGeoJsonRow(PG_FUNCTION_ARGS)
86{
87 Datum array = PG_GETARG_DATUM(0);
88 text *geom_column_text = PG_GETARG_TEXT_P(1);
89 char *geom_column = PG_ARGISNULL(1) ? "" : text_to_cstring(geom_column_text);
90 int32 maxdecimaldigits = PG_GETARG_INT32(2);
91 bool do_pretty = PG_GETARG_BOOL(3);
92 text *id_column_text = PG_GETARG_TEXT_P(4);
93 char *id_column = PG_ARGISNULL(4) ? "" : text_to_cstring(id_column_text);
94 StringInfoData result;
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 initStringInfo(&result);
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 */
118static void
119composite_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 */
282static 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 */
384static void
385datum_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;
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;
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 */
489static void
490array_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 */
536static void
537composite_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 */
607static void
608array_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
646static int
647postgis_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
659static int
660postgis_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
677static char *
678postgis_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)
unsigned int int32
Definition shpopen.c:54