Coverage Report

Created: 2026-05-30 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tinysparql/src/libtinysparql/tracker-deserializer-xml.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2022, Red Hat Inc.
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
 * Author: Carlos Garnacho <carlosg@gnome.org>
20
 */
21
22
/* Deserialization to cursors for the XML format defined at:
23
 *   https://www.w3.org/TR/2013/REC-rdf-sparql-XMLres-20130321/
24
 */
25
26
#include "config.h"
27
28
#include "tracker-deserializer-xml.h"
29
30
#include <libxml/xmlreader.h>
31
32
typedef struct {
33
  TrackerSparqlValueType type;
34
  xmlChar *str;
35
  xmlChar *langtag;
36
} ColumnData;
37
38
struct _TrackerDeserializerXml {
39
  TrackerDeserializer parent_instance;
40
  xmlTextReaderPtr reader;
41
  GPtrArray *columns;
42
  GPtrArray *column_names;
43
  GError *error;
44
  gboolean started;
45
};
46
47
0
G_DEFINE_TYPE (TrackerDeserializerXml,
48
0
               tracker_deserializer_xml,
49
0
               TRACKER_TYPE_DESERIALIZER)
50
0
51
0
static ColumnData *
52
0
column_new (TrackerSparqlValueType  type,
53
0
            xmlChar                *str,
54
0
            xmlChar                *langtag)
55
0
{
56
0
  ColumnData *col;
57
58
0
  col = g_slice_new0 (ColumnData);
59
0
  col->type = type;
60
0
  col->str = str;
61
0
  col->langtag = langtag;
62
63
0
  return col;
64
0
}
65
66
static void
67
column_free (gpointer data)
68
0
{
69
0
  ColumnData *col = data;
70
71
0
  xmlFree (col->str);
72
0
  xmlFree (col->langtag);
73
0
  g_slice_free (ColumnData, col);
74
0
}
75
76
static void
77
tracker_deserializer_xml_finalize (GObject *object)
78
0
{
79
0
  TrackerDeserializerXml *deserializer =
80
0
    TRACKER_DESERIALIZER_XML (object);
81
82
0
  g_clear_pointer (&deserializer->reader, xmlFreeTextReader);
83
0
  g_ptr_array_unref (deserializer->columns);
84
0
  g_ptr_array_unref (deserializer->column_names);
85
86
0
  G_OBJECT_CLASS (tracker_deserializer_xml_parent_class)->finalize (object);
87
0
}
88
89
static int
90
stream_read (gpointer  context,
91
             gchar    *buf,
92
             int       len)
93
0
{
94
0
  GInputStream *stream = context;
95
96
0
  return g_input_stream_read (stream, buf, len, NULL, NULL);
97
0
}
98
99
static int
100
stream_close (gpointer context)
101
0
{
102
0
  GInputStream *stream = context;
103
104
0
  return g_input_stream_close (stream, NULL, NULL) ? 0 : -1;
105
0
}
106
107
static void
108
error_handler (gpointer                 user_data,
109
               const gchar             *msg,
110
               xmlParserSeverities      severity,
111
               xmlTextReaderLocatorPtr  locator)
112
0
{
113
0
  TrackerDeserializerXml *deserializer = user_data;
114
115
0
  deserializer->error = g_error_new (TRACKER_SPARQL_ERROR,
116
0
                                     TRACKER_SPARQL_ERROR_PARSE,
117
0
                                     "Could not parse XML response: %s",
118
0
                                     msg);
119
0
}
120
121
static gboolean
122
reader_in_element (TrackerDeserializerXml *deserializer,
123
                   const gchar            *name,
124
                   int                     depth)
125
0
{
126
0
  return (xmlTextReaderNodeType (deserializer->reader) == XML_READER_TYPE_ELEMENT &&
127
0
          g_strcmp0 ((gchar *) xmlTextReaderConstName (deserializer->reader), name) == 0 &&
128
0
          xmlTextReaderDepth (deserializer->reader) == depth);
129
0
}
130
131
static gboolean
132
parse_head (TrackerDeserializerXml  *deserializer,
133
            GError                 **error)
