2 #include "../postgis_config.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"
26 #include "common/jsonapi.h"
28 #include "utils/typcache.h"
29 #include "utils/syscache.h"
32 #include "lwgeom_pg.h"
52 char key[NAMEDATALEN];
69 char *geom_column_name,
71 int32 maxdecimaldigits,
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);
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);
106 Oid geom_oid = InvalidOid;
107 Oid geog_oid = InvalidOid;
110 postgis_initialize_cache();
111 geom_oid = postgis_oid(GEOMETRYOID);
112 geog_oid = postgis_oid(GEOGRAPHYOID);
114 if (strlen(geom_column) == 0)
116 if (strlen(id_column) == 0)
123 PG_RETURN_TEXT_P(cstring_to_text_with_len(
result.data,
result.len));
132 char *geom_column_name,
133 char *id_column_name,
134 int32 maxdecimaldigits,
144 HeapTupleData tmptup, *tuple;
146 bool needsep =
false;
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;
155 MemSet(&ctl, 0,
sizeof(ctl));
156 ctl.keysize = NAMEDATALEN;
158 ctl.hcxt = CurrentMemoryContext;
160 sep = use_line_feeds ?
",\n " :
", ";
162 td = DatumGetHeapTupleHeader(composite);
165 tupType = HeapTupleHeaderGetTypeId(td);
166 tupTypmod = HeapTupleHeaderGetTypMod(td);
167 tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
176 #if POSTGIS_PGSQL_VERSION <= 130
177 hash_create(
"GeoJSON property keys", Max(tupdesc->natts, 8), &ctl, HASH_ELEM | HASH_CONTEXT);
179 hash_create(
"GeoJSON property keys", Max(tupdesc->natts, 8), &ctl, HASH_ELEM | HASH_CONTEXT | HASH_STRINGS);
183 tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
187 appendStringInfoString(
result,
"{\"type\": \"Feature\", \"geometry\": ");
189 for (i = 0; i < tupdesc->natts; i++)
196 Form_pg_attribute att = TupleDescAttr(tupdesc, i);
197 bool is_geom_column =
false;
198 bool is_id_column =
false;
200 if (att->attisdropped)
203 attname = NameStr(att->attname);
205 if (geom_column_name)
206 is_geom_column = (strcmp(attname, geom_column_name) == 0);
208 is_geom_column = (att->atttypid == geom_oid || att->atttypid == geog_oid);
211 is_id_column = (strcmp(attname, id_column_name) == 0);
213 if ((!geom_column_found) && is_geom_column)
216 geom_column_found =
true;
218 val = heap_getattr(tuple, i + 1, tupdesc, &isnull);
228 Int32GetDatum(maxdecimaldigits))));
232 appendStringInfoString(
result,
"null");
235 else if (is_id_column)
237 id_column_found =
true;
239 val = heap_getattr(tuple, i + 1, tupdesc, &isnull);
244 outfuncoid = InvalidOid;
249 datum_to_json(val, isnull,
id, tcategory, outfuncoid,
false);
256 appendStringInfoString(props, sep);
259 (void)hash_search(prop_keys, attname, HASH_ENTER, &found);
264 (errmsg(
"duplicate key \"%s\" encountered while building GeoJSON properties",
266 errhint(
"Only the last value for each key is preserved when casting to JSONB.")));
269 escape_json(props, attname);
270 appendStringInfoString(props,
": ");
272 val = heap_getattr(tuple, i + 1, tupdesc, &isnull);
277 outfuncoid = InvalidOid;
282 datum_to_json(val, isnull, props, tcategory, outfuncoid,
false);
286 if (!geom_column_found)
287 ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg(
"geometry column is missing")));
291 if (!id_column_found)
293 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
294 errmsg(
"Specified id column \"%s\" is missing", id_column_name)));
296 appendStringInfoString(
result,
", \"id\": ");
297 appendStringInfo(
result,
"%s", id->data);
300 appendStringInfoString(
result,
", \"properties\": {");
301 appendStringInfo(
result,
"%s", props->data);
303 appendStringInfoString(
result,
"}}");
304 hash_destroy(prop_keys);
305 ReleaseTupleDesc(tupdesc);
331 typoid = getBaseType(typoid);
333 *outfuncoid = InvalidOid;
353 getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
371 getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
377 if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID
378 || typoid == RECORDARRAYOID)
380 else if (type_is_rowtype(typoid))
387 if (typoid >= FirstNormalObjectId)
390 CoercionPathType ctype;
392 ctype = find_coercion_pathway(JSONOID, typoid,
395 if (ctype == COERCION_PATH_FUNC && OidIsValid(castfunc))
398 *outfuncoid = castfunc;
403 getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
409 getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
436 Assert(!(key_scalar && is_null));
440 appendStringInfoString(
result,
"null");
450 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
451 errmsg(
"key value must be scalar, not array, composite, or json")));
462 outputstr = DatumGetBool(val) ?
"true" :
"false";
464 escape_json(
result, outputstr);
466 appendStringInfoString(
result, outputstr);
469 outputstr = OidOutputFunctionCall(outfuncoid, val);
475 if (!key_scalar && IsValidJsonNumber(outputstr, strlen(outputstr)))
476 appendStringInfoString(
result, outputstr);
478 escape_json(
result, outputstr);
483 char buf[MAXDATELEN + 1];
486 appendStringInfo(
result,
"\"%s\"", buf);
491 char buf[MAXDATELEN + 1];
494 appendStringInfo(
result,
"\"%s\"", buf);
499 char buf[MAXDATELEN + 1];
502 appendStringInfo(
result,
"\"%s\"", buf);
507 outputstr = OidOutputFunctionCall(outfuncoid, val);
508 appendStringInfoString(
result, outputstr);
513 jsontext = DatumGetTextPP(OidFunctionCall1(outfuncoid, val));
514 outputstr = text_to_cstring(jsontext);
515 appendStringInfoString(
result, outputstr);
520 outputstr = OidOutputFunctionCall(outfuncoid, val);
521 escape_json(
result, outputstr);
533 ArrayType *v = DatumGetArrayTypeP(array);
534 Oid element_type = ARR_ELEMTYPE(v);
549 nitems = ArrayGetNItems(ndim, dim);
553 appendStringInfoString(
result,
"[]");
557 get_typlenbyvalalign(element_type,
558 &typlen, &typbyval, &typalign);
561 &tcategory, &outfuncoid);
563 deconstruct_array(v, element_type, typlen, typbyval,
564 typalign, &elements, &nulls,
568 outfuncoid, use_line_feeds);
584 HeapTupleData tmptup,
587 bool needsep =
false;
590 sep = use_line_feeds ?
",\n " :
",";
592 td = DatumGetHeapTupleHeader(composite);
595 tupType = HeapTupleHeaderGetTypeId(td);
596 tupTypmod = HeapTupleHeaderGetTypMod(td);
597 tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
600 tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
604 appendStringInfoChar(
result,
'{');
606 for (i = 0; i < tupdesc->natts; i++)
613 Form_pg_attribute att = TupleDescAttr(tupdesc, i);
615 if (att->attisdropped)
619 appendStringInfoString(
result, sep);
622 attname = NameStr(att->attname);
623 escape_json(
result, attname);
624 appendStringInfoChar(
result,
':');
626 val = heap_getattr(tuple, i + 1, tupdesc, &isnull);
631 outfuncoid = InvalidOid;
639 appendStringInfoChar(
result,
'}');
640 ReleaseTupleDesc(tupdesc);
651 Oid outfuncoid,
bool use_line_feeds)
658 sep = use_line_feeds ?
",\n " :
",";
660 appendStringInfoChar(
result,
'[');
662 for (i = 1; i <= dims[dim]; i++)
665 appendStringInfoString(
result, sep);
667 if (dim + 1 == ndims)
680 valcount, tcategory, outfuncoid,
false);
684 appendStringInfoChar(
result,
']');
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;
703 TimeOffset trem = time->time;
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;
722 buf = palloc(MAXDATELEN + 1);
731 date = DatumGetDateADT(
value);
734 if (DATE_NOT_FINITE(date))
735 EncodeSpecialDate(date, buf);
738 j2date(date + POSTGRES_EPOCH_JDATE,
739 &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
740 EncodeDateOnly(&tm, USE_XSD_DATES, buf);
746 TimeADT time = DatumGetTimeADT(
value);
753 EncodeTimeOnly(tm, fsec,
false, 0, USE_XSD_DATES, buf);
758 TimeTzADT *time = DatumGetTimeTzADTP(
value);
766 EncodeTimeOnly(tm, fsec,
true, tz, USE_XSD_DATES, buf);
775 timestamp = DatumGetTimestamp(
value);
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);
783 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
784 errmsg(
"timestamp out of range")));
789 TimestampTz timestamp;
793 const char *tzn = NULL;
795 timestamp = DatumGetTimestampTz(
value);
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);
803 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
804 errmsg(
"timestamp out of range")));
808 elog(ERROR,
"unknown jsonb value datetime type oid %d", typid);
char result[OUT_DOUBLE_BUFFER_SIZE]
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)
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)