/src/libxmlb/src/xb-query.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2018 Richard Hughes <richard@hughsie.com> |
3 | | * |
4 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | */ |
6 | | |
7 | 0 | #define G_LOG_DOMAIN "XbSilo" |
8 | | |
9 | | #include "config.h" |
10 | | |
11 | | #include <gio/gio.h> |
12 | | |
13 | | #include "xb-machine.h" |
14 | | #include "xb-opcode-private.h" |
15 | | #include "xb-query-private.h" |
16 | | #include "xb-silo-private.h" |
17 | | #include "xb-stack-private.h" |
18 | | |
19 | | typedef struct { |
20 | | GPtrArray *sections; /* of XbQuerySection */ |
21 | | XbQueryFlags flags; |
22 | | gchar *xpath; |
23 | | guint limit; |
24 | | } XbQueryPrivate; |
25 | | |
26 | | G_DEFINE_TYPE_WITH_PRIVATE(XbQuery, xb_query, G_TYPE_OBJECT) |
27 | 0 | #define GET_PRIVATE(o) (xb_query_get_instance_private(o)) |
28 | | |
29 | | typedef struct { |
30 | | XbSilo *silo; |
31 | | } XbQueryParseContext; |
32 | | |
33 | | /** |
34 | | * xb_query_get_sections: |
35 | | * @self: a #XbQuery |
36 | | * |
37 | | * Gets the sections that make up the query. |
38 | | * |
39 | | * Returns: (transfer none) (element-type XbQuerySection): sections |
40 | | * |
41 | | * Since: 0.1.4 |
42 | | **/ |
43 | | GPtrArray * |
44 | | xb_query_get_sections(XbQuery *self) |
45 | 0 | { |
46 | 0 | XbQueryPrivate *priv = GET_PRIVATE(self); |
47 | 0 | g_return_val_if_fail(XB_IS_QUERY(self), NULL); |
48 | 0 | return priv->sections; |
49 | 0 | } |
50 | | |
51 | | /** |
52 | | * xb_query_get_xpath: |
53 | | * @self: a #XbQuery |
54 | | * |
55 | | * Gets the XPath string that created the query. |
56 | | * |
57 | | * Returns: string |
58 | | * |
59 | | * Since: 0.1.4 |
60 | | **/ |
61 | | const gchar * |
62 | | xb_query_get_xpath(XbQuery *self) |
63 | 0 | { |
64 | 0 | XbQueryPrivate *priv = GET_PRIVATE(self); |
65 | 0 | g_return_val_if_fail(XB_IS_QUERY(self), NULL); |
66 | 0 | return priv->xpath; |
67 | 0 | } |
68 | | |
69 | | static gchar * |
70 | | xb_query_section_to_string(XbQuerySection *sect) |
71 | 0 | { |
72 | 0 | GString *str = g_string_new(NULL); |
73 | 0 | if (sect->kind == XB_SILO_QUERY_KIND_PARENT) |
74 | 0 | g_string_append(str, ".."); |
75 | 0 | else if (sect->kind == XB_SILO_QUERY_KIND_WILDCARD) |
76 | 0 | g_string_append(str, "*"); |
77 | 0 | else |
78 | 0 | g_string_append(str, sect->element); |
79 | 0 | if (sect->predicates != NULL && sect->predicates->len > 0) { |
80 | 0 | g_string_append(str, "["); |
81 | 0 | for (guint j = 0; j < sect->predicates->len; j++) { |
82 | 0 | XbStack *stack = g_ptr_array_index(sect->predicates, j); |
83 | 0 | g_autofree gchar *tmp = xb_stack_to_string(stack); |
84 | 0 | g_string_append(str, tmp); |
85 | 0 | } |
86 | 0 | g_string_append(str, "]"); |
87 | 0 | } |
88 | 0 | return g_string_free(str, FALSE); |
89 | 0 | } |
90 | | |
91 | | /** |
92 | | * xb_query_to_string: |
93 | | * @self: a #XbQuery |
94 | | * |
95 | | * Gets the XPath that was used for the query. |
96 | | * |
97 | | * Returns: string |
98 | | * |
99 | | * Since: 0.1.13 |
100 | | **/ |
101 | | gchar * |
102 | | xb_query_to_string(XbQuery *self) |
103 | 0 | { |
104 | 0 | XbQueryPrivate *priv = GET_PRIVATE(self); |
105 | 0 | GString *str = g_string_new(NULL); |
106 | 0 | for (guint i = 0; i < priv->sections->len; i++) { |
107 | 0 | XbQuerySection *sect = g_ptr_array_index(priv->sections, i); |
108 | 0 | g_autofree gchar *tmp = xb_query_section_to_string(sect); |
109 | 0 | g_string_append(str, tmp); |
110 | 0 | if (i != priv->sections->len - 1) |
111 | 0 | g_string_append(str, "/"); |
112 | 0 | } |
113 | 0 | return g_string_free(str, FALSE); |
114 | 0 | } |
115 | | |
116 | | /** |
117 | | * xb_query_get_limit: |
118 | | * @self: a #XbQuery |
119 | | * |
120 | | * Gets the results limit on this query, where 0 is 'all'. |
121 | | * |
122 | | * Returns: integer, default 0 |
123 | | * |
124 | | * Deprecated: 0.3.0: This is not thread-safe. Use xb_query_context_get_limit() |
125 | | * instead. |
126 | | * Since: 0.1.4 |
127 | | **/ |
128 | | guint |
129 | | xb_query_get_limit(XbQuery *self) |
130 | 0 | { |
131 | 0 | XbQueryPrivate *priv = GET_PRIVATE(self); |
132 | 0 | g_return_val_if_fail(XB_IS_QUERY(self), 0); |
133 | 0 | return priv->limit; |
134 | 0 | } |
135 | | |
136 | | /** |
137 | | * xb_query_set_limit: |
138 | | * @self: a #XbQuery |
139 | | * @limit: integer |
140 | | * |
141 | | * Sets the results limit on this query, where 0 is 'all'. |
142 | | * |
143 | | * Deprecated: 0.3.0: This is not thread-safe. Use xb_query_context_set_limit() |
144 | | * instead. |
145 | | * Since: 0.1.4 |
146 | | **/ |
147 | | void |
148 | | xb_query_set_limit(XbQuery *self, guint limit) |
149 | 0 | { |
150 | 0 | XbQueryPrivate *priv = GET_PRIVATE(self); |
151 | 0 | g_return_if_fail(XB_IS_QUERY(self)); |
152 | 0 | priv->limit = limit; |
153 | 0 | } |
154 | | |
155 | | /** |
156 | | * xb_query_get_flags: |
157 | | * @self: a #XbQuery |
158 | | * |
159 | | * Gets the flags used for this query. |
160 | | * |
161 | | * Returns: #XbQueryFlags, default %XB_QUERY_FLAG_NONE |
162 | | * |
163 | | * Deprecated: 0.3.0: This is not thread-safe. Use xb_query_context_get_flags() |
164 | | * instead. |
165 | | * Since: 0.1.15 |
166 | | **/ |
167 | | XbQueryFlags |
168 | | xb_query_get_flags(XbQuery *self) |
169 | 0 | { |
170 | 0 | XbQueryPrivate *priv = GET_PRIVATE(self); |
171 | 0 | g_return_val_if_fail(XB_IS_QUERY(self), 0); |
172 | 0 | return priv->flags; |
173 | 0 | } |
174 | | |
175 | | /** |
176 | | * xb_query_set_flags: |
177 | | * @self: a #XbQuery |
178 | | * @flags: a #XbQueryFlags, e.g. %XB_QUERY_FLAG_USE_INDEXES |
179 | | * |
180 | | * Sets the flags to use for this query. |
181 | | * |
182 | | * Deprecated: 0.3.0: This is not thread-safe. Use xb_query_context_set_flags() |
183 | | * instead. |
184 | | * Since: 0.1.15 |
185 | | **/ |
186 | | void |
187 | | xb_query_set_flags(XbQuery *self, XbQueryFlags flags) |
188 | 0 | { |
189 | 0 | XbQueryPrivate *priv = GET_PRIVATE(self); |
190 | 0 | g_return_if_fail(XB_IS_QUERY(self)); |
191 | 0 | priv->flags = flags; |
192 | 0 | } |
193 | | |
194 | | static XbOpcode * |
195 | | xb_query_get_bound_opcode(XbQuery *self, guint idx) |
196 | 0 | { |
197 | 0 | XbQueryPrivate *priv = GET_PRIVATE(self); |
198 | 0 | guint idx_cnt = 0; |
199 | |
|
200 | 0 | for (guint i = 0; i < priv->sections->len; i++) { |
201 | 0 | XbQuerySection *section = g_ptr_array_index(priv->sections, i); |
202 | 0 | if (section->predicates == NULL) |
203 | 0 | continue; |
204 | 0 | for (guint j = 0; j < section->predicates->len; j++) { |
205 | 0 | XbStack *stack = g_ptr_array_index(section->predicates, j); |
206 | 0 | for (guint k = 0; k < xb_stack_get_size(stack); k++) { |
207 | 0 | XbOpcode *op = xb_stack_peek(stack, k); |
208 | 0 | if (xb_opcode_is_binding(op)) { |
209 | 0 | if (idx == idx_cnt++) |
210 | 0 | return op; |
211 | 0 | } |
212 | 0 | } |
213 | 0 | } |
214 | 0 | } |
215 | 0 | return NULL; |
216 | 0 | } |
217 | | |
218 | | /** |
219 | | * xb_query_bind_str: |
220 | | * @self: a #XbQuery |
221 | | * @idx: an integer index |
222 | | * @str: string to assign to the bound variable |
223 | | * @error: a #GError, or %NULL |
224 | | * |
225 | | * Assigns a string to a bound value specified using `?`. |
226 | | * |
227 | | * Returns: %TRUE if the @idx existed |
228 | | * |
229 | | * Since: 0.1.4 |
230 | | * Deprecated: 0.3.0: Use #XbValueBindings and xb_value_bindings_bind_str() |
231 | | * instead. That keeps the value bindings separate from the #XbQuery, |
232 | | * allowing queries to be re-used over time and between threads. |
233 | | **/ |
234 | | gboolean |
235 | | xb_query_bind_str(XbQuery *self, guint idx, const gchar *str, GError **error) |
236 | 0 | { |
237 | 0 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
238 | 0 | XbOpcode *op; |
239 | |
|
240 | 0 | g_return_val_if_fail(XB_IS_QUERY(self), FALSE); |
241 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
242 | | |
243 | | /* get the correct opcode */ |
244 | 0 | op = xb_query_get_bound_opcode(self, idx); |
245 | 0 | if (op == NULL) { |
246 | 0 | g_set_error(error, |
247 | 0 | G_IO_ERROR, |
248 | 0 | G_IO_ERROR_INVALID_ARGUMENT, |
249 | 0 | "no bound opcode with index %u", |
250 | 0 | idx); |
251 | 0 | return FALSE; |
252 | 0 | } |
253 | 0 | xb_opcode_bind_str(op, g_strdup(str), g_free); |
254 | 0 | return TRUE; |
255 | 0 | G_GNUC_END_IGNORE_DEPRECATIONS |
256 | 0 | } |
257 | | |
258 | | /** |
259 | | * xb_query_bind_val: |
260 | | * @self: a #XbQuery |
261 | | * @idx: an integer index |
262 | | * @val: value to assign to the bound variable |
263 | | * @error: a #GError, or %NULL |
264 | | * |
265 | | * Assigns a string to a bound value specified using `?`. |
266 | | * |
267 | | * Returns: %TRUE if the @idx existed |
268 | | * |
269 | | * Since: 0.1.4 |
270 | | * Deprecated: 0.3.0: Use #XbValueBindings and xb_value_bindings_bind_val() |
271 | | * instead. That keeps the value bindings separate from the #XbQuery, |
272 | | * allowing queries to be re-used over time and between threads. |
273 | | **/ |
274 | | gboolean |
275 | | xb_query_bind_val(XbQuery *self, guint idx, guint32 val, GError **error) |
276 | 0 | { |
277 | 0 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
278 | 0 | XbOpcode *op; |
279 | |
|
280 | 0 | g_return_val_if_fail(XB_IS_QUERY(self), FALSE); |
281 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
282 | | |
283 | | /* get the correct opcode */ |
284 | 0 | op = xb_query_get_bound_opcode(self, idx); |
285 | 0 | if (op == NULL) { |
286 | 0 | g_set_error(error, |
287 | 0 | G_IO_ERROR, |
288 | 0 | G_IO_ERROR_INVALID_ARGUMENT, |
289 | 0 | "no bound opcode with index %u", |
290 | 0 | idx); |
291 | 0 | return FALSE; |
292 | 0 | } |
293 | 0 | xb_opcode_bind_val(op, val); |
294 | 0 | return TRUE; |
295 | 0 | G_GNUC_END_IGNORE_DEPRECATIONS |
296 | 0 | } |
297 | | |
298 | | static void |
299 | | xb_query_section_free(XbQuerySection *section) |
300 | 0 | { |
301 | 0 | if (section->predicates != NULL) |
302 | 0 | g_ptr_array_unref(section->predicates); |
303 | 0 | g_free(section->element); |
304 | 0 | g_slice_free(XbQuerySection, section); |
305 | 0 | } |
306 | | |
307 | | G_DEFINE_AUTOPTR_CLEANUP_FUNC(XbQuerySection, xb_query_section_free) |
308 | | |
309 | | static gboolean |
310 | | xb_query_repair_opcode_texi(XbQuery *self, |
311 | | XbQueryParseContext *context, |
312 | | XbOpcode *op, |
313 | | GError **error) |
314 | 0 | { |
315 | 0 | if (xb_opcode_get_val(op) == XB_SILO_UNSET) { |
316 | 0 | const gchar *tmp = xb_opcode_get_str(op); |
317 | 0 | guint32 val = xb_silo_strtab_index_lookup(context->silo, tmp); |
318 | 0 | if (val == XB_SILO_UNSET) { |
319 | 0 | g_set_error(error, |
320 | 0 | G_IO_ERROR, |
321 | 0 | G_IO_ERROR_INVALID_ARGUMENT, |
322 | 0 | "indexed string '%s' was unfound", |
323 | 0 | tmp); |
324 | 0 | return FALSE; |
325 | 0 | } |
326 | 0 | xb_opcode_set_val(op, val); |
327 | 0 | } |
328 | 0 | return TRUE; |
329 | 0 | } |
330 | | |
331 | | /* Returns an error if the XPath is invalid. */ |
332 | | static gboolean |
333 | | xb_query_parse_predicate(XbQuery *self, |
334 | | XbQueryParseContext *context, |
335 | | XbQuerySection *section, |
336 | | const gchar *text, |
337 | | gssize text_len, |
338 | | GError **error) |
339 | 0 | { |
340 | 0 | XbQueryPrivate *priv = GET_PRIVATE(self); |
341 | 0 | XbMachineParseFlags machine_flags = XB_MACHINE_PARSE_FLAG_NONE; |
342 | 0 | g_autoptr(XbStack) opcodes = NULL; |
343 | | |
344 | | /* set flags */ |
345 | 0 | if (priv->flags & XB_QUERY_FLAG_OPTIMIZE) |
346 | 0 | machine_flags |= XB_MACHINE_PARSE_FLAG_OPTIMIZE; |
347 | | |
348 | | /* parse */ |
349 | 0 | opcodes = xb_machine_parse_full(xb_silo_get_machine(context->silo), |
350 | 0 | text, |
351 | 0 | text_len, |
352 | 0 | machine_flags, |
353 | 0 | error); |
354 | 0 | if (opcodes == NULL) |
355 | 0 | return FALSE; |
356 | | |
357 | | /* repair or convert the indexed strings */ |
358 | 0 | if (priv->flags & XB_QUERY_FLAG_USE_INDEXES) { |
359 | 0 | for (guint i = 0; i < xb_stack_get_size(opcodes); i++) { |
360 | 0 | XbOpcode *op = xb_stack_peek(opcodes, i); |
361 | 0 | if (xb_opcode_get_kind(op) != XB_OPCODE_KIND_INDEXED_TEXT) |
362 | 0 | continue; |
363 | 0 | if (!xb_query_repair_opcode_texi(self, context, op, error)) |
364 | 0 | return FALSE; |
365 | 0 | } |
366 | 0 | } else { |
367 | 0 | for (guint i = 0; i < xb_stack_get_size(opcodes); i++) { |
368 | 0 | XbOpcode *op = xb_stack_peek(opcodes, i); |
369 | 0 | if (xb_opcode_get_kind(op) == XB_OPCODE_KIND_INDEXED_TEXT) |
370 | 0 | xb_opcode_set_kind(op, XB_OPCODE_KIND_TEXT); |
371 | 0 | } |
372 | 0 | } |
373 | | |
374 | | /* create array if it does not exist */ |
375 | 0 | if (section->predicates == NULL) |
376 | 0 | section->predicates = |
377 | 0 | g_ptr_array_new_with_free_func((GDestroyNotify)xb_stack_unref); |
378 | 0 | g_ptr_array_add(section->predicates, g_steal_pointer(&opcodes)); |
379 | 0 | return TRUE; |
380 | 0 | } |
381 | | |
382 | | /* Returns an error if the XPath is invalid. */ |
383 | | static XbQuerySection * |
384 | | xb_query_parse_section(XbQuery *self, |
385 | | XbQueryParseContext *context, |
386 | | const gchar *xpath, |
387 | | GError **error) |
388 | 0 | { |
389 | 0 | g_autoptr(XbQuerySection) section = g_slice_new0(XbQuerySection); |
390 | 0 | guint start = 0; |
391 | | |
392 | | /* common XPath sections */ |
393 | 0 | if (g_strcmp0(xpath, "parent::*") == 0 || g_strcmp0(xpath, "..") == 0) { |
394 | 0 | section->kind = XB_SILO_QUERY_KIND_PARENT; |
395 | 0 | return g_steal_pointer(§ion); |
396 | 0 | } |
397 | | |
398 | | /* parse element and predicate */ |
399 | 0 | for (guint i = 0; xpath[i] != '\0'; i++) { |
400 | 0 | if (start == 0 && xpath[i] == '[') { |
401 | 0 | if (section->element == NULL) |
402 | 0 | section->element = g_strndup(xpath, i); |
403 | 0 | start = i; |
404 | 0 | continue; |
405 | 0 | } |
406 | 0 | if (start > 0 && xpath[i] == ']') { |
407 | 0 | if (!xb_query_parse_predicate(self, |
408 | 0 | context, |
409 | 0 | section, |
410 | 0 | xpath + start + 1, |
411 | 0 | i - start - 1, |
412 | 0 | error)) { |
413 | 0 | return NULL; |
414 | 0 | } |
415 | 0 | start = 0; |
416 | 0 | continue; |
417 | 0 | } |
418 | 0 | } |
419 | | |
420 | | /* incomplete predicate */ |
421 | 0 | if (start != 0) { |
422 | 0 | g_set_error(error, |
423 | 0 | G_IO_ERROR, |
424 | 0 | G_IO_ERROR_INVALID_ARGUMENT, |
425 | 0 | "predicate %s was unfinished, missing ']'", |
426 | 0 | xpath + start); |
427 | 0 | return NULL; |
428 | 0 | } |
429 | | |
430 | 0 | if (section->element == NULL) |
431 | 0 | section->element = g_strdup(xpath); |
432 | 0 | if (g_strcmp0(section->element, "child::*") == 0 || g_strcmp0(section->element, "*") == 0) { |
433 | 0 | section->kind = XB_SILO_QUERY_KIND_WILDCARD; |
434 | 0 | return g_steal_pointer(§ion); |
435 | 0 | } |
436 | | |
437 | | /* This may result in @element_idx being set to %XB_SILO_UNSET if the |
438 | | * given element (`section->element`) is not in the silo at all. Ignore |
439 | | * that for now, and return no matches when the query is actually run. */ |
440 | 0 | section->element_idx = xb_silo_get_strtab_idx(context->silo, section->element); |
441 | |
|
442 | 0 | return g_steal_pointer(§ion); |
443 | 0 | } |
444 | | |
445 | | /* Returns an error if the XPath is invalid. */ |
446 | | static gboolean |
447 | | xb_query_parse(XbQuery *self, XbQueryParseContext *context, const gchar *xpath, GError **error) |
448 | 0 | { |
449 | 0 | XbQueryPrivate *priv = GET_PRIVATE(self); |
450 | 0 | XbQuerySection *section; |
451 | 0 | g_autoptr(GString) acc = g_string_new(NULL); |
452 | | |
453 | | // g_debug ("parsing XPath %s", xpath); |
454 | 0 | for (gsize i = 0; xpath[i] != '\0'; i++) { |
455 | | /* escaped chars */ |
456 | 0 | if (xpath[i] == '\\') { |
457 | 0 | if (xpath[i + 1] == '/' || xpath[i + 1] == 't' || xpath[i + 1] == 'n') { |
458 | 0 | g_string_append_c(acc, xpath[i + 1]); |
459 | 0 | i += 1; |
460 | 0 | continue; |
461 | 0 | } |
462 | 0 | } |
463 | | |
464 | | /* split */ |
465 | 0 | if (xpath[i] == '/') { |
466 | 0 | if (acc->len == 0) { |
467 | 0 | g_set_error_literal(error, |
468 | 0 | G_IO_ERROR, |
469 | 0 | G_IO_ERROR_NOT_FOUND, |
470 | 0 | "xpath section empty"); |
471 | 0 | return FALSE; |
472 | 0 | } |
473 | 0 | section = xb_query_parse_section(self, context, acc->str, error); |
474 | 0 | if (section == NULL) |
475 | 0 | return FALSE; |
476 | 0 | g_ptr_array_add(priv->sections, section); |
477 | 0 | g_string_truncate(acc, 0); |
478 | 0 | continue; |
479 | 0 | } |
480 | 0 | g_string_append_c(acc, xpath[i]); |
481 | 0 | } |
482 | | |
483 | | /* add any remaining section */ |
484 | 0 | section = xb_query_parse_section(self, context, acc->str, error); |
485 | 0 | if (section == NULL) |
486 | 0 | return FALSE; |
487 | 0 | g_ptr_array_add(priv->sections, section); |
488 | 0 | return TRUE; |
489 | 0 | } |
490 | | |
491 | | /** |
492 | | * xb_query_new_full: |
493 | | * @silo: a #XbSilo |
494 | | * @xpath: The XPath query |
495 | | * @flags: some #XbQueryFlags, e.g. #XB_QUERY_FLAG_USE_INDEXES |
496 | | * @error: the #GError, or %NULL |
497 | | * |
498 | | * Creates a query to be used by @silo. It may be quicker to create a query |
499 | | * manually and re-use it multiple times. |
500 | | * |
501 | | * The query will point to strings inside @silo, so the lifetime of @silo must |
502 | | * exceed the lifetime of the returned query. |
503 | | * |
504 | | * Returns: (transfer full): a #XbQuery |
505 | | * |
506 | | * Since: 0.1.6 |
507 | | **/ |
508 | | XbQuery * |
509 | | xb_query_new_full(XbSilo *silo, const gchar *xpath, XbQueryFlags flags, GError **error) |
510 | 0 | { |
511 | 0 | g_autoptr(XbQuery) self = g_object_new(XB_TYPE_QUERY, NULL); |
512 | 0 | XbQueryPrivate *priv = GET_PRIVATE(self); |
513 | 0 | XbQueryParseContext parse_context = { |
514 | 0 | .silo = silo, |
515 | 0 | }; |
516 | |
|
517 | 0 | g_return_val_if_fail(XB_IS_SILO(silo), NULL); |
518 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, NULL); |
519 | | |
520 | | /* create; don’t take a reference on @silo otherwise we get refcount |
521 | | * loops with cached queries from xb_silo_lookup_query() */ |
522 | 0 | priv->xpath = g_strdup(xpath); |
523 | 0 | priv->flags = flags; |
524 | 0 | priv->sections = g_ptr_array_new_with_free_func((GDestroyNotify)xb_query_section_free); |
525 | | |
526 | | /* add each section */ |
527 | 0 | if (!xb_query_parse(self, &parse_context, xpath, error)) |
528 | 0 | return NULL; |
529 | | |
530 | | /* nothing here! */ |
531 | 0 | if (priv->sections->len == 0) { |
532 | 0 | g_set_error(error, |
533 | 0 | G_IO_ERROR, |
534 | 0 | G_IO_ERROR_NOT_SUPPORTED, |
535 | 0 | "No query sections for '%s'", |
536 | 0 | xpath); |
537 | 0 | return NULL; |
538 | 0 | } |
539 | | |
540 | | /* success */ |
541 | 0 | return g_steal_pointer(&self); |
542 | 0 | } |
543 | | |
544 | | /** |
545 | | * xb_query_new: |
546 | | * @silo: a #XbSilo |
547 | | * @xpath: The XPath query |
548 | | * @error: the #GError, or %NULL |
549 | | * |
550 | | * Creates a query to be used by @silo. It may be quicker to create a query |
551 | | * manually and re-use it multiple times. |
552 | | * |
553 | | * Returns: (transfer full): a #XbQuery |
554 | | * |
555 | | * Since: 0.1.4 |
556 | | **/ |
557 | | XbQuery * |
558 | | xb_query_new(XbSilo *silo, const gchar *xpath, GError **error) |
559 | 0 | { |
560 | 0 | return xb_query_new_full(silo, |
561 | 0 | xpath, |
562 | 0 | XB_QUERY_FLAG_OPTIMIZE | XB_QUERY_FLAG_USE_INDEXES, |
563 | 0 | error); |
564 | 0 | } |
565 | | |
566 | | static void |
567 | | xb_query_init(XbQuery *self) |
568 | 0 | { |
569 | 0 | } |
570 | | |
571 | | static void |
572 | | xb_query_finalize(GObject *obj) |
573 | 0 | { |
574 | 0 | XbQuery *self = XB_QUERY(obj); |
575 | 0 | XbQueryPrivate *priv = GET_PRIVATE(self); |
576 | 0 | g_ptr_array_unref(priv->sections); |
577 | 0 | g_free(priv->xpath); |
578 | 0 | G_OBJECT_CLASS(xb_query_parent_class)->finalize(obj); |
579 | 0 | } |
580 | | |
581 | | static void |
582 | | xb_query_class_init(XbQueryClass *klass) |
583 | 0 | { |
584 | 0 | GObjectClass *object_class = G_OBJECT_CLASS(klass); |
585 | 0 | object_class->finalize = xb_query_finalize; |
586 | 0 | } |