134
0
{
135
0
  gboolean seen_link = FALSE;
136
137
0
  if (xmlTextReaderRead(deserializer->reader) <= 0 ||
138
0
      !reader_in_element (deserializer, "head", 1))
139
0
    goto error;
140
141
0
  while (xmlTextReaderRead (deserializer->reader) > 0) {
142
0
    if (xmlTextReaderNodeType (deserializer->reader) == XML_READER_TYPE_END_ELEMENT)
143
0
      break;
144
145
0
    if (reader_in_element (deserializer, "variable", 2)) {
146
0
      xmlChar *name;
147
148
0
      if (seen_link) {
149
0
        g_set_error (error,
150
0
                     TRACKER_SPARQL_ERROR,
151
0
                     TRACKER_SPARQL_ERROR_PARSE,
152
0
                     "Wrong XML format, variable node found after link");
153
0
        break;
154
0
      }
155
156
0
      name = xmlTextReaderGetAttribute (deserializer->reader,
157
0
                                        (xmlChar *) "name");
158
0
      g_ptr_array_add (deserializer->column_names, name);
159
0
    } else if (reader_in_element (deserializer, "link", 2)) {
160
      /* We do nothing about extra links in headers, but still
161
       * mandate that these appear after all variable nodes
162
       * as per spec.
163
       */
164
0
      seen_link = TRUE;
165
0
    } else {
166
0
      goto error;
167
0
    }
168
0
  }
169
170
0
  return TRUE;
171
172
0
 error:
173
0
  g_set_error (error,
174
0
               TRACKER_SPARQL_ERROR,
175
0
               TRACKER_SPARQL_ERROR_PARSE,
176
0
               "Wrong XML format, unexpected node '%s'",
177
0
               xmlTextReaderConstName (deserializer->reader));
178
179
0
  return FALSE;
180
0
}
181
182
static void
183
tracker_deserializer_xml_constructed (GObject *object)
184
0
{
185
0
  TrackerDeserializerXml *deserializer =
186
0
    TRACKER_DESERIALIZER_XML (object);
187
0
  GInputStream *stream;
188
189
0
  G_OBJECT_CLASS (tracker_deserializer_xml_parent_class)->constructed (object);
190
191
0
  stream = tracker_deserializer_get_stream (TRACKER_DESERIALIZER (object));
192
193
0
  deserializer->reader = xmlReaderForIO (stream_read,
194
0
                                         stream_close,
195
0
                                         stream,
196
0
                                         NULL, NULL, 0);
197
0
  if (deserializer->reader) {
198
0
    xmlTextReaderSetErrorHandler (deserializer->reader,
199
0
                                  error_handler, deserializer);
200
0
  }
201
202
0
  if (deserializer->reader &&
203
0
      xmlTextReaderRead(deserializer->reader) > 0 &&
204
0
      reader_in_element (deserializer, "sparql", 0)) {
205
0
    parse_head (deserializer, &deserializer->error);
206
0
  } else {
207
0
    g_set_error (&deserializer->error,
208
0
                 TRACKER_SPARQL_ERROR,
209
0
                 TRACKER_SPARQL_ERROR_PARSE,
210
0
                 "Wrong XML format, variable node found after link");
211
0
  }
212
0
}
213
214
static gint
215
tracker_deserializer_xml_get_n_columns (TrackerSparqlCursor  *cursor)
216
0
{
217
0
  TrackerDeserializerXml *deserializer =
218
0
    TRACKER_DESERIALIZER_XML (cursor);
219
220
0
  return deserializer->column_names->len;
221
0
}
222
223
static TrackerSparqlValueType
224
tracker_deserializer_xml_get_value_type (TrackerSparqlCursor  *cursor,
225
                                         gint                  column)
226
0
{
227
0
  TrackerDeserializerXml *deserializer =
228
0
    TRACKER_DESERIALIZER_XML (cursor);
229
0
  ColumnData *col;
230
231
0
  if (column < 0 || column >= (gint) deserializer->columns->len)
232
0
    return TRACKER_SPARQL_VALUE_TYPE_UNBOUND;
233
234
0
  col = g_ptr_array_index (deserializer->columns, column);
235
236
0
  return col->type;
237
0
}
238
239
static const gchar *
240
tracker_deserializer_xml_get_variable_name (TrackerSparqlCursor  *cursor,
241
                                            gint                  column)
