1 /*PGR-GNU*****************************************************************
2 File: transitiveClosure.c
3
4 Generated with Template by:
5 Copyright (c) 2015 pgRouting developers
6 Mail: project@pgrouting.org
7
8 Function's developer:
9 Copyright (c) 2019 Hang Wu
10 mail: nike0good@gmail.com
11
12 ------
13
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation; either version 2 of the License, or
17 (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27
28 ********************************************************************PGR-GNU*/
29
30 #include <stdbool.h>
31
32 #include "c_common/postgres_connection.h"
33 #include "utils/array.h"
34 #include "catalog/pg_type.h"
35 #include "utils/lsyscache.h"
36
37 #ifndef INT8ARRAYOID
38 #define INT8ARRAYOID 1016
39 #endif
40
41 #include "c_common/debug_macro.h"
42 #include "c_common/e_report.h"
43 #include "c_common/time_msg.h"
44 #include "c_types/transitiveClosure_rt.h"
45 #include "c_common/edges_input.h"
46 #include "c_common/arrays_input.h"
47 #include "drivers/transitiveClosure/transitiveClosure_driver.h"
48
49 PGDLLEXPORT Datum _pgr_transitiveclosure(PG_FUNCTION_ARGS);
50 PG_FUNCTION_INFO_V1(_pgr_transitiveclosure);
51
52
53 static
54 void
process(char * edges_sql,transitiveClosure_rt ** result_tuples,size_t * result_count)55 process(char* edges_sql,
56
57 transitiveClosure_rt **result_tuples,
58 size_t *result_count) {
59 pgr_SPI_connect();
60
61 size_t total_edges = 0;
62 pgr_edge_t* edges = NULL;
63 pgr_get_edges(edges_sql, &edges, &total_edges);
64 if (total_edges == 0) {
65 pgr_SPI_finish();
66 return;
67 }
68
69 PGR_DBG("Starting timer");
70 clock_t start_t = clock();
71 char* log_msg = NULL;
72 char* notice_msg = NULL;
73 char* err_msg = NULL;
74 do_pgr_transitiveClosure(
75 edges, total_edges,
76
77 result_tuples, result_count,
78 &log_msg,
79 ¬ice_msg,
80 &err_msg);
81
82 time_msg("processing pgr_transitiveClosure()", start_t, clock());
83
84
85 if (err_msg && (*result_tuples)) {
86 pfree(*result_tuples);
87 (*result_tuples) = NULL;
88 (*result_count) = 0;
89 }
90
91 pgr_global_report(log_msg, notice_msg, err_msg);
92
93 if (log_msg) pfree(log_msg);
94 if (notice_msg) pfree(notice_msg);
95 if (err_msg) pfree(err_msg);
96 if (edges) pfree(edges);
97 pgr_SPI_finish();
98 }
99
100 PGDLLEXPORT Datum
_pgr_transitiveclosure(PG_FUNCTION_ARGS)101 _pgr_transitiveclosure(PG_FUNCTION_ARGS) {
102 FuncCallContext *funcctx;
103 TupleDesc tuple_desc;
104
105 /**********************************************************************/
106 transitiveClosure_rt *result_tuples = NULL;
107 size_t result_count = 0;
108 /**********************************************************************/
109
110 if (SRF_IS_FIRSTCALL()) {
111 MemoryContext oldcontext;
112 funcctx = SRF_FIRSTCALL_INIT();
113 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
114 /**********************************************************************/
115 /*
116 edges_sql TEXT,
117
118 **********************************************************************/
119
120 process(
121 text_to_cstring(PG_GETARG_TEXT_P(0)),
122 &result_tuples,
123 &result_count);
124
125
126 /**********************************************************************/
127 #if PGSQL_VERSION > 95
128 funcctx->max_calls = result_count;
129 #else
130 funcctx->max_calls = (uint32_t)result_count;
131 #endif
132 funcctx->user_fctx = result_tuples;
133 if (get_call_result_type(fcinfo, NULL, &tuple_desc)
134 != TYPEFUNC_COMPOSITE)
135 ereport(ERROR,
136 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
137 errmsg("function returning record called in context "
138 "that cannot accept type record")));
139 funcctx->tuple_desc = tuple_desc;
140 MemoryContextSwitchTo(oldcontext);
141 }
142
143 funcctx = SRF_PERCALL_SETUP();
144 tuple_desc = funcctx->tuple_desc;
145 result_tuples = (transitiveClosure_rt*) funcctx->user_fctx;
146
147 if (funcctx->call_cntr < funcctx->max_calls) {
148 HeapTuple tuple;
149 Datum result;
150 Datum *values;
151 bool *nulls;
152 int16 typlen;
153 size_t call_cntr = funcctx->call_cntr;
154
155 /**********************************************************************/
156 size_t numb = 3;
157 values =(Datum *)palloc(numb * sizeof(Datum));
158 nulls = palloc(numb * sizeof(bool));
159 size_t i;
160 for (i = 0; i < numb; ++i) {
161 nulls[i] = false;
162 }
163
164 size_t target_array_size =
165 (size_t)result_tuples[call_cntr].target_array_size;
166
167 Datum* target_array_array;
168 target_array_array = (Datum*) palloc(sizeof(Datum) *
169 (size_t)target_array_size);
170
171 for (i = 0; i < target_array_size; ++i) {
172 PGR_DBG("Storing target_array vertex %ld",
173 result_tuples[call_cntr].target_array[i]);
174 target_array_array[i] =
175 Int64GetDatum(result_tuples[call_cntr].target_array[i]);
176 }
177
178 bool typbyval;
179 char typalign;
180 get_typlenbyvalalign(INT8OID, &typlen, &typbyval, &typalign);
181 ArrayType* arrayType;
182 /*
183 * https://doxygen.postgresql.org/arrayfuncs_8c.html
184
185 ArrayType* construct_array(
186 Datum* elems,
187 int nelems,
188 Oid elmtype, int elmlen, bool elmbyval, char elmalign
189 )
190 */
191 arrayType = construct_array(
192 target_array_array,
193 (int)target_array_size,
194 INT8OID, typlen, typbyval, typalign);
195 /*
196 void TupleDescInitEntry(
197 TupleDesc desc,
198 AttrNumber attributeNumber,
199 const char * attributeName,
200 Oid oidtypeid,
201 int32 typmod,
202 int attdim
203 )
204 */
205 TupleDescInitEntry(tuple_desc, (AttrNumber) 3, "target_array",
206 INT8ARRAYOID, -1, 0);
207
208 values[0] = Int32GetDatum(call_cntr + 1);
209 values[1] = Int64GetDatum(result_tuples[call_cntr].vid);
210 values[2] = PointerGetDatum(arrayType);
211
212 /*********************************************************************/
213 tuple = heap_form_tuple(tuple_desc, values, nulls);
214 result = HeapTupleGetDatum(tuple);
215
216 /*
217 * cleaning up the target_array array
218 */
219 if (result_tuples[funcctx->call_cntr].target_array) {
220 pfree(result_tuples[funcctx->call_cntr].target_array);
221 }
222 SRF_RETURN_NEXT(funcctx, result);
223 } else {
224 SRF_RETURN_DONE(funcctx);
225 }
226 }
227