PostGIS  3.4.0dev-r@@SVN_REVISION@@
lwgeom_in_flatgeobuf.c
Go to the documentation of this file.
1 /**********************************************************************
2  *
3  * PostGIS - Spatial Types for PostgreSQL
4  * http://postgis.net
5  *
6  * PostGIS is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * PostGIS is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
18  *
19  **********************************************************************
20  *
21  * Copyright (C) 2021 Björn Harrtell <bjorn@wololo.org>
22  *
23  **********************************************************************/
24 
25 
26 #include <assert.h>
27 
28 #include "postgres.h"
29 
30 #include "../postgis_config.h"
31 #include "lwgeom_pg.h"
32 #include "liblwgeom.h"
33 #include "lwgeom_cache.h"
34 #include "funcapi.h"
35 #include <executor/spi.h>
36 #include <utils/builtins.h>
37 #include "flatgeobuf.h"
38 
39 static char *get_pgtype(uint8_t column_type) {
40  switch (column_type) {
41  case flatgeobuf_column_type_bool:
42  return "boolean";
43  case flatgeobuf_column_type_byte:
44  case flatgeobuf_column_type_ubyte:
45  return "smallint";
46  case flatgeobuf_column_type_short:
47  return "smallint";
48  case flatgeobuf_column_type_int:
49  return "integer";
50  case flatgeobuf_column_type_uint:
51  case flatgeobuf_column_type_long:
52  case flatgeobuf_column_type_ulong:
53  return "bigint";
54  case flatgeobuf_column_type_float:
55  return "real";
56  case flatgeobuf_column_type_double:
57  return "double precision";
58  case flatgeobuf_column_type_datetime:
59  return "timestamptz";
60  case flatgeobuf_column_type_string:
61  return "text";
62  case flatgeobuf_column_type_binary:
63  return "bytea";
64  case flatgeobuf_column_type_json:
65  return "jsonb";
66  }
67  elog(ERROR, "unknown column_type %d", column_type);
68 }
69 
71 Datum pgis_tablefromflatgeobuf(PG_FUNCTION_ARGS)
72 {
73  struct flatgeobuf_decode_ctx *ctx;
74  text *schema_input;
75  char *schema;
76  text *table_input;
77  char *table;
78  char *format;
79  char *sql;
80  bytea *data;
81  uint16_t i;
82  char **column_defs;
83  size_t column_defs_total_len;
84  char *column_defs_str;
85 
86  if (PG_ARGISNULL(0))
87  PG_RETURN_NULL();
88  if (PG_ARGISNULL(1))
89  PG_RETURN_NULL();
90 
91  schema_input = PG_GETARG_TEXT_P(0);
92  schema = text_to_cstring(schema_input);
93 
94  table_input = PG_GETARG_TEXT_P(1);
95  table = text_to_cstring(table_input);
96 
97  data = PG_GETARG_BYTEA_PP(2);
98 
99  ctx = palloc0(sizeof(*ctx));
100  ctx->ctx = palloc0(sizeof(flatgeobuf_ctx));
101  ctx->ctx->size = VARSIZE_ANY_EXHDR(data);
102  POSTGIS_DEBUGF(3, "bytea data size is %ld", ctx->ctx->size);
103  ctx->ctx->buf = lwalloc(ctx->ctx->size);
104  memcpy(ctx->ctx->buf, VARDATA_ANY(data), ctx->ctx->size);
105  ctx->ctx->offset = 0;
106 
108  flatgeobuf_decode_header(ctx->ctx);
109 
110  column_defs = palloc(sizeof(char *) * ctx->ctx->columns_size);
111  column_defs_total_len = 0;
112  POSTGIS_DEBUGF(2, "found %d columns", ctx->ctx->columns_size);
113  for (i = 0; i < ctx->ctx->columns_size; i++) {
114  flatgeobuf_column *column = ctx->ctx->columns[i];
115  const char *name = column->name;
116  uint8_t column_type = column->type;
117  char *pgtype = get_pgtype(column_type);
118  size_t len = strlen(name) + 1 + strlen(pgtype) + 1;
119  column_defs[i] = palloc0(sizeof(char) * len);
120  strcat(column_defs[i], name);
121  strcat(column_defs[i], " ");
122  strcat(column_defs[i], pgtype);
123  column_defs_total_len += len;
124  }
125  column_defs_str = palloc0(sizeof(char) * column_defs_total_len + (ctx->ctx->columns_size * 2) + 2 + 1);
126  if (ctx->ctx->columns_size > 0)
127  strcat(column_defs_str, ", ");
128  for (i = 0; i < ctx->ctx->columns_size; i++) {
129  strcat(column_defs_str, column_defs[i]);
130  if (i < ctx->ctx->columns_size - 1)
131  strcat(column_defs_str, ", ");
132  }
133 
134  POSTGIS_DEBUGF(2, "column_defs_str %s", column_defs_str);
135 
136  format = "create table %s.%s (id int, geom geometry%s)";
137  sql = palloc0(strlen(format) + strlen(schema) + strlen(table) + strlen(column_defs_str) + 1);
138 
139  sprintf(sql, format, schema, table, column_defs_str);
140 
141  POSTGIS_DEBUGF(3, "sql: %s", sql);
142 
143  if (SPI_connect() != SPI_OK_CONNECT)
144  elog(ERROR, "Failed to connect SPI");
145 
146  if (SPI_execute(sql, false, 0) != SPI_OK_UTILITY)
147  elog(ERROR, "Failed to create table");
148 
149  if (SPI_finish() != SPI_OK_FINISH)
150  elog(ERROR, "Failed to finish SPI");
151 
152  POSTGIS_DEBUG(3, "finished");
153 
154  PG_RETURN_NULL();
155 }
156 
157 // https://stackoverflow.com/questions/11740256/refactor-a-pl-pgsql-function-to-return-the-output-of-various-select-queries
159 Datum pgis_fromflatgeobuf(PG_FUNCTION_ARGS)
160 {
161  FuncCallContext *funcctx;
162 
163  TupleDesc tupdesc;
164  bytea *data;
165  MemoryContext oldcontext;
166 
167  struct flatgeobuf_decode_ctx *ctx;
168 
169  if (SRF_IS_FIRSTCALL()) {
170  funcctx = SRF_FIRSTCALL_INIT();
171  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
172 
173  funcctx->max_calls = 0;
174 
175  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
176  ereport(ERROR,
177  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
178  errmsg("function returning record called in context "
179  "that cannot accept type record")));
180 
181  data = PG_GETARG_BYTEA_PP(1);
182 
183  ctx = palloc0(sizeof(*ctx));
184  ctx->tupdesc = tupdesc;
185  ctx->ctx = palloc0(sizeof(flatgeobuf_ctx));
186  ctx->ctx->size = VARSIZE_ANY_EXHDR(data);
187  POSTGIS_DEBUGF(3, "VARSIZE_ANY_EXHDR %ld", ctx->ctx->size);
188  ctx->ctx->buf = palloc(ctx->ctx->size);
189  memcpy(ctx->ctx->buf, VARDATA_ANY(data), ctx->ctx->size);
190  ctx->ctx->offset = 0;
191  ctx->done = false;
192  ctx->fid = 0;
193 
194  funcctx->user_fctx = ctx;
195 
196  if (ctx->ctx->size == 0) {
197  POSTGIS_DEBUG(2, "no data");
198  MemoryContextSwitchTo(oldcontext);
199  SRF_RETURN_DONE(funcctx);
200  }
201 
203  flatgeobuf_decode_header(ctx->ctx);
204 
205  POSTGIS_DEBUGF(2, "header decoded now at offset %ld", ctx->ctx->offset);
206 
207  if (ctx->ctx->size == ctx->ctx->offset) {
208  POSTGIS_DEBUGF(2, "no feature data offset %ld", ctx->ctx->offset);
209  MemoryContextSwitchTo(oldcontext);
210  SRF_RETURN_DONE(funcctx);
211  }
212 
213  // TODO: get table and verify structure against header
214  MemoryContextSwitchTo(oldcontext);
215  }
216 
217  funcctx = SRF_PERCALL_SETUP();
218  ctx = funcctx->user_fctx;
219 
220  if (!ctx->done) {
222  POSTGIS_DEBUG(2, "Calling SRF_RETURN_NEXT");
223  SRF_RETURN_NEXT(funcctx, ctx->result);
224  } else {
225  POSTGIS_DEBUG(2, "Calling SRF_RETURN_DONE");
226  SRF_RETURN_DONE(funcctx);
227  }
228 }
void flatgeobuf_check_magicbytes(struct flatgeobuf_decode_ctx *ctx)
Definition: flatgeobuf.c:269
void flatgeobuf_decode_row(struct flatgeobuf_decode_ctx *ctx)
Definition: flatgeobuf.c:465
void * lwalloc(size_t size)
Definition: lwutil.c:227
This library is the generic geometry handling section of PostGIS.
Datum pgis_fromflatgeobuf(PG_FUNCTION_ARGS)
static char * get_pgtype(uint8_t column_type)
PG_FUNCTION_INFO_V1(pgis_tablefromflatgeobuf)
Datum pgis_tablefromflatgeobuf(PG_FUNCTION_ARGS)
data
Definition: ovdump.py:104
flatgeobuf_ctx * ctx
Definition: flatgeobuf.h:62