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