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