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