242
0
{
243
0
  TrackerDeserializerXml *deserializer =
244
0
    TRACKER_DESERIALIZER_XML (cursor);
245
246
0
  if (column < 0 || column >= (gint) deserializer->column_names->len)
247
0
    return NULL;
248
249
0
  return g_ptr_array_index (deserializer->column_names, column);
250
0
}
251
252
static const gchar *
253
tracker_deserializer_xml_get_string (TrackerSparqlCursor  *cursor,
254
                                     gint                  column,
255
                                     const gchar         **langtag,
256
                                     glong                *length)
257
0
{
258
0
  TrackerDeserializerXml *deserializer =
259
0
    TRACKER_DESERIALIZER_XML (cursor);
260
0
  ColumnData *col;
261
262
0
  if (length)
263
0
    *length = 0;
264
0
  if (langtag)
265
0
    *langtag = NULL;
266
267
0
  if (column < 0 || column >= (gint) deserializer->columns->len)
268
0
    return NULL;
269
270
0
  col = g_ptr_array_index (deserializer->columns, column);
271
272
0
  if (length)
273
0
    *length = strlen ((const gchar *) col->str);
274
0
  if (langtag)
275
0
    *langtag = (const gchar *) col->langtag;
276
277
0
  return (const gchar *) col->str;
278
0
}
279
280
static gboolean
281
maybe_propagate_error (TrackerDeserializerXml  *deserializer,
282
                       GError                 **error)
283
0
{
284
0
  if (deserializer->error) {
285
0
    g_propagate_error (error, deserializer->error);
286
0
    deserializer->error = NULL;
287
0
    return TRUE;
288
0
  }
289
290
0
  return FALSE;
291
0
}
292
293
static gboolean
294
parse_binding_type (TrackerDeserializerXml  *deserializer,
295
                    TrackerSparqlValueType  *type,
296
                    GError                 **error)
297
0
{
298
0
  if (reader_in_element (deserializer, "uri", 4)) {
299
0
    *type = TRACKER_SPARQL_VALUE_TYPE_URI;
300
0
  } else if (reader_in_element (deserializer, "bnode", 4)) {
301
0
    *type = TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE;
302
0
  } else if (reader_in_element (deserializer, "literal", 4)) {
303
0
    xmlChar *datatype;
304
0
    const gchar *suffix;
305
306
0
    datatype = xmlTextReaderGetAttribute (deserializer->reader,
307
0
                                          (xmlChar *) "datatype");
308
309
0
    if (!datatype ||
310
0
        !g_str_has_prefix ((const gchar *) datatype, TRACKER_PREFIX_XSD)) {
311
0
      g_clear_pointer (&datatype, xmlFree);
312
0
      *type = TRACKER_SPARQL_VALUE_TYPE_STRING;
313
0
      return TRUE;
314
0
    }
315
316
0
    suffix = (const gchar *) &datatype[strlen (TRACKER_PREFIX_XSD)];
317
318
0
    if (g_str_equal (suffix, "byte") ||
319
0
        g_str_equal (suffix, "int") ||
320
0
        g_str_equal (suffix, "integer") ||
321
0
        g_str_equal (suffix, "long"))
322
0
      *type = TRACKER_SPARQL_VALUE_TYPE_INTEGER;
323
0
    else if (g_str_equal (suffix, "decimal") ||
324
0
             g_str_equal (suffix, "double"))
325
0
      *type = TRACKER_SPARQL_VALUE_TYPE_DOUBLE;
326
0
    else if (g_str_equal (suffix, "date") ||
327
0
             g_str_equal (suffix, "dateTime"))
328
0
      *type = TRACKER_SPARQL_VALUE_TYPE_DATETIME;
329
0
    else if (g_str_equal (suffix, "boolean"))
330
0
      *type = TRACKER_SPARQL_VALUE_TYPE_BOOLEAN;
331
0
    else
332
0
      *type = TRACKER_SPARQL_VALUE_TYPE_STRING;
333
334
0
    xmlFree (datatype);
335
0
  } else {
336
0
    g_set_error (error,
337
0
                 TRACKER_SPARQL_ERROR,
338
0
                 TRACKER_SPARQL_ERROR_PARSE,
339
0
                 "Unknown binding type '%s'",
340
0
                 xmlTextReaderConstName (deserializer->reader));
341
0
    return FALSE;
342
0
  }
343
344
0
  return TRUE;
345
0
}
346
347
static gboolean
348
parse_binding (TrackerDeserializerXml  *deserializer,
349
               TrackerSparqlValueType  *type,
350
               xmlChar                **name,
351
               xmlChar                **value,
352
               xmlChar                **langtag,
353
               GError                 **error)
