1 /* -*- Mode: c; c-basic-offset: 2 -*-
2 *
3 * rasqal_rowsource_graph.c - Rasqal graph rowsource class
4 *
5 * Copyright (C) 2008-2009, David Beckett http://www.dajobe.org/
6 *
7 * This package is Free Software and part of Redland http://librdf.org/
8 *
9 * It is licensed under the following three licenses as alternatives:
10 * 1. GNU Lesser General Public License (LGPL) V2.1 or any newer version
11 * 2. GNU General Public License (GPL) V2 or any newer version
12 * 3. Apache License, V2.0 or any newer version
13 *
14 * You may not use this file except in compliance with at least one of
15 * the above three licenses.
16 *
17 * See LICENSE.html or LICENSE.txt at the top of this package for the
18 * complete terms and further detail along with the license texts for
19 * the licenses in COPYING.LIB, COPYING and LICENSE-2.0.txt respectively.
20 *
21 */
22
23
24 #ifdef HAVE_CONFIG_H
25 #include <rasqal_config.h>
26 #endif
27
28 #ifdef WIN32
29 #include <win32_rasqal_config.h>
30 #endif
31
32 #include <stdio.h>
33 #include <string.h>
34 #ifdef HAVE_STDLIB_H
35 #include <stdlib.h>
36 #endif
37
38 #include <raptor.h>
39
40 #include "rasqal.h"
41 #include "rasqal_internal.h"
42
43
44 #define DEBUG_FH stderr
45
46
47 /*
48
49 This rowsource evaluates part #3 where a variable is given.
50
51 rasqal_algebra_graph_algebra_node_to_rowsource() implements #1 and #2
52
53
54 http://www.w3.org/TR/2008/REC-rdf-sparql-query-20080115/#sparqlAlgebraEval
55
56 SPARQL Query Language for RDF - Evaluation of a Graph Pattern
57
58 #1 if IRI is a graph name in D
59 eval(D(G), Graph(IRI,P)) = eval(D(D[IRI]), P)
60
61 #2 if IRI is not a graph name in D
62 eval(D(G), Graph(IRI,P)) = the empty multiset
63
64 #3 eval(D(G), Graph(var,P)) =
65 Let R be the empty multiset
66 foreach IRI i in D
67 R := Union(R, Join( eval(D(D[i]), P) , Ω(?var->i) )
68 the result is R
69 */
70
71
72
73 typedef struct
74 {
75 /* inner rowsource */
76 rasqal_rowsource *rowsource;
77
78 /* GRAPH literal constant URI or variable */
79 rasqal_variable* var;
80
81 /* dataset graph offset */
82 int dg_offset;
83
84 /* number of graphs total */
85 int dg_size;
86
87 /* row offset for read_row() */
88 int offset;
89
90 int finished;
91
92 } rasqal_graph_rowsource_context;
93
94
95 static int
rasqal_graph_next_dg(rasqal_graph_rowsource_context * con)96 rasqal_graph_next_dg(rasqal_graph_rowsource_context *con)
97 {
98 rasqal_query *query = con->rowsource->query;
99 rasqal_data_graph *dg;
100
101 con->finished = 0;
102
103 while(1) {
104 rasqal_literal *o;
105
106 con->dg_offset++;
107 dg = rasqal_query_get_data_graph(query, con->dg_offset);
108 if(!dg) {
109 con->finished = 1;
110 break;
111 }
112
113 if(!dg->name_uri)
114 continue;
115
116 o = rasqal_new_uri_literal(query->world, raptor_uri_copy(dg->name_uri));
117 if(!o) {
118 RASQAL_DEBUG1("Failed to create new URI literal\n");
119 con->finished = 1;
120 break;
121 }
122
123 RASQAL_DEBUG2("Using data graph URI literal <%s>\n",
124 rasqal_literal_as_string(o));
125
126 rasqal_rowsource_set_origin(con->rowsource, o);
127
128 /* this passes ownership of o to con->var */
129 rasqal_variable_set_value(con->var, o);
130
131 break;
132 }
133
134 return con->finished;
135 }
136
137
138 static int
rasqal_graph_rowsource_init(rasqal_rowsource * rowsource,void * user_data)139 rasqal_graph_rowsource_init(rasqal_rowsource* rowsource, void *user_data)
140 {
141 rasqal_graph_rowsource_context *con;
142 raptor_sequence* seq;
143
144 con = (rasqal_graph_rowsource_context*)user_data;
145
146 seq = rasqal_query_get_data_graph_sequence(rowsource->query);
147 if(!seq)
148 return 1;
149
150 con->dg_size = raptor_sequence_size(seq);
151
152 con->finished = 0;
153 con->dg_offset = -1;
154 con->offset = 0;
155
156 /* Do not care if finished at this stage (it is not an
157 * error). rasqal_graph_rowsource_read_row() will deal with
158 * returning NULL for an empty result.
159 */
160 rasqal_graph_next_dg(con);
161
162 return 0;
163 }
164
165
166 static int
rasqal_graph_rowsource_finish(rasqal_rowsource * rowsource,void * user_data)167 rasqal_graph_rowsource_finish(rasqal_rowsource* rowsource, void *user_data)
168 {
169 rasqal_graph_rowsource_context *con;
170 con = (rasqal_graph_rowsource_context*)user_data;
171
172 if(con->rowsource)
173 rasqal_free_rowsource(con->rowsource);
174
175 rasqal_variable_set_value(con->var, NULL);
176
177 RASQAL_FREE(rasqal_graph_rowsource_context, con);
178
179 return 0;
180 }
181
182
183 static int
rasqal_graph_rowsource_ensure_variables(rasqal_rowsource * rowsource,void * user_data)184 rasqal_graph_rowsource_ensure_variables(rasqal_rowsource* rowsource,
185 void *user_data)
186 {
187 rasqal_graph_rowsource_context* con;
188
189 con = (rasqal_graph_rowsource_context*)user_data;
190
191 rasqal_rowsource_ensure_variables(con->rowsource);
192
193 rowsource->size = 0;
194 /* Put GRAPH variable first in result row */
195 rasqal_rowsource_add_variable(rowsource, con->var);
196 rasqal_rowsource_copy_variables(rowsource, con->rowsource);
197
198 return 0;
199 }
200
201
202 static rasqal_row*
rasqal_graph_rowsource_read_row(rasqal_rowsource * rowsource,void * user_data)203 rasqal_graph_rowsource_read_row(rasqal_rowsource* rowsource, void *user_data)
204 {
205 rasqal_graph_rowsource_context *con;
206 rasqal_row* row = NULL;
207
208 con = (rasqal_graph_rowsource_context*)user_data;
209
210 if(con->finished)
211 return NULL;
212
213 while(1) {
214 row = rasqal_rowsource_read_row(con->rowsource);
215 if(row)
216 break;
217
218 if(rasqal_graph_next_dg(con)) {
219 con->finished = 1;
220 break;
221 }
222 if(rasqal_rowsource_reset(con->rowsource)) {
223 con->finished = 1;
224 break;
225 }
226 }
227
228 /* If a row is returned, put the GRAPH variable value as first literal */
229 if(row) {
230 rasqal_row* nrow;
231 int i;
232
233 nrow = rasqal_new_row_for_size(rowsource->world, 1 + row->size);
234 if(!nrow) {
235 rasqal_free_row(row);
236 row = NULL;
237 } else {
238 rasqal_row_set_rowsource(nrow, rowsource);
239 nrow->offset = row->offset;
240
241 /* Put GRAPH variable value (or NULL) first in result row */
242 nrow->values[0] = rasqal_new_literal_from_literal(con->var->value);
243
244 /* Copy (size-1) remaining variables from input row */
245 for(i = 0; i < row->size; i++)
246 nrow->values[i + 1] = rasqal_new_literal_from_literal(row->values[i]);
247 rasqal_free_row(row);
248 row = nrow;
249 }
250 }
251
252 return row;
253 }
254
255
256 static int
rasqal_graph_rowsource_reset(rasqal_rowsource * rowsource,void * user_data)257 rasqal_graph_rowsource_reset(rasqal_rowsource* rowsource, void *user_data)
258 {
259 rasqal_graph_rowsource_context *con;
260 con = (rasqal_graph_rowsource_context*)user_data;
261
262 con->finished = 0;
263 con->dg_offset = -1;
264 con->offset = 0;
265
266 rasqal_graph_next_dg(con);
267
268 return rasqal_rowsource_reset(con->rowsource);
269 }
270
271
272 static rasqal_rowsource*
rasqal_graph_rowsource_get_inner_rowsource(rasqal_rowsource * rowsource,void * user_data,int offset)273 rasqal_graph_rowsource_get_inner_rowsource(rasqal_rowsource* rowsource,
274 void *user_data, int offset)
275 {
276 rasqal_graph_rowsource_context *con;
277 con = (rasqal_graph_rowsource_context*)user_data;
278
279 if(offset == 0)
280 return con->rowsource;
281 return NULL;
282 }
283
284
285 static const rasqal_rowsource_handler rasqal_graph_rowsource_handler = {
286 /* .version = */ 1,
287 "graph",
288 /* .init = */ rasqal_graph_rowsource_init,
289 /* .finish = */ rasqal_graph_rowsource_finish,
290 /* .ensure_variables = */ rasqal_graph_rowsource_ensure_variables,
291 /* .read_row = */ rasqal_graph_rowsource_read_row,
292 /* .read_all_rows = */ NULL,
293 /* .reset = */ rasqal_graph_rowsource_reset,
294 /* .set_requirements = */ NULL,
295 /* .get_inner_rowsource = */ rasqal_graph_rowsource_get_inner_rowsource,
296 /* .set_origin = */ NULL,
297 };
298
299
300 /**
301 * rasqal_new_graph_rowsource:
302 * @world: world object
303 * @query: query object
304 * @rowsource: input rowsource
305 * @var: graph variable
306 *
307 * INTERNAL - create a new GRAPH rowsource that binds a variable
308 *
309 * The @rowsource becomes owned by the new rowsource
310 *
311 * Return value: new rowsource or NULL on failure
312 */
313 rasqal_rowsource*
rasqal_new_graph_rowsource(rasqal_world * world,rasqal_query * query,rasqal_rowsource * rowsource,rasqal_variable * var)314 rasqal_new_graph_rowsource(rasqal_world *world,
315 rasqal_query *query,
316 rasqal_rowsource* rowsource,
317 rasqal_variable *var)
318 {
319 rasqal_graph_rowsource_context *con;
320 int flags = 0;
321
322 if(!world || !query || !rowsource || !var)
323 return NULL;
324
325 con = RASQAL_CALLOC(rasqal_graph_rowsource_context*, 1, sizeof(*con));
326 if(!con)
327 return NULL;
328
329 con->rowsource = rowsource;
330 con->var = var;
331
332 return rasqal_new_rowsource_from_handler(world, query,
333 con,
334 &rasqal_graph_rowsource_handler,
335 query->vars_table,
336 flags);
337 }
338