root/tags/release-1.0-rc1/extra/driving_distance/src/alpha.c

Revision 37, 9.3 KB (checked in by anton, 3 years ago)

Cmake files fixed

Line 
1/*
2 * Alpha-Shapes for PostgreSQL
3 *
4 * Copyright (c) 2006 Anton A. Patrushev, Orkney, Inc.
5 *
6 * This program 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 * This program 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 this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 *
20 */
21
22
23#include "postgres.h"
24#include "executor/spi.h"
25#include "funcapi.h"
26
27#include "alpha.h"
28
29/*
30 * Define this to have profiling enabled
31 */
32//#define PROFILE
33
34#ifdef PROFILE
35#include <sys/time.h>
36
37/*! \def MAX_NODES
38  \brief Maximal number of nodes in the path (to avoid infinite loops)
39*/
40#define MAX_NODES 1000000
41
42#ifdef PG_MODULE_MAGIC
43PG_MODULE_MAGIC;
44#endif
45
46struct timeval prof_alpha, prof_store, prof_extract, prof_total;
47long proftime[5];
48long profipts1, profipts2, profopts;
49#define profstart(x) do { gettimeofday(&x, NULL); } while (0);
50#define profstop(n, x) do { struct timeval _profstop;   \
51        long _proftime;                         \
52        gettimeofday(&_profstop, NULL);                         \
53        _proftime = ( _profstop.tv_sec*1000000+_profstop.tv_usec) -     \
54                ( x.tv_sec*1000000+x.tv_usec); \
55        elog(NOTICE, \
56                "PRF(%s) %lu (%f ms)", \
57                (n), \
58             _proftime, _proftime / 1000.0);    \
59        } while (0);
60
61#else
62
63#define profstart(x) do { } while (0);
64#define profstop(n, x) do { } while (0);
65
66
67#endif // PROFILE
68
69
70Datum alphashape(PG_FUNCTION_ARGS);
71
72#undef DEBUG
73//#define DEBUG 1
74
75#ifdef DEBUG
76#define DBG(format, arg...)                     \
77    elog(NOTICE, format , ## arg)
78#else
79#define DBG(format, arg...) do { ; } while (0)
80#endif
81   
82// The number of tuples to fetch from the SPI cursor at each iteration
83#define TUPLIMIT 1000
84
85static char *
86text2char(text *in)
87{
88  char *out = palloc(VARSIZE(in));
89   
90  memcpy(out, VARDATA(in), VARSIZE(in) - VARHDRSZ);
91  out[VARSIZE(in) - VARHDRSZ] = '\0';
92  return out;
93}
94
95static int
96finish(int code, int ret)
97{
98  code = SPI_finish();
99  if (code  != SPI_OK_FINISH )
100  {
101    elog(ERROR,"couldn't disconnect from SPI");
102    return -1 ;
103  }
104  return ret;
105}
106                 
107
108typedef struct vertex_columns
109{
110  int id;
111  int x;
112  int y;
113
114} vertex_columns_t;
115
116
117
118static int
119fetch_vertices_columns(SPITupleTable *tuptable,
120                       vertex_columns_t *vertex_columns)
121{
122  vertex_columns->id = SPI_fnumber(SPI_tuptable->tupdesc, "id");
123  vertex_columns->x = SPI_fnumber(SPI_tuptable->tupdesc, "x");
124  vertex_columns->y = SPI_fnumber(SPI_tuptable->tupdesc, "y");
125
126  if (vertex_columns->id == SPI_ERROR_NOATTRIBUTE ||
127      vertex_columns->x == SPI_ERROR_NOATTRIBUTE ||
128      vertex_columns->y == SPI_ERROR_NOATTRIBUTE)
129    {
130      elog(ERROR, "Error, query must return columns "
131           "'id', 'x' and 'y'");
132      return -1;
133    }
134
135  if (SPI_gettypeid(SPI_tuptable->tupdesc, vertex_columns->id) != INT4OID ||
136      SPI_gettypeid(SPI_tuptable->tupdesc, vertex_columns->x) != FLOAT8OID ||
137      SPI_gettypeid(SPI_tuptable->tupdesc, vertex_columns->y) != FLOAT8OID)
138    {
139      elog(ERROR,
140           "Error, column 'id' must be of type int4, 'x' and 'y' must be of type float8");
141      return -1;
142    }
143   
144  return 0;
145}
146
147static void
148fetch_vertex(HeapTuple *tuple, TupleDesc *tupdesc,
149             vertex_columns_t *vertex_columns, vertex_t *target_vertex)
150{
151  Datum binval;
152  bool isnull;
153
154  binval = SPI_getbinval(*tuple, *tupdesc, vertex_columns->x, &isnull);
155  if (isnull)
156    elog(ERROR, "x contains a null value");
157  target_vertex->x = DatumGetFloat8(binval);
158
159  binval = SPI_getbinval(*tuple, *tupdesc, vertex_columns->y, &isnull);
160  if (isnull)
161    elog(ERROR, "y contains a null value");
162  target_vertex->y = DatumGetFloat8(binval);
163}
164
165static int compute_alpha_shape(char* sql, vertex_t **res, int *res_count)
166{
167
168  int SPIcode;
169  void *SPIplan;
170  Portal SPIportal;
171  bool moredata = TRUE;
172  int ntuples;
173  vertex_t *vertices = NULL;
174  int total_tuples = 0;
175  vertex_columns_t vertex_columns = {id: -1, x: -1, y: -1};
176  char *err_msg;
177  int ret = -1;
178
179  DBG("start alpha_shape\n");
180       
181  SPIcode = SPI_connect();
182  if (SPIcode  != SPI_OK_CONNECT)
183    {
184      elog(ERROR, "alpha_shape: couldn't open a connection to SPI");
185      return -1;
186    }
187
188  SPIplan = SPI_prepare(sql, 0, NULL);
189  if (SPIplan  == NULL)
190    {
191      elog(ERROR, "alpha_shape: couldn't create query plan via SPI");
192      return -1;
193    }
194
195  if ((SPIportal = SPI_cursor_open(NULL, SPIplan, NULL, NULL, true)) == NULL)
196    {
197      elog(ERROR, "alpha_shape: SPI_cursor_open('%s') returns NULL", sql);
198      return -1;
199    }
200
201  while (moredata == TRUE)
202    {
203      SPI_cursor_fetch(SPIportal, TRUE, TUPLIMIT);
204
205      if (vertex_columns.id == -1)
206        {
207          if (fetch_vertices_columns(SPI_tuptable, &vertex_columns) == -1)
208            return finish(SPIcode, ret);
209        }
210
211      ntuples = SPI_processed;
212      total_tuples += ntuples;
213      if (!vertices)
214        vertices = palloc(total_tuples * sizeof(vertex_t));
215      else
216        vertices = repalloc(vertices, total_tuples * sizeof(vertex_t));
217
218      if (vertices == NULL)
219        {
220          elog(ERROR, "Out of memory");
221          return finish(SPIcode, ret);
222        }
223
224      if (ntuples > 0)
225        {
226          int t;
227          SPITupleTable *tuptable = SPI_tuptable;
228          TupleDesc tupdesc = SPI_tuptable->tupdesc;
229               
230          for (t = 0; t < ntuples; t++)
231            {
232              HeapTuple tuple = tuptable->vals[t];
233              fetch_vertex(&tuple, &tupdesc, &vertex_columns,
234                           &vertices[total_tuples - ntuples + t]);
235            }
236          SPI_freetuptable(tuptable);
237        }
238      else
239        {
240          moredata = FALSE;
241        }
242    }
243
244  DBG("Calling CGAL alpha-shape\n");
245       
246  profstop("extract", prof_extract);
247  profstart(prof_alpha);
248
249  ret = alpha_shape(vertices, total_tuples, res, res_count, &err_msg);
250
251  profstop("alpha", prof_alpha);
252  profstart(prof_store);
253
254  if (ret < 0)
255    {
256      //elog(ERROR, "Error computing shape: %s", err_msg);
257      ereport(ERROR, (errcode(ERRCODE_E_R_E_CONTAINING_SQL_NOT_PERMITTED), errmsg("Error computing shape: %s", err_msg)));
258    }
259 
260  return finish(SPIcode, ret);   
261}
262
263PG_FUNCTION_INFO_V1(alphashape);
264
265Datum alphashape(PG_FUNCTION_ARGS)
266{
267  FuncCallContext      *funcctx;
268  int                  call_cntr;
269  int                  max_calls;
270  TupleDesc            tuple_desc;
271  vertex_t     *res;
272                   
273  /* stuff done only on the first call of the function */
274  if (SRF_IS_FIRSTCALL())
275    {
276      MemoryContext   oldcontext;
277      int res_count;
278      int ret;
279                           
280      // XXX profiling messages are not thread safe
281      profstart(prof_total);
282      profstart(prof_extract);
283                                           
284      /* create a function context for cross-call persistence */
285      funcctx = SRF_FIRSTCALL_INIT();
286                                                                   
287      /* switch to memory context appropriate for multiple function calls */
288      oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
289
290      ret = compute_alpha_shape(text2char(PG_GETARG_TEXT_P(0)),
291                                &res, &res_count);
292
293      /* total number of tuples to be returned */
294      DBG("Conting tuples number\n");
295      funcctx->max_calls = res_count;
296      funcctx->user_fctx = res;
297
298      DBG("Total count %i", res_count);
299
300      funcctx->tuple_desc = BlessTupleDesc(
301                           RelationNameGetTupleDesc("vertex_result"));
302
303      MemoryContextSwitchTo(oldcontext);
304    }
305
306  /* stuff done on every call of the function */
307  DBG("Strange stuff doing\n");
308  funcctx = SRF_PERCALL_SETUP();
309
310  call_cntr = funcctx->call_cntr;
311  max_calls = funcctx->max_calls;
312  tuple_desc = funcctx->tuple_desc;
313  res = (vertex_t*) funcctx->user_fctx;
314
315  DBG("Trying to allocate some memory\n");
316
317  if (call_cntr < max_calls)    /* do when there is more left to send */
318    {
319      HeapTuple    tuple;
320      Datum        result;
321      Datum *values;
322      char* nulls;
323
324      DBG("Before allocation\n");
325
326      values = palloc(2 * sizeof(Datum));
327      nulls = palloc(2 * sizeof(char));
328
329      DBG("After allocation\n");
330
331      values[0] = Float8GetDatum(res[call_cntr].x);
332      nulls[0] = ' ';
333
334      values[1] = Float8GetDatum(res[call_cntr].y);
335      nulls[1] = ' ';
336
337      DBG("Heap making\n");
338
339      tuple = heap_formtuple(tuple_desc, values, nulls);
340
341      DBG("Datum making\n");
342
343      /* make the tuple into a datum */
344      result = HeapTupleGetDatum(tuple);
345
346      DBG("Trying to free some memory\n");
347
348      /* clean up (this is not really necessary) */
349      pfree(values);
350      pfree(nulls);
351
352      SRF_RETURN_NEXT(funcctx, result);
353    }
354  else    /* do when there is no more left */
355    {
356      profstop("store", prof_store);
357      profstop("total", prof_total);
358#ifdef PROFILE
359      elog(NOTICE, "_________");
360#endif
361      SRF_RETURN_DONE(funcctx);
362    }
363}
Note: See TracBrowser for help on using the browser.