354
0
{
355
0
  xmlChar *binding_name = NULL, *binding_value = NULL, *binding_langtag = NULL;
356
357
0
  if (!reader_in_element (deserializer, "binding", 3))
358
0
    goto error;
359
360
0
  binding_name = xmlTextReaderGetAttribute (deserializer->reader,
361
0
                                            (xmlChar *) "name");
362
363
0
  if (xmlTextReaderRead(deserializer->reader) <= 0)
364
0
    goto error;
365
366
0
  binding_langtag = xmlTextReaderGetAttribute (deserializer->reader,
367
0
                                               (xmlChar *) "xml:lang");
368
369
0
  if (!parse_binding_type (deserializer, type, error))
370
0
    goto error_already_set;
371
372
0
  if (xmlTextReaderRead(deserializer->reader) <= 0)
373
0
    goto error;
374
375
0
  binding_value = xmlTextReaderValue (deserializer->reader);
376
377
  /* End of binding content */
378
0
  if (xmlTextReaderRead(deserializer->reader) <= 0 ||
379
0
      xmlTextReaderNodeType (deserializer->reader) != XML_READER_TYPE_END_ELEMENT)
380
0
    goto error;
381
382
  /* End of binding */
383
0
  if (xmlTextReaderRead(deserializer->reader) <= 0 ||
384
0
      xmlTextReaderNodeType (deserializer->reader) != XML_READER_TYPE_END_ELEMENT)
385
0
    goto error;
386
387
0
  *name = binding_name;
388
0
  *value = binding_value;
389
0
  *langtag = binding_langtag;
390
391
0
  return TRUE;
392
0
 error:
393
0
  g_set_error (error,
394
0
               TRACKER_SPARQL_ERROR,
395
0
               TRACKER_SPARQL_ERROR_PARSE,
396
0
               "Wrong XML format, unexpected node '%s'",
397
0
               xmlTextReaderConstName (deserializer->reader));
398
0
 error_already_set:
399
0
  g_clear_pointer (&binding_name, xmlFree);
400
0
  g_clear_pointer (&binding_value, xmlFree);
401
0
  g_clear_pointer (&binding_langtag, xmlFree);
402
403
0
  return FALSE;
404
0
}
405
406
static gboolean
407
parse_result (TrackerDeserializerXml  *deserializer,
408
              GError                 **error)
