PostGIS  3.7.0dev-r@@SVN_REVISION@@

◆ composite_to_geojson()

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 
)
static

Definition at line 130 of file lwgeom_out_geojson.c.

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 }
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition: cu_print.c:267
struct GeoJsonPropKey GeoJsonPropKey
static void json_categorize_type(Oid typoid, JsonTypeCategory *tcategory, Oid *outfuncoid)
Datum LWGEOM_asGeoJson(PG_FUNCTION_ARGS)
JsonTypeCategory
@ JSONTYPE_NULL
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

References datum_to_json(), json_categorize_type(), JSONTYPE_NULL, LWGEOM_asGeoJson(), and result.

Referenced by ST_AsGeoJsonRow().

Here is the call graph for this function:
Here is the caller graph for this function: