1 /*
2  * Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA  02110-1301, USA.
18  */
19 /**
20  * SECTION: tracker-sparql-cursor
21  * @short_description: Iteration of the query results
22  * @title: TrackerSparqlCursor
23  * @stability: Stable
24  * @include: tracker-sparql.h
25  *
26  * <para>
27  * #TrackerSparqlCursor is an object which provides methods to iterate the
28  * results of a query to the Tracker Store.
29  * </para>
30  */
31 #include "config.h"
32 
33 #include "tracker-cursor.h"
34 #include "tracker-private.h"
35 
36 enum {
37 	PROP_0,
38 	PROP_CONNECTION,
39 	PROP_N_COLUMNS,
40 	N_PROPS
41 };
42 
43 static GParamSpec *props[N_PROPS];
44 
45 typedef struct {
46 	TrackerSparqlConnection *connection;
47 	gint n_columns;
48 } TrackerSparqlCursorPrivate;
49 
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(TrackerSparqlCursor,tracker_sparql_cursor,G_TYPE_OBJECT)50 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (TrackerSparqlCursor, tracker_sparql_cursor,
51                                      G_TYPE_OBJECT)
52 
53 static void
54 tracker_sparql_cursor_init (TrackerSparqlCursor *cursor)
55 {
56 }
57 
58 static gboolean
tracker_sparql_cursor_real_is_bound(TrackerSparqlCursor * cursor,gint column)59 tracker_sparql_cursor_real_is_bound (TrackerSparqlCursor *cursor,
60                                      gint                 column)
61 {
62 	return tracker_sparql_cursor_get_value_type (cursor, column) != TRACKER_SPARQL_VALUE_TYPE_UNBOUND;
63 }
64 
65 static gint64
tracker_sparql_cursor_real_get_integer(TrackerSparqlCursor * cursor,gint column)66 tracker_sparql_cursor_real_get_integer (TrackerSparqlCursor *cursor,
67                                         gint                 column)
68 {
69 	const gchar *text;
70 
71 	g_return_val_if_fail (tracker_sparql_cursor_real_is_bound (cursor, column), 0);
72 
73 	text = tracker_sparql_cursor_get_string (cursor, column, NULL);
74 	return g_ascii_strtoll (text, NULL, 10);
75 }
76 
77 static gdouble
tracker_sparql_cursor_real_get_double(TrackerSparqlCursor * cursor,gint column)78 tracker_sparql_cursor_real_get_double (TrackerSparqlCursor *cursor,
79                                        gint                 column)
80 {
81 	const gchar *text;
82 
83 	g_return_val_if_fail (tracker_sparql_cursor_real_is_bound (cursor, column), 0);
84 
85 	text = tracker_sparql_cursor_get_string (cursor, column, NULL);
86 
87 	return g_ascii_strtod (text, NULL);
88 }
89 
90 static gboolean
tracker_sparql_cursor_real_get_boolean(TrackerSparqlCursor * cursor,gint column)91 tracker_sparql_cursor_real_get_boolean (TrackerSparqlCursor *cursor,
92                                         gint                 column)
93 {
94 	const gchar *text;
95 
96 	g_return_val_if_fail (tracker_sparql_cursor_real_is_bound (cursor, column), FALSE);
97 
98 	text = tracker_sparql_cursor_get_string (cursor, column, NULL);
99 
100 	return g_ascii_strcasecmp (text, "true") == 0;
101 }
102 
103 static void
tracker_sparql_cursor_finalize(GObject * object)104 tracker_sparql_cursor_finalize (GObject *object)
105 {
106 	TrackerSparqlCursor *cursor = TRACKER_SPARQL_CURSOR (object);
107 	TrackerSparqlCursorPrivate *priv = tracker_sparql_cursor_get_instance_private (cursor);
108 
109 	g_clear_object (&priv->connection);
110 	G_OBJECT_CLASS (tracker_sparql_cursor_parent_class)->finalize (object);
111 }
112 
113 static void
tracker_sparql_cursor_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)114 tracker_sparql_cursor_set_property (GObject      *object,
115                                     guint         prop_id,
116                                     const GValue *value,
117                                     GParamSpec   *pspec)
118 {
119 	TrackerSparqlCursor *cursor = TRACKER_SPARQL_CURSOR (object);
120 	TrackerSparqlCursorPrivate *priv = tracker_sparql_cursor_get_instance_private (cursor);
121 
122 	switch (prop_id) {
123 	case PROP_CONNECTION:
124 		priv->connection = g_value_dup_object (value);
125 		break;
126 	case PROP_N_COLUMNS:
127 		priv->n_columns = g_value_get_int (value);
128 		break;
129 	default:
130 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
131 	}
132 }
133 
134 static void
tracker_sparql_cursor_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)135 tracker_sparql_cursor_get_property (GObject    *object,
136                                     guint       prop_id,
137                                     GValue     *value,
138                                     GParamSpec *pspec)
139 {
140 	TrackerSparqlCursor *cursor = TRACKER_SPARQL_CURSOR (object);
141 	TrackerSparqlCursorPrivate *priv = tracker_sparql_cursor_get_instance_private (cursor);
142 
143 	switch (prop_id) {
144 	case PROP_CONNECTION:
145 		g_value_set_object (value, priv->connection);
146 		break;
147 	case PROP_N_COLUMNS:
148 		g_value_set_int (value, priv->n_columns);
149 		break;
150 	default:
151 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
152 	}
153 }
154 
155 static void
tracker_sparql_cursor_class_init(TrackerSparqlCursorClass * klass)156 tracker_sparql_cursor_class_init (TrackerSparqlCursorClass *klass)
157 {
158 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
159 
160 	object_class->finalize = tracker_sparql_cursor_finalize;
161 	object_class->set_property = tracker_sparql_cursor_set_property;
162 	object_class->get_property = tracker_sparql_cursor_get_property;
163 
164 	klass->get_integer = tracker_sparql_cursor_real_get_integer;
165 	klass->get_double = tracker_sparql_cursor_real_get_double;
166 	klass->get_boolean = tracker_sparql_cursor_real_get_boolean;
167 	klass->is_bound = tracker_sparql_cursor_real_is_bound;
168 
169 	/**
170 	 * TrackerSparqlCursor:connection:
171 	 *
172 	 * The #TrackerSparqlConnection used to retrieve the results.
173 	 */
174 	props[PROP_CONNECTION] =
175 		g_param_spec_object ("connection",
176 		                     "connection",
177 		                     "connection",
178 		                     TRACKER_SPARQL_TYPE_CONNECTION,
179 		                     G_PARAM_CONSTRUCT_ONLY |
180 		                     G_PARAM_STATIC_STRINGS |
181 		                     G_PARAM_READABLE |
182 		                     G_PARAM_WRITABLE);
183 	/**
184 	 * TrackerSparqlCursor:n_columns:
185 	 *
186 	 * Number of columns available in the results to iterate.
187 	 */
188 	props[PROP_N_COLUMNS] =
189 		g_param_spec_int ("n-columns",
190 		                  "n-columns",
191 		                  "n-columns",
192 		                  G_MININT, G_MAXINT, 0,
193 		                  G_PARAM_STATIC_STRINGS |
194 		                  G_PARAM_READABLE);
195 
196 	g_object_class_install_properties (object_class, N_PROPS, props);
197 }
198 
199 /**
200  * tracker_sparql_cursor_get_connection:
201  * @cursor: a #TrackerSparqlCursor
202  *
203  * Returns the #TrackerSparqlConnection associated with this
204  * #TrackerSparqlCursor.
205  *
206  * Returns: (transfer none): the cursor #TrackerSparqlConnection. The
207  * returned object must not be unreferenced by the caller.
208  */
209 TrackerSparqlConnection *
tracker_sparql_cursor_get_connection(TrackerSparqlCursor * cursor)210 tracker_sparql_cursor_get_connection (TrackerSparqlCursor *cursor)
211 {
212 	TrackerSparqlCursorPrivate *priv = tracker_sparql_cursor_get_instance_private (cursor);
213 
214 	g_return_val_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor), NULL);
215 
216 	return priv->connection;
217 }
218 
219 void
tracker_sparql_cursor_set_connection(TrackerSparqlCursor * cursor,TrackerSparqlConnection * connection)220 tracker_sparql_cursor_set_connection (TrackerSparqlCursor     *cursor,
221                                       TrackerSparqlConnection *connection)
222 {
223 	TrackerSparqlCursorPrivate *priv = tracker_sparql_cursor_get_instance_private (cursor);
224 
225 	g_return_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor));
226 	g_return_if_fail (TRACKER_IS_SPARQL_CONNECTION (connection));
227 
228 	g_set_object (&priv->connection, connection);
229 }
230 
231 /**
232  * tracker_sparql_cursor_get_n_columns:
233  * @cursor: a #TrackerSparqlCursor
234  *
235  * This method should only be called after a successful
236  * tracker_sparql_cursor_next(); otherwise its return value
237  * will be undefined.
238  *
239  * Returns: a #gint representing the number of columns available in the
240  * results to iterate.
241  */
242 gint
tracker_sparql_cursor_get_n_columns(TrackerSparqlCursor * cursor)243 tracker_sparql_cursor_get_n_columns (TrackerSparqlCursor *cursor)
244 {
245 	g_return_val_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor), 0);
246 
247 	return TRACKER_SPARQL_CURSOR_GET_CLASS (cursor)->get_n_columns (cursor);
248 }
249 
250 /**
251  * tracker_sparql_cursor_get_string:
252  * @cursor: a #TrackerSparqlCursor
253  * @column: column number to retrieve (first one is 0)
254  * @length: (out) (nullable): length of the returned string, or %NULL
255  *
256  * Retrieves a string representation of the data in the current
257  * row in @column.
258  *
259  * Returns: (nullable): a string which must not be freed. %NULL is returned if
260  * the column is not in the [0,#n_columns] range.
261  */
262 const gchar *
tracker_sparql_cursor_get_string(TrackerSparqlCursor * cursor,gint column,glong * length)263 tracker_sparql_cursor_get_string (TrackerSparqlCursor *cursor,
264                                   gint                 column,
265                                   glong               *length)
266 {
267 	g_return_val_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor), NULL);
268 
269 	return TRACKER_SPARQL_CURSOR_GET_CLASS (cursor)->get_string (cursor,
270 	                                                             column,
271 	                                                             length);
272 }
273 
274 /**
275  * tracker_sparql_cursor_get_boolean:
276  * @cursor: a #TrackerSparqlCursor
277  * @column: column number to retrieve (first one is 0)
278  *
279  * Retrieve a boolean for the current row in @column.
280  *
281  * Returns: a #gboolean.
282  */
283 gboolean
tracker_sparql_cursor_get_boolean(TrackerSparqlCursor * cursor,gint column)284 tracker_sparql_cursor_get_boolean (TrackerSparqlCursor *cursor,
285                                    gint                 column)
286 {
287 	g_return_val_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor), FALSE);
288 
289 	return TRACKER_SPARQL_CURSOR_GET_CLASS (cursor)->get_boolean (cursor,
290 	                                                              column);
291 }
292 
293 /**
294  * tracker_sparql_cursor_get_double:
295  * @cursor: a #TrackerSparqlCursor
296  * @column: column number to retrieve (first one is 0)
297  *
298  * Retrieve a double for the current row in @column.
299  *
300  * Returns: a double.
301  */
302 gdouble
tracker_sparql_cursor_get_double(TrackerSparqlCursor * cursor,gint column)303 tracker_sparql_cursor_get_double (TrackerSparqlCursor *cursor,
304                                   gint                 column)
305 {
306 	g_return_val_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor), -1);
307 
308 	return TRACKER_SPARQL_CURSOR_GET_CLASS (cursor)->get_double (cursor,
309 	                                                             column);
310 }
311 
312 /**
313  * tracker_sparql_cursor_get_integer:
314  * @cursor: a #TrackerSparqlCursor
315  * @column: column number to retrieve (first one is 0)
316  *
317  * Retrieve an integer for the current row in @column.
318  *
319  * Returns: a #gint64.
320  */
321 gint64
tracker_sparql_cursor_get_integer(TrackerSparqlCursor * cursor,gint column)322 tracker_sparql_cursor_get_integer (TrackerSparqlCursor *cursor,
323                                    gint                 column)
324 {
325 	g_return_val_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor), -1);
326 
327 	return TRACKER_SPARQL_CURSOR_GET_CLASS (cursor)->get_integer (cursor,
328 	                                                              column);
329 }
330 
331 /**
332  * tracker_sparql_cursor_get_value_type:
333  * @cursor: a #TrackerSparqlCursor
334  * @column: column number to retrieve (first one is 0)
335  *
336  * The data type bound to the current row in @column is returned.
337  *
338  * Returns: a #TrackerSparqlValueType.
339  */
340 TrackerSparqlValueType
tracker_sparql_cursor_get_value_type(TrackerSparqlCursor * cursor,gint column)341 tracker_sparql_cursor_get_value_type (TrackerSparqlCursor *cursor,
342                                       gint                 column)
343 {
344 	g_return_val_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor),
345 	                      TRACKER_SPARQL_VALUE_TYPE_UNBOUND);
346 
347 	return TRACKER_SPARQL_CURSOR_GET_CLASS (cursor)->get_value_type (cursor,
348 	                                                                 column);
349 }
350 
351 /**
352  * tracker_sparql_cursor_get_variable_name:
353  * @cursor: a #TrackerSparqlCursor
354  * @column: column number to retrieve (first one is 0)
355  *
356  * Retrieves the variable name for the current row in @column.
357  *
358  * Returns: a string which must not be freed.
359  */
360 const gchar *
tracker_sparql_cursor_get_variable_name(TrackerSparqlCursor * cursor,gint column)361 tracker_sparql_cursor_get_variable_name (TrackerSparqlCursor *cursor,
362                                          gint                 column)
363 {
364 	g_return_val_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor), NULL);
365 
366 	return TRACKER_SPARQL_CURSOR_GET_CLASS (cursor)->get_variable_name (cursor,
367 	                                                                    column);
368 }
369 
370 /**
371  * tracker_sparql_cursor_close:
372  * @cursor: a #TrackerSparqlCursor
373  *
374  * Closes the iterator, making it invalid.
375  */
376 void
tracker_sparql_cursor_close(TrackerSparqlCursor * cursor)377 tracker_sparql_cursor_close (TrackerSparqlCursor *cursor)
378 {
379 	g_return_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor));
380 
381 	return TRACKER_SPARQL_CURSOR_GET_CLASS (cursor)->close (cursor);
382 }
383 
384 /**
385  * tracker_sparql_cursor_is_bound:
386  * @cursor: a #TrackerSparqlCursor
387  * @column: column number to retrieve (first one is 0)
388  *
389  * If the current row and @column are bound to a value, %TRUE is returned.
390  *
391  * Returns: a %TRUE or %FALSE.
392  */
393 gboolean
tracker_sparql_cursor_is_bound(TrackerSparqlCursor * cursor,gint column)394 tracker_sparql_cursor_is_bound (TrackerSparqlCursor *cursor,
395                                 gint                 column)
396 {
397 	g_return_val_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor), FALSE);
398 
399 	return TRACKER_SPARQL_CURSOR_GET_CLASS (cursor)->is_bound (cursor, column);
400 }
401 
402 /**
403  * tracker_sparql_cursor_next:
404  * @cursor: a #TrackerSparqlCursor
405  * @cancellable: a #GCancellable used to cancel the operation
406  * @error: #GError for error reporting.
407  *
408  * Iterates to the next result. This is completely synchronous and
409  * it may block.
410  *
411  * Returns: %FALSE if no more results found, otherwise %TRUE.
412  */
413 gboolean
tracker_sparql_cursor_next(TrackerSparqlCursor * cursor,GCancellable * cancellable,GError ** error)414 tracker_sparql_cursor_next (TrackerSparqlCursor  *cursor,
415                             GCancellable         *cancellable,
416                             GError              **error)
417 {
418 	GError *inner_error = NULL;
419 	gboolean success;
420 
421 	g_return_val_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor), FALSE);
422 	g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
423 	g_return_val_if_fail (!error || !*error, FALSE);
424 
425 	success = TRACKER_SPARQL_CURSOR_GET_CLASS (cursor)->next (cursor,
426 	                                                          cancellable,
427 	                                                          &inner_error);
428 
429 	if (inner_error)
430 		g_propagate_error (error, _translate_internal_error (inner_error));
431 
432 	return success;
433 }
434 
435 /**
436  * tracker_sparql_cursor_next_async:
437  * @cursor: a #TrackerSparqlCursor
438  * @cancellable: a #GCancellable used to cancel the operation
439  * @callback: user-defined #GAsyncReadyCallback to be called when
440  *            asynchronous operation is finished.
441  * @user_data: user-defined data to be passed to @callback
442  *
443  * Iterates, asynchronously, to the next result.
444  */
445 void
tracker_sparql_cursor_next_async(TrackerSparqlCursor * cursor,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)446 tracker_sparql_cursor_next_async (TrackerSparqlCursor  *cursor,
447                                   GCancellable         *cancellable,
448                                   GAsyncReadyCallback   callback,
449                                   gpointer              user_data)
450 {
451 	g_return_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor));
452 	g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
453 
454 	return TRACKER_SPARQL_CURSOR_GET_CLASS (cursor)->next_async (cursor,
455 	                                                             cancellable,
456 	                                                             callback,
457 	                                                             user_data);
458 }
459 
460 /**
461  * tracker_sparql_cursor_next_finish:
462  * @cursor: a #TrackerSparqlCursor
463  * @res: a #GAsyncResult with the result of the operation
464  * @error: #GError for error reporting.
465  *
466  * Finishes the asynchronous iteration to the next result.
467  *
468  * Returns: %FALSE if no more results found, otherwise %TRUE.
469  */
470 gboolean
tracker_sparql_cursor_next_finish(TrackerSparqlCursor * cursor,GAsyncResult * res,GError ** error)471 tracker_sparql_cursor_next_finish (TrackerSparqlCursor  *cursor,
472                                    GAsyncResult         *res,
473                                    GError              **error)
474 {
475 	GError *inner_error = NULL;
476 	gboolean success;
477 
478 	g_return_val_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor), FALSE);
479 	g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
480 	g_return_val_if_fail (!error || !*error, FALSE);
481 
482 	success = TRACKER_SPARQL_CURSOR_GET_CLASS (cursor)->next_finish (cursor,
483 	                                                                 res,
484 	                                                                 &inner_error);
485 
486 	if (inner_error)
487 		g_propagate_error (error, _translate_internal_error (inner_error));
488 
489 	return success;
490 }
491 
492 /**
493  * tracker_sparql_cursor_rewind:
494  * @cursor: a #TrackerSparqlCursor
495  *
496  * Resets the iterator to point back to the first result.
497  */
498 void
tracker_sparql_cursor_rewind(TrackerSparqlCursor * cursor)499 tracker_sparql_cursor_rewind (TrackerSparqlCursor *cursor)
500 {
501 	g_return_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor));
502 
503 	return TRACKER_SPARQL_CURSOR_GET_CLASS (cursor)->rewind (cursor);
504 }
505