root/tags/release-1.0-beta/alpha.c

Revision 39, 9.2 KB (checked in by anton, 3 years ago)

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