409
0
{
410
0
  TrackerSparqlCursor *cursor = TRACKER_SPARQL_CURSOR (deserializer);
411
0
  const gchar *var_name;
412
0
  GHashTable *ht = NULL;
413
0
  gint n_columns, i;
414
415
0
  if (!reader_in_element (deserializer, "result", 2))
416
0
    goto error;
417
418
0
  g_ptr_array_set_size (deserializer->columns, 0);
419
0
  ht = g_hash_table_new_full (g_str_hash, g_str_equal, xmlFree, column_free);
420
421
0
  while (xmlTextReaderRead (deserializer->reader) > 0) {
422
0
    ColumnData *col;
423
0
    xmlChar *name, *value, *langtag;
424
0
    TrackerSparqlValueType type;
425
426
0
    if (xmlTextReaderNodeType (deserializer->reader) == XML_READER_TYPE_END_ELEMENT)
427
0
      break;
428
429
0
    if (!parse_binding (deserializer, &type, &name, &value, &langtag, error))
430
0
      goto error_already_set;
431
432
0
    col = column_new (type, value, langtag);
433
0
    g_hash_table_insert (ht, name, col);
434
0
  }
435
436
0
  if (maybe_propagate_error (deserializer, error))
437
0
    goto error_already_set;
438
439
0
  n_columns = tracker_sparql_cursor_get_n_columns (cursor);
440
441
0
  for (i = 0; i < n_columns; i++) {
442
0
    ColumnData *col;
443
0
    gpointer key, value;
444
445
0
    var_name = tracker_sparql_cursor_get_variable_name (cursor, i);
446
447
0
    if (g_hash_table_lookup_extended (ht, var_name, &key, &value)) {
448
0
      col = value;
449
0
      g_hash_table_steal (ht, var_name);
450
0
      xmlFree (key);
451
0
    } else {
452
0
      col = column_new (TRACKER_SPARQL_VALUE_TYPE_UNBOUND, NULL, NULL);
453
0
    }
454
455
0
    g_ptr_array_add (deserializer->columns, col);
456
0
  }
457
458
  /* There should be no bindings left */
459
0
  if (g_hash_table_size (ht) > 0) {
460
0
    g_set_error (error,
461
0
                 TRACKER_SPARQL_ERROR,
462
0
                 TRACKER_SPARQL_ERROR_PARSE,
463
0
                 "Wrong XML format, unexpected additional bindings");
464
0
    goto error_already_set;
465
0
  }
466
467
0
  g_clear_pointer (&ht, g_hash_table_unref);
468
469
0
  return TRUE;
470
471
0
 error:
472
0
  g_set_error (error,
473
0
               TRACKER_SPARQL_ERROR,
474
0
               TRACKER_SPARQL_ERROR_PARSE,
475
0
               "Wrong XML format, unexpected node '%s'",
476
0
               xmlTextReaderConstName (deserializer->reader));
477
0
 error_already_set:
478
0
  g_clear_pointer (&ht, g_hash_table_unref);
479
0
  return FALSE;
480
0
}
481
482
static gboolean
483
tracker_deserializer_xml_next (TrackerSparqlCursor  *cursor,
484
                               GCancellable         *cancellable,
485
                               GError              **error)
486
0
{
487
0
  TrackerDeserializerXml *deserializer =
488
0
    TRACKER_DESERIALIZER_XML (cursor);
489
490
0
  if (g_cancellable_set_error_if_cancelled (cancellable, error))
491
0
    return FALSE;
492
493
0
  g_ptr_array_set_size (deserializer->columns, 0);
494
495
0
 again:
496
0
  if (xmlTextReaderRead(deserializer->reader) <= 0) {
497
0
    if (!maybe_propagate_error (deserializer, error)) {
498
0
      g_set_error (error,
499
0
                   TRACKER_SPARQL_ERROR,
500
0
                   TRACKER_SPARQL_ERROR_PARSE,
501
0
                   "Unexpected termination of XML document");
502
0
    }
503
0
    return FALSE;
504
0
  }
505
506
0
  if (!deserializer->started) {
507
0
    if (reader_in_element (deserializer, "results", 1)) {
508
0
      deserializer->started = TRUE;
509
      /* We want to read the next element, the first <result> */
510
0
      goto again;
511
0
    } else if (reader_in_element (deserializer, "boolean", 1)) {
512
0
      ColumnData *col;
513
0
      xmlChar *content;
514
515
0
      content = xmlTextReaderValue (deserializer->reader);
516
0
      col = column_new (TRACKER_SPARQL_VALUE_TYPE_BOOLEAN, content, NULL);
517
0
      g_ptr_array_add (deserializer->columns, col);
518
0
    } else {
519
0
      g_set_error (error,
520
0
                   TRACKER_SPARQL_ERROR,
521
0
                   TRACKER_SPARQL_ERROR_PARSE,
522
0
                   "Wrong XML format, unexpected node '%s'",
523
0
                   xmlTextReaderConstName (deserializer->reader));
524
0
      return FALSE;
525
0
    }
526
0
  }
527
528
  /* We've reached the end of results */
529
0
  if (xmlTextReaderNodeType (deserializer->reader) == XML_READER_TYPE_END_ELEMENT)
530
0
    return FALSE;
531
532
0
  return parse_result (deserializer, error);
533
0
}
534
535
static void
536
tracker_deserializer_xml_next_async (TrackerSparqlCursor  *cursor,
537
                                     GCancellable         *cancellable,
538
                                     GAsyncReadyCallback   cb,
539
                                     gpointer              user_data)
