PGROUTING  3.2
trsp.c
Go to the documentation of this file.
1 /*PGR-GNU*****************************************************************
2 
3 File: trsp.c
4 
5 Generated with Template by:
6 Copyright (c) 2013 pgRouting developers
8 
9 ------
10 
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
15 
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20 
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 
25  ********************************************************************PGR-GNU*/
26 
27 
29 #include "catalog/pg_type.h"
30 #include "c_common/debug_macro.h"
31 
32 
33 #include "c_types/trsp/trsp.h"
34 
35 PGDLLEXPORT Datum _pgr_trsp(PG_FUNCTION_ARGS);
36 
37 
38 typedef struct edge_columns {
39  int id;
40  int source;
41  int target;
42  int cost;
45 
46 typedef struct restrict_columns {
47  int target_id;
48  int via_path;
49  int to_cost;
51 
52 
53 
54 
55 static int
56 finish(int code, int ret) {
57  PGR_DBG("In finish, trying to disconnect from spi %d", ret);
58  code = SPI_finish();
59  if (code != SPI_OK_FINISH) {
60  elog(ERROR, "couldn't disconnect from SPI");
61  return -1;
62  }
63  return ret;
64 }
65 
66 /*
67  * This function fetches the resturction columns from an SPITupleTable..
68  *
69 */
70 static int
71 fetch_restrict_columns(SPITupleTable *tuptable,
73  restrict_columns->target_id = SPI_fnumber(tuptable->tupdesc, "target_id");
74  restrict_columns->via_path = SPI_fnumber(tuptable->tupdesc, "via_path");
75  restrict_columns->to_cost = SPI_fnumber(tuptable->tupdesc, "to_cost");
76  if (restrict_columns->target_id == SPI_ERROR_NOATTRIBUTE ||
77  restrict_columns->via_path == SPI_ERROR_NOATTRIBUTE ||
78  restrict_columns->to_cost == SPI_ERROR_NOATTRIBUTE) {
79  elog(ERROR, "Error, restriction query must return columns "
80  "'target_id', 'via_path' and 'to_cost'");
81  return -1;
82  }
83 
84  if (SPI_gettypeid(tuptable->tupdesc,
85  restrict_columns->target_id) != INT4OID ||
86  SPI_gettypeid(tuptable->tupdesc, restrict_columns->via_path) != TEXTOID ||
87  SPI_gettypeid(tuptable->tupdesc, restrict_columns->to_cost) != FLOAT8OID) {
88  elog(ERROR, "Error, restriction columns 'target_id' must be of type int4,"
89  "'via_path' must be of type text, 'to_cost' must be of type float8");
90  return -1;
91  }
92 
93  return 0;
94 }
95 
96 /*
97  * This function fetches the edge columns from the SPITupleTable.
98  *
99 */
100 static int
102  bool has_reverse_cost) {
103  edge_columns->id = SPI_fnumber(tuptable->tupdesc, "id");
104  edge_columns->source = SPI_fnumber(tuptable->tupdesc, "source");
105  edge_columns->target = SPI_fnumber(tuptable->tupdesc, "target");
106  edge_columns->cost = SPI_fnumber(tuptable->tupdesc, "cost");
107  if (edge_columns->id == SPI_ERROR_NOATTRIBUTE ||
108  edge_columns->source == SPI_ERROR_NOATTRIBUTE ||
109  edge_columns->target == SPI_ERROR_NOATTRIBUTE ||
110  edge_columns->cost == SPI_ERROR_NOATTRIBUTE) {
111  elog(ERROR, "Error, query must return columns "
112  "'id', 'source', 'target' and 'cost'");
113  return -1;
114  }
115 
116  if (SPI_gettypeid(tuptable->tupdesc, edge_columns->source) != INT4OID ||
117  SPI_gettypeid(tuptable->tupdesc, edge_columns->target) != INT4OID ||
118  SPI_gettypeid(tuptable->tupdesc, edge_columns->cost) != FLOAT8OID) {
119  elog(ERROR, "Error, columns 'source', 'target' must be of type int4, "
120  "'cost' must be of type float8");
121  return -1;
122  }
123 
124  PGR_DBG("columns: id %i source %i target %i cost %i",
127 
128  if (has_reverse_cost) {
129  edge_columns->reverse_cost = SPI_fnumber(tuptable->tupdesc,
130  "reverse_cost");
131 
132  if (edge_columns->reverse_cost == SPI_ERROR_NOATTRIBUTE) {
133  elog(ERROR, "Error, reverse_cost is used, but query did't return "
134  "'reverse_cost' column");
135  return -1;
136  }
137 
138  if (SPI_gettypeid(tuptable->tupdesc, edge_columns->reverse_cost)
139  != FLOAT8OID) {
140  elog(ERROR, "Error, columns 'reverse_cost' must be of type float8");
141  return -1;
142  }
143 
144  PGR_DBG("columns: reverse_cost cost %i", edge_columns->reverse_cost);
145  }
146 
147  return 0;
148 }
149 
150 /*
151  * To fetch a edge from Tuple.
152  *
153  */
154 
155 static void
156 fetch_edge(HeapTuple *tuple, TupleDesc *tupdesc,
157  edge_columns_t *edge_columns, edge_t *target_edge) {
158  Datum binval;
159  bool isnull;
160 
161  binval = SPI_getbinval(*tuple, *tupdesc, edge_columns->id, &isnull);
162  if (isnull)
163  elog(ERROR, "id contains a null value");
164  target_edge->id = DatumGetInt32(binval);
165 
166  binval = SPI_getbinval(*tuple, *tupdesc, edge_columns->source, &isnull);
167  if (isnull)
168  elog(ERROR, "source contains a null value");
169  target_edge->source = DatumGetInt32(binval);
170 
171  binval = SPI_getbinval(*tuple, *tupdesc, edge_columns->target, &isnull);
172  if (isnull)
173  elog(ERROR, "target contains a null value");
174  target_edge->target = DatumGetInt32(binval);
175 
176  binval = SPI_getbinval(*tuple, *tupdesc, edge_columns->cost, &isnull);
177  if (isnull)
178  elog(ERROR, "cost contains a null value");
179  target_edge->cost = DatumGetFloat8(binval);
180 
181  if (edge_columns->reverse_cost != -1) {
182  binval = SPI_getbinval(*tuple, *tupdesc, edge_columns->reverse_cost,
183  &isnull);
184  if (isnull)
185  elog(ERROR, "reverse_cost contains a null value");
186  target_edge->reverse_cost = DatumGetFloat8(binval);
187  }
188 }
189 
190 
191 /*
192  * To fetch a edge from Tuple.
193  *
194  */
195 
196 static void
197 fetch_restrict(HeapTuple *tuple, TupleDesc *tupdesc,
199  Datum binval;
200  bool isnull;
201  int t;
202 
203  for (t = 0; t < MAX_RULE_LENGTH; ++t)
204  rest->via[t] = -1;
205 
206  binval = SPI_getbinval(*tuple, *tupdesc, restrict_columns->target_id,
207  &isnull);
208  if (isnull)
209  elog(ERROR, "target_id contains a null value");
210  rest->target_id = DatumGetInt32(binval);
211 
212  binval = SPI_getbinval(*tuple, *tupdesc, restrict_columns->to_cost, &isnull);
213  if (isnull)
214  elog(ERROR, "to_cost contains a null value");
215  rest->to_cost = DatumGetFloat8(binval);
216  char *str = DatumGetCString(SPI_getvalue(*tuple, *tupdesc,
218 
219  // PGR_DBG("restriction: %f, %i, %s", rest->to_cost, rest->target_id, str);
220 
221  if (str != NULL) {
222  int ci = 0;
223  char* pch = (char *)strtok(str, " ,");
224 
225  while (pch != NULL && ci < MAX_RULE_LENGTH) {
226  rest->via[ci] = atoi(pch);
227  // PGR_DBG(" rest->via[%i]=%i", ci, rest->via[ci]);
228  ci++;
229  pch = (char *)strtok(NULL, " ,");
230  }
231  }
232 }
233 
234 
235 
236 static int compute_trsp(
237  char* sql,
238  int dovertex,
239  int64_t start_id,
240  double start_pos,
241  int64_t end_id,
242  double end_pos,
243  bool directed,
244  bool has_reverse_cost,
245  char* restrict_sql,
246  path_element_tt **path,
247  size_t *path_count) {
248 
249  int SPIcode;
250  SPIPlanPtr SPIplan;
251  Portal SPIportal;
252  bool moredata = true;
253  uint32_t TUPLIMIT = 1000;
254  uint32_t ntuples;
255 
256  edge_t *edges = NULL;
257  uint32_t total_tuples = 0;
258 #ifndef _MSC_VER
259  edge_columns_t edge_columns = {.id = -1, .source = -1, .target = -1,
260  .cost = -1, .reverse_cost = -1};
261 #else // _MSC_VER
262  edge_columns_t edge_columns = {-1, -1, -1, -1, -1};
263 #endif // _MSC_VER
264  restrict_t *restricts = NULL;
265  uint32_t total_restrict_tuples = 0;
266  restrict_columns_t restrict_columns = {.target_id = -1, .via_path = -1,
267  .to_cost = -1};
268  int64_t v_max_id = 0;
269  int64_t v_min_id = INT_MAX;
270 
271  /* track if start and end are both in edge tuples */
272  int s_count = 0;
273  int t_count = 0;
274 
275  char *err_msg;
276  int ret = -1;
277  uint32_t z;
278 
279  PGR_DBG("start turn_restrict_shortest_path\n");
280 
281  SPIcode = SPI_connect();
282  if (SPIcode != SPI_OK_CONNECT) {
283  elog(ERROR, "turn_restrict_shortest_path: "
284  "couldn't open a connection to SPI");
285  return -1;
286  }
287 
288  SPIplan = SPI_prepare(sql, 0, NULL);
289  if (SPIplan == NULL) {
290  elog(ERROR, "turn_restrict_shortest_path: "
291  "couldn't create query plan via SPI");
292  return -1;
293  }
294 
295  if ((SPIportal = SPI_cursor_open(NULL, SPIplan, NULL, NULL, true)) == NULL) {
296  elog(ERROR, "turn_restrict_shortest_path: "
297  "SPI_cursor_open('%s') returns NULL", sql);
298  return -1;
299  }
300 
301  while (moredata == true) {
302  // PGR_DBG("calling SPI_cursor_fetch");
303  SPI_cursor_fetch(SPIportal, true, TUPLIMIT);
304 
305  if (SPI_tuptable == NULL) {
306  elog(ERROR, "SPI_tuptable is NULL");
307  return finish(SPIcode, -1);
308  }
309 
310  if (edge_columns.id == -1) {
311  if (fetch_edge_columns(SPI_tuptable, &edge_columns,
312  has_reverse_cost) == -1)
313  return finish(SPIcode, ret);
314  }
315 
316  ntuples = SPI_processed;
317 
318  total_tuples += ntuples;
319 
320  if (ntuples > 0) {
321  if (!edges)
322  edges = palloc(total_tuples * sizeof(edge_t));
323  else
324  edges = repalloc(edges, total_tuples * sizeof(edge_t));
325 
326  if (edges == NULL) {
327  elog(ERROR, "Out of memory");
328  return finish(SPIcode, ret);
329  }
330 
331  uint32_t t;
332  SPITupleTable *tuptable = SPI_tuptable;
333  TupleDesc tupdesc = SPI_tuptable->tupdesc;
334 
335  for (t = 0; t < ntuples; t++) {
336  HeapTuple tuple = tuptable->vals[t];
337  fetch_edge(&tuple, &tupdesc, &edge_columns,
338  &edges[total_tuples - ntuples + t]);
339  }
340  SPI_freetuptable(tuptable);
341  } else {
342  moredata = false;
343  }
344  }
345  SPI_cursor_close(SPIportal);
346 
347  // defining min and max vertex id
348 
349  for (z = 0; z < total_tuples; z++) {
350  if (edges[z].source < v_min_id)
351  v_min_id = edges[z].source;
352 
353  if (edges[z].source > v_max_id)
354  v_max_id = edges[z].source;
355 
356  if (edges[z].target < v_min_id)
357  v_min_id = edges[z].target;
358 
359  if (edges[z].target > v_max_id)
360  v_max_id = edges[z].target;
361  }
362 
363  // ::::::::::::::::::::::::::::::::::::
364  // :: reducing vertex id (renumbering)
365  // ::::::::::::::::::::::::::::::::::::
366  for (z = 0; z < total_tuples; z++) {
367  // check if edges[] contains source and target
368  if (dovertex) {
369  if (edges[z].source == start_id || edges[z].target == start_id)
370  ++s_count;
371  if (edges[z].source == end_id || edges[z].target == end_id)
372  ++t_count;
373  } else {
374  if (edges[z].id == start_id)
375  ++s_count;
376  if (edges[z].id == end_id)
377  ++t_count;
378  }
379 
380  edges[z].source -= v_min_id;
381  edges[z].target -= v_min_id;
382  }
383 
384  PGR_DBG("Min vertex id: %ld , Max vid: %ld", v_min_id, v_max_id);
385  PGR_DBG("Total %i edge tuples", total_tuples);
386 
387  if (s_count == 0) {
388  elog(ERROR, "Start id was not found.");
389  return -1;
390  }
391 
392  if (t_count == 0) {
393  elog(ERROR, "Target id was not found.");
394  return -1;
395  }
396 
397  if (dovertex) {
398  start_id -= v_min_id;
399  end_id -= v_min_id;
400  }
401 
402  PGR_DBG("Fetching restriction tuples\n");
403 
404  if (restrict_sql == NULL) {
405  PGR_DBG("Sql for restrictions is null.");
406  } else {
407  SPIplan = SPI_prepare(restrict_sql, 0, NULL);
408  if (SPIplan == NULL) {
409  elog(ERROR, "turn_restrict_shortest_path: "
410  "couldn't create query plan via SPI");
411  return -1;
412  }
413 
414  if ((SPIportal = SPI_cursor_open(NULL, SPIplan, NULL, NULL, true)) \
415  == NULL) {
416  elog(ERROR, "turn_restrict_shortest_path:"
417  " SPI_cursor_open('%s') returns NULL", restrict_sql);
418  return -1;
419  }
420 
421  moredata = true;
422  while (moredata == true) {
423  SPI_cursor_fetch(SPIportal, true, TUPLIMIT);
424 
425  if (restrict_columns.target_id == -1) {
426  if (fetch_restrict_columns(SPI_tuptable, &restrict_columns) \
427  == -1) {
428  PGR_DBG("fetch_restrict_columns failed!");
429  return finish(SPIcode, ret);
430  }
431  }
432 
433  /* Suppress the -Wconversion warning temporarily */
434  #pragma GCC diagnostic push
435  #pragma GCC diagnostic ignored "-Wconversion"
436  ntuples = SPI_processed;
437  #pragma GCC diagnostic pop
438 
439  total_restrict_tuples += ntuples;
440 
441  if (ntuples > 0) {
442  if (!restricts)
443  restricts = palloc(total_restrict_tuples * sizeof(restrict_t));
444  else
445  restricts = repalloc(restricts,
446  total_restrict_tuples * sizeof(restrict_t));
447 
448  if (restricts == NULL) {
449  elog(ERROR, "Out of memory");
450  return finish(SPIcode, ret);
451  }
452 
453  uint32_t t;
454  SPITupleTable *tuptable = SPI_tuptable;
455  TupleDesc tupdesc = SPI_tuptable->tupdesc;
456 
457  for (t = 0; t < ntuples; t++) {
458  HeapTuple tuple = tuptable->vals[t];
459  fetch_restrict(&tuple, &tupdesc, &restrict_columns,
460  &restricts[total_restrict_tuples - ntuples + t]);
461  }
462  SPI_freetuptable(tuptable);
463  } else {
464  moredata = false;
465  }
466  }
467  SPI_cursor_close(SPIportal);
468  }
469 
470  PGR_DBG("Total %i restriction tuples", total_restrict_tuples);
471 
472  PGR_DBG("Calling trsp_edge_wrapper\n");
473  ret = trsp_edge_wrapper(edges, total_tuples,
474  restricts, total_restrict_tuples,
475  start_id, start_pos, end_id, end_pos,
476  directed, has_reverse_cost,
477  path, path_count, &err_msg);
478 
479  PGR_DBG("Message received from inside:");
480  PGR_DBG("%s", err_msg);
481 
482 
483  // ::::::::::::::::::::::::::::::::
484  // :: restoring original vertex id
485  // ::::::::::::::::::::::::::::::::
486  for (z = 0; z < *path_count; z++) {
487  if (z || (*path)[z].vertex_id != -1)
488  (*path)[z].vertex_id += v_min_id;
489  }
490 
491  PGR_DBG("ret = %i\n", ret);
492 
493  PGR_DBG("*path_count = %ld\n", *path_count);
494 
495  if (ret < 0) {
496  ereport(ERROR, (errcode(ERRCODE_E_R_E_CONTAINING_SQL_NOT_PERMITTED),
497  errmsg("Error computing path: %s", err_msg)));
498  }
499 
500  return finish(SPIcode, ret);
501 }
502 
503 
505 PGDLLEXPORT Datum
506 _pgr_trsp(PG_FUNCTION_ARGS) {
507  FuncCallContext *funcctx;
508  TupleDesc tuple_desc;
509  path_element_tt *path;
510 
511  // stuff done only on the first call of the function
512  if (SRF_IS_FIRSTCALL()) {
513  MemoryContext oldcontext;
514  size_t path_count = 0;
515  int i;
516  double s_pos;
517  double e_pos;
518  char * sql;
519 
520  // create a function context for cross-call persistence
521  funcctx = SRF_FIRSTCALL_INIT();
522 
523  // switch to memory context appropriate for multiple function calls
524  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
525 
526  // verify that the first 5 args are not NULL
527  for (i = 0; i < 7; i++) {
528  if (i == 2 || i == 4) continue;
529  if (PG_ARGISNULL(i)) {
530  elog(ERROR, "turn_restrict_shortest_path(): "
531  "Argument %i may not be NULL", i+1);
532  }
533  }
534 
535  if (PG_ARGISNULL(2)) {
536  s_pos = 0.5;
537  } else {
538  s_pos = PG_GETARG_FLOAT8(2);
539  if (s_pos < 0.0) s_pos = 0.5;
540  if (s_pos > 1.0) s_pos = 0.5;
541  }
542 
543  if (PG_ARGISNULL(4)) {
544  e_pos = 0.5;
545  } else {
546  e_pos = PG_GETARG_FLOAT8(4);
547  if (e_pos < 0.0) e_pos = 0.5;
548  if (e_pos > 1.0) e_pos = 0.5;
549  }
550 
551  if (PG_ARGISNULL(7)) {
552  sql = NULL;
553  } else {
554  sql = text_to_cstring(PG_GETARG_TEXT_P(7));
555  if (strlen(sql) == 0)
556  sql = NULL;
557  }
558 
559  PGR_DBG("Calling compute_trsp");
560 
561  compute_trsp(text_to_cstring(PG_GETARG_TEXT_P(0)),
562  0, // sdo edge
563  PG_GETARG_INT32(1),
564  s_pos,
565  PG_GETARG_INT32(3),
566  e_pos,
567  PG_GETARG_BOOL(5),
568  PG_GETARG_BOOL(6),
569  sql,
570  &path, &path_count);
571 
572  // total number of tuples to be returned
573 #if PGSQL_VERSION > 95
574  funcctx->max_calls = path_count;
575 #else
576  funcctx->max_calls = (uint32_t)path_count;
577 #endif
578 
579  funcctx->user_fctx = path;
580  if (get_call_result_type(fcinfo, NULL, &tuple_desc)
581  != TYPEFUNC_COMPOSITE) {
582  ereport(ERROR,
583  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
584  errmsg("function returning record called in context "
585  "that cannot accept type record")));
586  }
587 
588  funcctx->tuple_desc = tuple_desc;
589 
590  MemoryContextSwitchTo(oldcontext);
591  }
592 
593  // stuff done on every call of the function
594  funcctx = SRF_PERCALL_SETUP();
595 
596  tuple_desc = funcctx->tuple_desc;
597  path = (path_element_tt*) funcctx->user_fctx;
598 
599  if (funcctx->call_cntr < funcctx->max_calls) {
600  // do when there is more left to send
601  HeapTuple tuple;
602  Datum result;
603  Datum *values;
604  bool* nulls;
605 
606  values = palloc(4 * sizeof(Datum));
607  nulls = palloc(4 * sizeof(char));
608 
609  values[0] = Int32GetDatum(funcctx->call_cntr);
610  nulls[0] = false;
611  values[1] = Int32GetDatum(path[funcctx->call_cntr].vertex_id);
612  nulls[1] = false;
613  values[2] = Int32GetDatum(path[funcctx->call_cntr].edge_id);
614  nulls[2] = false;
615  values[3] = Float8GetDatum(path[funcctx->call_cntr].cost);
616  nulls[3] = false;
617 
618  tuple = heap_form_tuple(tuple_desc, values, nulls);
619 
620  // make the tuple into a datum
621  result = HeapTupleGetDatum(tuple);
622 
623  // clean up (this is not really necessary)
624  pfree(values);
625  pfree(nulls);
626 
627  SRF_RETURN_NEXT(funcctx, result);
628  } else { // do when there is no more left
629  PGR_DBG("Going to free path");
630  if (path) free(path);
631  SRF_RETURN_DONE(funcctx);
632  }
633 }
postgres_connection.h
edge_t::source
int64_t source
Definition: trsp_types.h:39
path_element::edge_id
int64 edge_id
Definition: trsp.h:58
fetch_restrict_columns
static int fetch_restrict_columns(SPITupleTable *tuptable, restrict_columns_t *restrict_columns)
Definition: trsp.c:71
edge_t
Definition: trsp_types.h:37
edge_t::target
int64_t target
Definition: trsp_types.h:40
edge_t::reverse_cost
double reverse_cost
Definition: trsp_types.h:42
path_element
Definition: trsp.h:56
edge_columns_t
struct edge_columns edge_columns_t
restrict_struct
Definition: trsp.h:49
MAX_RULE_LENGTH
#define MAX_RULE_LENGTH
Definition: trsp.h:29
restrict_struct::to_cost
float8 to_cost
Definition: trsp.h:51
edge_columns::target
int target
Definition: trsp.c:41
debug_macro.h
compute_trsp
static int compute_trsp(char *sql, int dovertex, int64_t start_id, double start_pos, int64_t end_id, double end_pos, bool directed, bool has_reverse_cost, char *restrict_sql, path_element_tt **path, size_t *path_count)
Definition: trsp.c:236
fetch_edge
static void fetch_edge(HeapTuple *tuple, TupleDesc *tupdesc, edge_columns_t *edge_columns, edge_t *target_edge)
Definition: trsp.c:156
restrict_struct::target_id
int target_id
Definition: trsp.h:50
PGR_DBG
#define PGR_DBG(...)
Definition: debug_macro.h:34
restrict_columns::to_cost
int to_cost
Definition: trsp.c:49
trsp_edge_wrapper
int trsp_edge_wrapper(edge_t *edges, size_t edge_count, restrict_t *restricts, size_t restrict_count, int64_t start_edge, double start_pos, int64_t end_edge, double end_pos, bool directed, bool has_reverse_cost, path_element_tt **path, size_t *path_count, char **err_msg)
Definition: trsp_core.cpp:37
restrict_columns_t
struct restrict_columns restrict_columns_t
edge_t::id
int64_t id
Definition: trsp_types.h:38
path_element::cost
float8 cost
Definition: trsp.h:59
restrict_columns::via_path
int via_path
Definition: trsp.c:48
PG_FUNCTION_INFO_V1
PG_FUNCTION_INFO_V1(_pgr_trsp)
edge_columns::id
int id
Definition: trsp.c:39
if
if(DOXYGEN_FOUND) configure_file($
Definition: doxygen/CMakeLists.txt:13
fetch_edge_columns
static int fetch_edge_columns(SPITupleTable *tuptable, edge_columns_t *edge_columns, bool has_reverse_cost)
Definition: trsp.c:101
finish
static int finish(int code, int ret)
Definition: trsp.c:56
trsp.h
restrict_columns
Definition: trsp.c:46
fetch_restrict
static void fetch_restrict(HeapTuple *tuple, TupleDesc *tupdesc, restrict_columns_t *restrict_columns, restrict_t *rest)
Definition: trsp.c:197
restrict_columns::target_id
int target_id
Definition: trsp.c:47
path_element::vertex_id
int64 vertex_id
Definition: trsp.h:57
edge_columns::cost
int cost
Definition: trsp.c:42
edge_columns::reverse_cost
int reverse_cost
Definition: trsp.c:43
restrict_struct::via
int via[5]
Definition: trsp.h:52
_pgr_trsp
PGDLLEXPORT Datum _pgr_trsp(PG_FUNCTION_ARGS)
Definition: trsp.c:506
edge_columns
Definition: trsp.c:38
edge_columns::source
int source
Definition: trsp.c:40
edge_t::cost
double cost
Definition: trsp_types.h:41