root/branches/debug/extra/driving_distance/src/alpha.c

Revision 128, 9.7 KB (checked in by anton, 3 years ago)

Debugging branch added

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