540
0
{
541
0
  GError *error = NULL;
542
0
  GTask *task;
543
544
0
  task = g_task_new (cursor, cancellable, cb, user_data);
545
546
0
  if (tracker_sparql_cursor_next (cursor, cancellable, &error))
547
0
    g_task_return_boolean (task, TRUE);
548
0
  else if (!error)
549
0
    g_task_return_boolean (task, FALSE);
550
0
  else
551
0
    g_task_return_error (task, error);
552
553
0
  g_object_unref (task);
554
0
}
555
556
static gboolean
557
tracker_deserializer_xml_next_finish (TrackerSparqlCursor  *cursor,
558
                                      GAsyncResult         *res,
559
                                      GError              **error)
560
0
{
561
0
  return g_task_propagate_boolean (G_TASK (res), error);
562
0
}
563
564
static void
565
tracker_deserializer_xml_close (TrackerSparqlCursor *cursor)
566
0
{
567
0
  TrackerDeserializerXml *deserializer =
568
0
    TRACKER_DESERIALIZER_XML (cursor);
569
570
0
  xmlTextReaderClose (deserializer->reader);
571
572
0
  TRACKER_SPARQL_CURSOR_CLASS (tracker_deserializer_xml_parent_class)->close (cursor);
573
0
}
574
575
gboolean
576
tracker_deserializer_xml_get_parser_location (TrackerDeserializer  *deserializer,
577
                                              const char          **name,
578
                                              goffset              *line_no,
579
                                              goffset              *column_no)
580
0
{
581
0
  TrackerDeserializerXml *deserializer_xml =
582
0
    TRACKER_DESERIALIZER_XML (deserializer);
583
584
0
  if (name)
585
0
    *name = tracker_deserializer_get_name (deserializer);
586
587
0
  *line_no = xmlTextReaderGetParserLineNumber (deserializer_xml->reader);
588
0
  *column_no = xmlTextReaderGetParserColumnNumber (deserializer_xml->reader);
589
590
0
  return TRUE;
591
0
}
592
593
static void
594
tracker_deserializer_xml_class_init (TrackerDeserializerXmlClass *klass)
595
0
{
596
0
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
597
0
  TrackerSparqlCursorClass *cursor_class =
598
0
    TRACKER_SPARQL_CURSOR_CLASS (klass);
599
0
  TrackerDeserializerClass *deserializer_class =
600
0
    TRACKER_DESERIALIZER_CLASS (klass);
601
602
0
  object_class->finalize = tracker_deserializer_xml_finalize;
603
0
  object_class->constructed = tracker_deserializer_xml_constructed;
604
605
0
  cursor_class->get_n_columns = tracker_deserializer_xml_get_n_columns;
606
0
  cursor_class->get_value_type = tracker_deserializer_xml_get_value_type;
607
0
  cursor_class->get_variable_name = tracker_deserializer_xml_get_variable_name;
608
0
  cursor_class->get_string = tracker_deserializer_xml_get_string;
609
0
  cursor_class->next = tracker_deserializer_xml_next;
610
0
  cursor_class->next_async = tracker_deserializer_xml_next_async;
611
0
  cursor_class->next_finish = tracker_deserializer_xml_next_finish;
612
0
  cursor_class->close = tracker_deserializer_xml_close;
613
614
0
  deserializer_class->get_parser_location =
615
0
    tracker_deserializer_xml_get_parser_location;
616
0
}
617
618
static void
619
tracker_deserializer_xml_init (TrackerDeserializerXml *deserializer)
620
0
{
621
0
  deserializer->columns = g_ptr_array_new_with_free_func (column_free);
622
0
  deserializer->column_names = g_ptr_array_new_with_free_func (xmlFree);
623
0
}
624
625
TrackerSparqlCursor *
626
tracker_deserializer_xml_new (GInputStream            *stream,
627
                              TrackerNamespaceManager *namespaces)
628
0
{
629
0
  return g_object_new (TRACKER_TYPE_DESERIALIZER_XML,
630
0
                       "stream", stream,
631
0
                       "namespace-manager", namespaces,
632
                       NULL);
633
0
}