29 #include "catalog/pg_type.h"
35 PGDLLEXPORT Datum
_pgr_trsp(PG_FUNCTION_ARGS);
57 PGR_DBG(
"In finish, trying to disconnect from spi %d", ret);
59 if (code != SPI_OK_FINISH) {
60 elog(ERROR,
"couldn't disconnect from SPI");
79 elog(ERROR,
"Error, restriction query must return columns "
80 "'target_id', 'via_path' and 'to_cost'");
84 if (SPI_gettypeid(tuptable->tupdesc,
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");
102 bool has_reverse_cost) {
111 elog(ERROR,
"Error, query must return columns "
112 "'id', 'source', 'target' and 'cost'");
119 elog(ERROR,
"Error, columns 'source', 'target' must be of type int4, "
120 "'cost' must be of type float8");
124 PGR_DBG(
"columns: id %i source %i target %i cost %i",
128 if (has_reverse_cost) {
133 elog(ERROR,
"Error, reverse_cost is used, but query did't return "
134 "'reverse_cost' column");
140 elog(ERROR,
"Error, columns 'reverse_cost' must be of type float8");
161 binval = SPI_getbinval(*tuple, *tupdesc,
edge_columns->
id, &isnull);
163 elog(ERROR,
"id contains a null value");
164 target_edge->
id = DatumGetInt32(binval);
168 elog(ERROR,
"source contains a null value");
169 target_edge->
source = DatumGetInt32(binval);
173 elog(ERROR,
"target contains a null value");
174 target_edge->
target = DatumGetInt32(binval);
178 elog(ERROR,
"cost contains a null value");
179 target_edge->
cost = DatumGetFloat8(binval);
185 elog(ERROR,
"reverse_cost contains a null value");
209 elog(ERROR,
"target_id contains a null value");
214 elog(ERROR,
"to_cost contains a null value");
215 rest->
to_cost = DatumGetFloat8(binval);
216 char *str = DatumGetCString(SPI_getvalue(*tuple, *tupdesc,
223 char* pch = (
char *)strtok(str,
" ,");
226 rest->
via[ci] = atoi(pch);
229 pch = (
char *)strtok(NULL,
" ,");
244 bool has_reverse_cost,
247 size_t *path_count) {
252 bool moredata =
true;
253 uint32_t TUPLIMIT = 1000;
257 uint32_t total_tuples = 0;
260 .cost = -1, .reverse_cost = -1};
265 uint32_t total_restrict_tuples = 0;
268 int64_t v_max_id = 0;
269 int64_t v_min_id = INT_MAX;
279 PGR_DBG(
"start turn_restrict_shortest_path\n");
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");
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");
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);
301 while (moredata ==
true) {
303 SPI_cursor_fetch(SPIportal,
true, TUPLIMIT);
305 if (SPI_tuptable == NULL) {
306 elog(ERROR,
"SPI_tuptable is NULL");
307 return finish(SPIcode, -1);
312 has_reverse_cost) == -1)
313 return finish(SPIcode, ret);
316 ntuples = SPI_processed;
318 total_tuples += ntuples;
322 edges = palloc(total_tuples *
sizeof(
edge_t));
324 edges = repalloc(edges, total_tuples *
sizeof(
edge_t));
327 elog(ERROR,
"Out of memory");
328 return finish(SPIcode, ret);
332 SPITupleTable *tuptable = SPI_tuptable;
333 TupleDesc tupdesc = SPI_tuptable->tupdesc;
335 for (t = 0; t < ntuples; t++) {
336 HeapTuple tuple = tuptable->vals[t];
338 &edges[total_tuples - ntuples + t]);
340 SPI_freetuptable(tuptable);
345 SPI_cursor_close(SPIportal);
349 for (z = 0; z < total_tuples; z++) {
350 if (edges[z].source < v_min_id)
351 v_min_id = edges[z].
source;
353 if (edges[z].source > v_max_id)
354 v_max_id = edges[z].
source;
356 if (edges[z].target < v_min_id)
357 v_min_id = edges[z].
target;
359 if (edges[z].target > v_max_id)
360 v_max_id = edges[z].
target;
366 for (z = 0; z < total_tuples; z++) {
369 if (edges[z].source == start_id || edges[z].target == start_id)
371 if (edges[z].source == end_id || edges[z].target == end_id)
374 if (edges[z].
id == start_id)
376 if (edges[z].
id == end_id)
380 edges[z].
source -= v_min_id;
381 edges[z].
target -= v_min_id;
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);
388 elog(ERROR,
"Start id was not found.");
393 elog(ERROR,
"Target id was not found.");
398 start_id -= v_min_id;
402 PGR_DBG(
"Fetching restriction tuples\n");
404 if (restrict_sql == NULL) {
405 PGR_DBG(
"Sql for restrictions is null.");
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");
414 if ((SPIportal = SPI_cursor_open(NULL, SPIplan, NULL, NULL,
true)) \
416 elog(ERROR,
"turn_restrict_shortest_path:"
417 " SPI_cursor_open('%s') returns NULL", restrict_sql);
422 while (moredata ==
true) {
423 SPI_cursor_fetch(SPIportal,
true, TUPLIMIT);
428 PGR_DBG(
"fetch_restrict_columns failed!");
429 return finish(SPIcode, ret);
434 #pragma GCC diagnostic push
435 #pragma GCC diagnostic ignored "-Wconversion"
436 ntuples = SPI_processed;
437 #pragma GCC diagnostic pop
439 total_restrict_tuples += ntuples;
443 restricts = palloc(total_restrict_tuples *
sizeof(
restrict_t));
445 restricts = repalloc(restricts,
448 if (restricts == NULL) {
449 elog(ERROR,
"Out of memory");
450 return finish(SPIcode, ret);
454 SPITupleTable *tuptable = SPI_tuptable;
455 TupleDesc tupdesc = SPI_tuptable->tupdesc;
457 for (t = 0; t < ntuples; t++) {
458 HeapTuple tuple = tuptable->vals[t];
460 &restricts[total_restrict_tuples - ntuples + t]);
462 SPI_freetuptable(tuptable);
467 SPI_cursor_close(SPIportal);
470 PGR_DBG(
"Total %i restriction tuples", total_restrict_tuples);
472 PGR_DBG(
"Calling trsp_edge_wrapper\n");
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);
479 PGR_DBG(
"Message received from inside:");
486 for (z = 0; z < *path_count; z++) {
487 if (z || (*path)[z].vertex_id != -1)
488 (*path)[z].vertex_id += v_min_id;
493 PGR_DBG(
"*path_count = %ld\n", *path_count);
496 ereport(ERROR, (errcode(ERRCODE_E_R_E_CONTAINING_SQL_NOT_PERMITTED),
497 errmsg(
"Error computing path: %s", err_msg)));
500 return finish(SPIcode, ret);
507 FuncCallContext *funcctx;
508 TupleDesc tuple_desc;
512 if (SRF_IS_FIRSTCALL()) {
513 MemoryContext oldcontext;
514 size_t path_count = 0;
521 funcctx = SRF_FIRSTCALL_INIT();
524 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
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);
535 if (PG_ARGISNULL(2)) {
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;
543 if (PG_ARGISNULL(4)) {
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;
551 if (PG_ARGISNULL(7)) {
554 sql = text_to_cstring(PG_GETARG_TEXT_P(7));
555 if (strlen(sql) == 0)
559 PGR_DBG(
"Calling compute_trsp");
573 #if PGSQL_VERSION > 95
574 funcctx->max_calls = path_count;
576 funcctx->max_calls = (uint32_t)path_count;
579 funcctx->user_fctx = path;
580 if (get_call_result_type(fcinfo, NULL, &tuple_desc)
581 != TYPEFUNC_COMPOSITE) {
583 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
584 errmsg(
"function returning record called in context "
585 "that cannot accept type record")));
588 funcctx->tuple_desc = tuple_desc;
590 MemoryContextSwitchTo(oldcontext);
594 funcctx = SRF_PERCALL_SETUP();
596 tuple_desc = funcctx->tuple_desc;
599 if (funcctx->call_cntr < funcctx->max_calls) {
606 values = palloc(4 *
sizeof(Datum));
607 nulls = palloc(4 *
sizeof(
char));
609 values[0] = Int32GetDatum(funcctx->call_cntr);
611 values[1] = Int32GetDatum(path[funcctx->call_cntr].
vertex_id);
613 values[2] = Int32GetDatum(path[funcctx->call_cntr].
edge_id);
615 values[3] = Float8GetDatum(path[funcctx->call_cntr].
cost);
618 tuple = heap_form_tuple(tuple_desc, values, nulls);
621 result = HeapTupleGetDatum(tuple);
627 SRF_RETURN_NEXT(funcctx, result);
630 if (path) free(path);
631 SRF_RETURN_DONE(funcctx);