From 986b5fea356dbdbb1a9af399f4ef83874166f619 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Fri, 26 Feb 2021 14:55:29 +0100 Subject: libtracker-data: Check BIND variable in the current context As per https://www.w3.org/TR/sparql11-query/#bind: "The variable introduced by the BIND clause must not have been used in the group graph pattern up to the point of use in BIND." We are currently checking that the variable has bindings, but that's also true if the variable was used in other graph patterns prior to the current one. We must check here only in the current context to match that behavior. This makes queries like: SELECT ?u { { BIND (1 AS ?u) } UNION { BIND (2 AS ?u) } } work as expected. --- src/libtracker-data/tracker-sparql.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/libtracker-data/tracker-sparql.c b/src/libtracker-data/tracker-sparql.c index 8091aea23..6278978ab 100644 --- a/src/libtracker-data/tracker-sparql.c +++ b/src/libtracker-data/tracker-sparql.c @@ -5312,7 +5312,8 @@ translate_Bind (TrackerSparql *sparql, TrackerVariable *variable; TrackerBinding *binding; TrackerPropertyType type; - gboolean is_empty; + gboolean is_empty, already_defined; + gchar *var_name; /* Bind ::= 'BIND' '(' Expression 'AS' Var ')' */ @@ -5337,9 +5338,18 @@ translate_Bind (TrackerSparql *sparql, _expect (sparql, RULE_TYPE_LITERAL, LITERAL_AS); _call_rule (sparql, NAMED_RULE_Var, error); + /* "The variable introduced by the BIND clause must + * not have been used in the group graph pattern up + * to the point of use in BIND." + */ + var_name = _dup_last_string (sparql); + already_defined = tracker_context_lookup_variable_by_name (sparql->current_state->context, + var_name); + g_free (var_name); + variable = _last_node_variable (sparql); - if (tracker_variable_has_bindings (variable)) + if (already_defined) _raise (PARSE, "Expected undefined variable in BIND", variable->name); _append_string_printf (sparql, "AS %s ", -- cgit v1.2.1 From 79cfcfd5b9e51e670cad13197ce5b16ff8d71469 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Fri, 26 Feb 2021 16:09:36 +0100 Subject: tests: Add tests for BIND reusing variable names This is allowed as long as the variable was not previously defined in the same group graph pattern, forbidden otherwise. Test both cases. --- tests/libtracker-data/bind/bind-reused-different-patterns.out | 2 ++ tests/libtracker-data/bind/bind-reused-different-patterns.rq | 3 +++ tests/libtracker-data/error/bind-reused-same-pattern.out | 0 tests/libtracker-data/error/bind-reused-same-pattern.rq | 3 +++ tests/libtracker-data/tracker-sparql-test.c | 3 +++ 5 files changed, 11 insertions(+) create mode 100644 tests/libtracker-data/bind/bind-reused-different-patterns.out create mode 100644 tests/libtracker-data/bind/bind-reused-different-patterns.rq create mode 100644 tests/libtracker-data/error/bind-reused-same-pattern.out create mode 100644 tests/libtracker-data/error/bind-reused-same-pattern.rq diff --git a/tests/libtracker-data/bind/bind-reused-different-patterns.out b/tests/libtracker-data/bind/bind-reused-different-patterns.out new file mode 100644 index 000000000..e1c03aad4 --- /dev/null +++ b/tests/libtracker-data/bind/bind-reused-different-patterns.out @@ -0,0 +1,2 @@ +"1" +"2" diff --git a/tests/libtracker-data/bind/bind-reused-different-patterns.rq b/tests/libtracker-data/bind/bind-reused-different-patterns.rq new file mode 100644 index 000000000..24a86d6b4 --- /dev/null +++ b/tests/libtracker-data/bind/bind-reused-different-patterns.rq @@ -0,0 +1,3 @@ +# Bound variables are allowed to be reused across different +# group graph patterns. +SELECT ?u { { BIND (1 AS ?u) } UNION { BIND (2 AS ?u) } } diff --git a/tests/libtracker-data/error/bind-reused-same-pattern.out b/tests/libtracker-data/error/bind-reused-same-pattern.out new file mode 100644 index 000000000..e69de29bb diff --git a/tests/libtracker-data/error/bind-reused-same-pattern.rq b/tests/libtracker-data/error/bind-reused-same-pattern.rq new file mode 100644 index 000000000..37bf436c7 --- /dev/null +++ b/tests/libtracker-data/error/bind-reused-same-pattern.rq @@ -0,0 +1,3 @@ +# BIND is not allowed to redefine variables already defined +# in the same group graph pattern. +SELECT ?u { BIND (1 AS ?u) . BIND (2 AS ?u) } diff --git a/tests/libtracker-data/tracker-sparql-test.c b/tests/libtracker-data/tracker-sparql-test.c index c2b7d6225..5b61365d5 100644 --- a/tests/libtracker-data/tracker-sparql-test.c +++ b/tests/libtracker-data/tracker-sparql-test.c @@ -229,6 +229,8 @@ const TestInfo tests[] = { /* Unknown property */ { "error/query-error-2", "error/query-error-2", TRUE, FALSE }, { "error/update-error-query-1", "error/update-error-1", FALSE, TRUE }, + /* Remapping variables in BIND */ + { "error/bind-reused-same-pattern", "error/query-error-1", TRUE, FALSE }, { "turtle/turtle-query-001", "turtle/turtle-data-001", FALSE }, { "turtle/turtle-query-002", "turtle/turtle-data-002", FALSE }, @@ -243,6 +245,7 @@ const TestInfo tests[] = { { "bind/bind5", "bind/data", FALSE }, { "bind/bind6", "bind/data", FALSE }, { "bind/bind7", "bind/data", FALSE }, + { "bind/bind-reused-different-patterns", "bind/data", FALSE }, /* Property paths */ { "property-paths/inverse-path-1", "property-paths/data", FALSE }, { "property-paths/inverse-path-2", "property-paths/data", FALSE }, -- cgit v1.2.1 From 86b0b7258e731a2ade2033b2e57400cd1ed8e750 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Fri, 26 Feb 2021 20:29:50 +0100 Subject: libtracker-data: Handle unbound variables in GraphGraphPattern We currently don't handle correctly the situation of GRAPH ?g { ... } clauses in SELECTs that leave ?g unbound. E.g. situations that the contained GroupGraphPattern does not contain anything that actually requires accessing graphs: SELECT ?g { GRAPH ?g { } } SELECT ?g ?a { GRAPH ?g { BIND (1 AS ?a) } } Check for this specific situation, and union the contained query with a query for all available named graphs. This makes these queries behave as expected (e.g. bind ?g to all available graphs, because the empty pattern { } matches everything). --- src/libtracker-data/tracker-sparql.c | 72 ++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/libtracker-data/tracker-sparql.c b/src/libtracker-data/tracker-sparql.c index 6278978ab..a32b4960e 100644 --- a/src/libtracker-data/tracker-sparql.c +++ b/src/libtracker-data/tracker-sparql.c @@ -790,6 +790,49 @@ tracker_sparql_add_union_graph_subquery_for_class (TrackerSparql *sparql, tracker_sparql_swap_builder (sparql, old); } +static void +tracker_sparql_add_union_graph_subquery_for_named_graphs (TrackerSparql *sparql) +{ + TrackerStringBuilder *old; + gpointer graph_id; + GHashTable *graphs; + GHashTableIter iter; + gboolean first = TRUE; + + if (g_hash_table_lookup (sparql->current_state->union_views, "graphs")) + return; + + g_hash_table_add (sparql->current_state->union_views, g_strdup ("graphs")); + old = tracker_sparql_swap_builder (sparql, sparql->current_state->with_clauses); + + if (tracker_string_builder_is_empty (sparql->current_state->with_clauses)) + _append_string (sparql, "WITH "); + else + _append_string (sparql, ", "); + + graphs = tracker_sparql_get_effective_graphs (sparql); + + _append_string (sparql, "\"unionGraph_graphs\"(graph) AS ("); + + g_hash_table_iter_init (&iter, graphs); + while (g_hash_table_iter_next (&iter, NULL, &graph_id)) { + if (first) + _append_string (sparql, "VALUES "); + else + _append_string (sparql, ", "); + + _append_string_printf (sparql, "(%d) ", GPOINTER_TO_INT (graph_id)); + first = FALSE; + } + + if (g_hash_table_size (graphs) == 0) + _append_string (sparql, "SELECT NULL WHERE FALSE"); + + _append_string (sparql, ") "); + + tracker_sparql_swap_builder (sparql, old); +} + static gint tracker_sparql_find_graph (TrackerSparql *sparql, const gchar *name) @@ -5049,7 +5092,9 @@ static gboolean translate_GraphGraphPattern (TrackerSparql *sparql, GError **error) { + TrackerStringBuilder *str, *old; TrackerToken old_graph; + TrackerVariable *graph_var; gboolean do_join; /* GraphGraphPattern ::= 'GRAPH' VarOrIri GroupGraphPattern @@ -5066,10 +5111,37 @@ translate_GraphGraphPattern (TrackerSparql *sparql, _expect (sparql, RULE_TYPE_LITERAL, LITERAL_GRAPH); _call_rule (sparql, NAMED_RULE_VarOrIri, error); + graph_var = _last_node_variable (sparql); _init_token (&sparql->current_state->graph, sparql->current_state->prev_node, sparql); + + str = _append_placeholder (sparql); + _call_rule (sparql, NAMED_RULE_GroupGraphPattern, error); + if (graph_var && !tracker_variable_has_bindings (graph_var)) { + TrackerBinding *binding; + + tracker_sparql_add_union_graph_subquery_for_named_graphs (sparql); + + old = tracker_sparql_swap_builder (sparql, str); + _append_string_printf (sparql, + "SELECT * FROM ( " + "SELECT graph AS %s FROM \"unionGraph_graphs\")," + " (", + tracker_variable_get_sql_expression (graph_var)); + + tracker_sparql_swap_builder (sparql, old); + _append_string (sparql, ") "); + + binding = tracker_variable_binding_new (graph_var, NULL, NULL); + tracker_binding_set_data_type (TRACKER_BINDING (binding), + TRACKER_PROPERTY_TYPE_RESOURCE); + tracker_variable_set_sample_binding (graph_var, + TRACKER_VARIABLE_BINDING (binding)); + g_object_unref (binding); + } + tracker_token_unset (&sparql->current_state->graph); sparql->current_state->graph = old_graph; -- cgit v1.2.1 From 0127a31091602b09ca184e5348bd785524bdb4e7 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Fri, 26 Feb 2021 21:04:23 +0100 Subject: tests: Add tests for unbound GRAPH variables Ensure these work correctly. --- tests/libtracker-data/graph/graph-unbound-1.out | 2 ++ tests/libtracker-data/graph/graph-unbound-1.rq | 1 + tests/libtracker-data/graph/graph-unbound-2.out | 2 ++ tests/libtracker-data/graph/graph-unbound-2.rq | 1 + tests/libtracker-data/graph/graph-unbound-3.out | 6 ++++++ tests/libtracker-data/graph/graph-unbound-3.rq | 1 + tests/libtracker-data/tracker-sparql-test.c | 3 +++ 7 files changed, 16 insertions(+) create mode 100644 tests/libtracker-data/graph/graph-unbound-1.out create mode 100644 tests/libtracker-data/graph/graph-unbound-1.rq create mode 100644 tests/libtracker-data/graph/graph-unbound-2.out create mode 100644 tests/libtracker-data/graph/graph-unbound-2.rq create mode 100644 tests/libtracker-data/graph/graph-unbound-3.out create mode 100644 tests/libtracker-data/graph/graph-unbound-3.rq diff --git a/tests/libtracker-data/graph/graph-unbound-1.out b/tests/libtracker-data/graph/graph-unbound-1.out new file mode 100644 index 000000000..dabff8b9f --- /dev/null +++ b/tests/libtracker-data/graph/graph-unbound-1.out @@ -0,0 +1,2 @@ +"http://example/graphA" +"http://example/graphB" diff --git a/tests/libtracker-data/graph/graph-unbound-1.rq b/tests/libtracker-data/graph/graph-unbound-1.rq new file mode 100644 index 000000000..2ed6176a5 --- /dev/null +++ b/tests/libtracker-data/graph/graph-unbound-1.rq @@ -0,0 +1 @@ +SELECT ?g { GRAPH ?g { } } diff --git a/tests/libtracker-data/graph/graph-unbound-2.out b/tests/libtracker-data/graph/graph-unbound-2.out new file mode 100644 index 000000000..b472b5bf2 --- /dev/null +++ b/tests/libtracker-data/graph/graph-unbound-2.out @@ -0,0 +1,2 @@ +"http://example/graphA" "1" +"http://example/graphB" "1" diff --git a/tests/libtracker-data/graph/graph-unbound-2.rq b/tests/libtracker-data/graph/graph-unbound-2.rq new file mode 100644 index 000000000..34a91b5d7 --- /dev/null +++ b/tests/libtracker-data/graph/graph-unbound-2.rq @@ -0,0 +1 @@ +SELECT ?g ?a { GRAPH ?g { BIND (1 AS ?a) } } ORDER BY ?g ?a diff --git a/tests/libtracker-data/graph/graph-unbound-3.out b/tests/libtracker-data/graph/graph-unbound-3.out new file mode 100644 index 000000000..984ce4453 --- /dev/null +++ b/tests/libtracker-data/graph/graph-unbound-3.out @@ -0,0 +1,6 @@ +"http://example/graphA" "1" +"http://example/graphA" "2" +"http://example/graphA" "3" +"http://example/graphB" "1" +"http://example/graphB" "2" +"http://example/graphB" "3" diff --git a/tests/libtracker-data/graph/graph-unbound-3.rq b/tests/libtracker-data/graph/graph-unbound-3.rq new file mode 100644 index 000000000..a95980fe9 --- /dev/null +++ b/tests/libtracker-data/graph/graph-unbound-3.rq @@ -0,0 +1 @@ +SELECT ?g ?a { GRAPH ?g { VALUES ?a { 1 2 3 } } } ORDER BY ?g ?a diff --git a/tests/libtracker-data/tracker-sparql-test.c b/tests/libtracker-data/tracker-sparql-test.c index 5b61365d5..abff972a5 100644 --- a/tests/libtracker-data/tracker-sparql-test.c +++ b/tests/libtracker-data/tracker-sparql-test.c @@ -160,6 +160,9 @@ const TestInfo tests[] = { { "graph/non-existent-1", "graph/data-1", FALSE }, { "graph/non-existent-2", "graph/data-1", FALSE }, { "graph/non-existent-3", "graph/data-1", FALSE }, + { "graph/graph-unbound-1", "graph/data-1", FALSE }, + { "graph/graph-unbound-2", "graph/data-1", FALSE }, + { "graph/graph-unbound-3", "graph/data-1", FALSE }, { "graph/drop", "graph/data-drop", FALSE }, { "graph/drop-non-existent", "graph/data-drop-non-existent", FALSE, TRUE }, { "graph/drop-default", "graph/data-drop-default", FALSE }, -- cgit v1.2.1 From 4b60f6b8dc0da6df960159f4ad379dd706f6d6d9 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Fri, 26 Feb 2021 23:47:10 +0100 Subject: libtracker-data: Store generation number on TrackerSparql construction We currently happen to be needlessly regenerating the SQL string from the SPARQL query, as the TrackerSparql deems to need one as the generation number is not up-to-date. We can safely assign the generation number on construction, before the first parsing. --- src/libtracker-data/tracker-sparql.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libtracker-data/tracker-sparql.c b/src/libtracker-data/tracker-sparql.c index a32b4960e..e2f1139b0 100644 --- a/src/libtracker-data/tracker-sparql.c +++ b/src/libtracker-data/tracker-sparql.c @@ -9406,6 +9406,8 @@ tracker_sparql_new (TrackerDataManager *manager, sparql = g_object_new (TRACKER_TYPE_SPARQL, NULL); sparql->query_type = TRACKER_SPARQL_QUERY_SELECT; sparql->data_manager = g_object_ref (manager); + sparql->generation = tracker_data_manager_get_generation (sparql->data_manager); + if (strcasestr (query, "\\u")) sparql->sparql = tracker_unescape_unichars (query, -1); else -- cgit v1.2.1 From 8e7a3c9ed7784fd5f2a6826fb0db27a37afd2b19 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 27 Feb 2021 12:11:49 +0100 Subject: libtracker-sparql: Handle correctly OFFSET without LIMIT In Sparql, OFFSET and LIMIT can be set independently (and event flip around!), however SQL mandates that OFFSET also needs LIMIT. Handle correctly the case that only OFFSET is given, by setting an unlimited limit. --- src/libtracker-data/tracker-sparql.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libtracker-data/tracker-sparql.c b/src/libtracker-data/tracker-sparql.c index e2f1139b0..ce33a5ca2 100644 --- a/src/libtracker-data/tracker-sparql.c +++ b/src/libtracker-data/tracker-sparql.c @@ -3708,6 +3708,8 @@ translate_LimitOffsetClauses (TrackerSparql *sparql, TRACKER_LITERAL_BINDING (limit)); _append_literal_sql (sparql, TRACKER_LITERAL_BINDING (limit)); g_object_unref (limit); + } else if (offset) { + _append_string (sparql, "LIMIT -1 "); } if (offset) { -- cgit v1.2.1 From fd38d9a3a6d027ab29e11272e41236ff3475deda Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 27 Feb 2021 12:44:11 +0100 Subject: tests: Add tests for LIMIT/OFFSET modifiers Test all combinations and orders of these 2 modifiers. --- tests/libtracker-data/algebra/modifier-limit-1.out | 1 + tests/libtracker-data/algebra/modifier-limit-1.rq | 1 + tests/libtracker-data/algebra/modifier-limit-offset-1.out | 1 + tests/libtracker-data/algebra/modifier-limit-offset-1.rq | 1 + tests/libtracker-data/algebra/modifier-limit-offset-2.out | 1 + tests/libtracker-data/algebra/modifier-limit-offset-2.rq | 2 ++ tests/libtracker-data/algebra/modifier-offset-1.out | 2 ++ tests/libtracker-data/algebra/modifier-offset-1.rq | 1 + tests/libtracker-data/tracker-sparql-test.c | 4 ++++ 9 files changed, 14 insertions(+) create mode 100644 tests/libtracker-data/algebra/modifier-limit-1.out create mode 100644 tests/libtracker-data/algebra/modifier-limit-1.rq create mode 100644 tests/libtracker-data/algebra/modifier-limit-offset-1.out create mode 100644 tests/libtracker-data/algebra/modifier-limit-offset-1.rq create mode 100644 tests/libtracker-data/algebra/modifier-limit-offset-2.out create mode 100644 tests/libtracker-data/algebra/modifier-limit-offset-2.rq create mode 100644 tests/libtracker-data/algebra/modifier-offset-1.out create mode 100644 tests/libtracker-data/algebra/modifier-offset-1.rq diff --git a/tests/libtracker-data/algebra/modifier-limit-1.out b/tests/libtracker-data/algebra/modifier-limit-1.out new file mode 100644 index 000000000..f27b76c59 --- /dev/null +++ b/tests/libtracker-data/algebra/modifier-limit-1.out @@ -0,0 +1 @@ +"1" diff --git a/tests/libtracker-data/algebra/modifier-limit-1.rq b/tests/libtracker-data/algebra/modifier-limit-1.rq new file mode 100644 index 000000000..2d0e3b986 --- /dev/null +++ b/tests/libtracker-data/algebra/modifier-limit-1.rq @@ -0,0 +1 @@ +SELECT ?a { VALUES ?a { 1 2 3 } } LIMIT 1 diff --git a/tests/libtracker-data/algebra/modifier-limit-offset-1.out b/tests/libtracker-data/algebra/modifier-limit-offset-1.out new file mode 100644 index 000000000..1026c253e --- /dev/null +++ b/tests/libtracker-data/algebra/modifier-limit-offset-1.out @@ -0,0 +1 @@ +"2" diff --git a/tests/libtracker-data/algebra/modifier-limit-offset-1.rq b/tests/libtracker-data/algebra/modifier-limit-offset-1.rq new file mode 100644 index 000000000..5a60710b4 --- /dev/null +++ b/tests/libtracker-data/algebra/modifier-limit-offset-1.rq @@ -0,0 +1 @@ +SELECT ?a { VALUES ?a { 1 2 3 } } LIMIT 1 OFFSET 1 diff --git a/tests/libtracker-data/algebra/modifier-limit-offset-2.out b/tests/libtracker-data/algebra/modifier-limit-offset-2.out new file mode 100644 index 000000000..1026c253e --- /dev/null +++ b/tests/libtracker-data/algebra/modifier-limit-offset-2.out @@ -0,0 +1 @@ +"2" diff --git a/tests/libtracker-data/algebra/modifier-limit-offset-2.rq b/tests/libtracker-data/algebra/modifier-limit-offset-2.rq new file mode 100644 index 000000000..83aefe64e --- /dev/null +++ b/tests/libtracker-data/algebra/modifier-limit-offset-2.rq @@ -0,0 +1,2 @@ +# Try OFFSET/LIMIT ordering +SELECT ?a { VALUES ?a { 1 2 3 } } OFFSET 1 LIMIT 1 diff --git a/tests/libtracker-data/algebra/modifier-offset-1.out b/tests/libtracker-data/algebra/modifier-offset-1.out new file mode 100644 index 000000000..f1861d764 --- /dev/null +++ b/tests/libtracker-data/algebra/modifier-offset-1.out @@ -0,0 +1,2 @@ +"2" +"3" diff --git a/tests/libtracker-data/algebra/modifier-offset-1.rq b/tests/libtracker-data/algebra/modifier-offset-1.rq new file mode 100644 index 000000000..3059b418a --- /dev/null +++ b/tests/libtracker-data/algebra/modifier-offset-1.rq @@ -0,0 +1 @@ +SELECT ?a { VALUES ?a { 1 2 3 } } OFFSET 1 diff --git a/tests/libtracker-data/tracker-sparql-test.c b/tests/libtracker-data/tracker-sparql-test.c index abff972a5..6a92ba744 100644 --- a/tests/libtracker-data/tracker-sparql-test.c +++ b/tests/libtracker-data/tracker-sparql-test.c @@ -62,6 +62,10 @@ const TestInfo tests[] = { { "algebra/filter-in-4", "algebra/data-2", FALSE }, { "algebra/filter-in-5", "algebra/data-2", FALSE }, { "algebra/var-scope-join-1", "algebra/var-scope-join-1", FALSE }, + { "algebra/modifier-limit-offset-1", "algebra/data-1", FALSE }, + { "algebra/modifier-limit-offset-2", "algebra/data-1", FALSE }, + { "algebra/modifier-limit-1", "algebra/data-1", FALSE }, + { "algebra/modifier-offset-1", "algebra/data-1", FALSE }, { "anon/query", "anon/data", FALSE }, { "anon/query-2", "anon/data", FALSE }, { "anon/query-3", "anon/data", FALSE }, -- cgit v1.2.1 From d8035fe97c435ee8d2b642fbaed2a91828f67368 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 27 Feb 2021 12:47:16 +0100 Subject: libtracker-data: Special case empty remote queries on services vtable Make these queries succeed, but come up empty. This is effectively the same than asking the remote service for the empty graph pattern "{ }". --- src/libtracker-data/tracker-vtab-service.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/libtracker-data/tracker-vtab-service.c b/src/libtracker-data/tracker-vtab-service.c index f141bd2a1..469f05f4d 100644 --- a/src/libtracker-data/tracker-vtab-service.c +++ b/src/libtracker-data/tracker-vtab-service.c @@ -296,6 +296,7 @@ service_filter (sqlite3_vtab_cursor *vtab_cursor, TrackerSparqlStatement *statement; GHashTable *names = NULL, *values = NULL; GError *error = NULL; + gboolean empty_query = FALSE; gint i; cursor->finished = FALSE; @@ -354,6 +355,11 @@ service_filter (sqlite3_vtab_cursor *vtab_cursor, TRACKER_SPARQL_ERROR_PARSE, "Query not given to services virtual table"); goto fail; + } else if (*cursor->query == '\0') { + g_clear_pointer (&names, g_hash_table_unref); + g_clear_pointer (&values, g_hash_table_unref); + cursor->finished = TRUE; + return SQLITE_OK; } connection = tracker_data_manager_get_remote_connection (module->data_manager, @@ -389,9 +395,9 @@ fail: g_clear_pointer (&names, g_hash_table_unref); g_clear_pointer (&values, g_hash_table_unref); - if (cursor->silent) { + if (cursor->silent || empty_query) { cursor->finished = TRUE; - g_error_free (error); + g_clear_error (&error); return SQLITE_OK; } else { tracker_service_cursor_set_vtab_error (cursor, error->message); -- cgit v1.2.1 From 9e023e3b052dcc51c974c365c763e19091c0a37b Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 27 Feb 2021 12:48:32 +0100 Subject: libtracker-data: Detect remote queries with no projected parameters Querying a service that does not use any variable in its graph pattern is fairly pointless. There are no variables to project into the rest of the query, thus there's no way to match anything with the service graph pattern. In pure SPARQL, that is basically just: SELECT * { SERVICE <...> { } } But since in Tracker we are more lenient wrt giving each projected variable a name, we also have cases like: SELECT * { SERVICE <...> { SELECT 2 { } } } That are anyways pointless to execute. Detect all these cases and send an empty query string instead, the service vtable will behave as expected. --- src/libtracker-data/tracker-sparql.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/libtracker-data/tracker-sparql.c b/src/libtracker-data/tracker-sparql.c index ce33a5ca2..10e04bfd1 100644 --- a/src/libtracker-data/tracker-sparql.c +++ b/src/libtracker-data/tracker-sparql.c @@ -5196,7 +5196,7 @@ translate_ServiceGraphPattern (TrackerSparql *sparql, GList *variable_rules = NULL, *l; GList *join_vars = NULL; TrackerToken service; - GString *service_sparql; + GString *service_sparql = NULL; gboolean silent = FALSE, do_join; gint i = 0; @@ -5241,7 +5241,6 @@ translate_ServiceGraphPattern (TrackerSparql *sparql, pattern = _skip_rule (sparql, NAMED_RULE_GroupGraphPattern); _append_string (sparql, "SELECT "); - service_sparql = g_string_new ("SELECT "); variable_rules = extract_variables (sparql, pattern); @@ -5269,6 +5268,9 @@ translate_ServiceGraphPattern (TrackerSparql *sparql, if (i > 0) _append_string (sparql, ", "); + if (!service_sparql) + service_sparql = g_string_new ("SELECT "); + /* Variable was used before in the graph pattern, preserve * for later so we join on it properly. */ @@ -5289,6 +5291,9 @@ translate_ServiceGraphPattern (TrackerSparql *sparql, i++; } + if (variable_rules == NULL) + _append_string (sparql, "* "); + if (tracker_token_get_variable (&service)) { if (variable_rules != NULL) _append_string (sparql, ", "); @@ -5298,16 +5303,18 @@ translate_ServiceGraphPattern (TrackerSparql *sparql, join_vars = g_list_prepend (join_vars, tracker_token_get_variable (&service)); } - tracker_parser_node_get_extents (pattern, &pattern_start, &pattern_end); - pattern_str = g_strndup (&sparql->sparql[pattern_start], pattern_end - pattern_start); - escaped_str = _escape_sql_string (pattern_str, '"'); - g_string_append (service_sparql, escaped_str); - g_list_free (variables); - g_free (pattern_str); - g_free (escaped_str); + if (service_sparql) { + tracker_parser_node_get_extents (pattern, &pattern_start, &pattern_end); + pattern_str = g_strndup (&sparql->sparql[pattern_start], pattern_end - pattern_start); + escaped_str = _escape_sql_string (pattern_str, '"'); + g_string_append (service_sparql, escaped_str); + g_list_free (variables); + g_free (pattern_str); + g_free (escaped_str); + } _append_string_printf (sparql, "FROM tracker_service WHERE query=\"%s\" AND silent=%d ", - service_sparql->str, + service_sparql ? service_sparql->str : "", silent); if (!tracker_token_get_variable (&service)) { @@ -5315,6 +5322,9 @@ translate_ServiceGraphPattern (TrackerSparql *sparql, tracker_token_get_idstring (&service)); } + if (service_sparql) + g_string_free (service_sparql, TRUE); + i = 0; /* Proxy parameters to the virtual table */ @@ -5344,7 +5354,6 @@ translate_ServiceGraphPattern (TrackerSparql *sparql, tracker_token_unset (&service); tracker_sparql_pop_context (sparql, TRUE); - g_string_free (service_sparql, TRUE); g_list_free (variable_rules); if (do_join) { -- cgit v1.2.1 From 3f24096396aadca64651f203e6c1f84b99b4916b Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 27 Feb 2021 13:09:09 +0100 Subject: tests: Add tests for SERVICE queries with empty/pointless patterns Test that the service graph pattern comes up empty if there is no pattern/variables to project. --- tests/libtracker-data/service/service-empty-1.out | 0 tests/libtracker-data/service/service-empty-1.rq | 4 ++++ tests/libtracker-data/service/service-empty-2.out | 0 tests/libtracker-data/service/service-empty-2.rq | 5 +++++ tests/libtracker-data/tracker-service-test.c | 2 ++ 5 files changed, 11 insertions(+) create mode 100644 tests/libtracker-data/service/service-empty-1.out create mode 100644 tests/libtracker-data/service/service-empty-1.rq create mode 100644 tests/libtracker-data/service/service-empty-2.out create mode 100644 tests/libtracker-data/service/service-empty-2.rq diff --git a/tests/libtracker-data/service/service-empty-1.out b/tests/libtracker-data/service/service-empty-1.out new file mode 100644 index 000000000..e69de29bb diff --git a/tests/libtracker-data/service/service-empty-1.rq b/tests/libtracker-data/service/service-empty-1.rq new file mode 100644 index 000000000..476290070 --- /dev/null +++ b/tests/libtracker-data/service/service-empty-1.rq @@ -0,0 +1,4 @@ +SELECT ?u { + SERVICE { + } +} diff --git a/tests/libtracker-data/service/service-empty-2.out b/tests/libtracker-data/service/service-empty-2.out new file mode 100644 index 000000000..e69de29bb diff --git a/tests/libtracker-data/service/service-empty-2.rq b/tests/libtracker-data/service/service-empty-2.rq new file mode 100644 index 000000000..688f584b9 --- /dev/null +++ b/tests/libtracker-data/service/service-empty-2.rq @@ -0,0 +1,5 @@ +SELECT ?u { + SERVICE { + SELECT 42 { } + } +} diff --git a/tests/libtracker-data/tracker-service-test.c b/tests/libtracker-data/tracker-service-test.c index f2e67e468..5fd33e3f9 100644 --- a/tests/libtracker-data/tracker-service-test.c +++ b/tests/libtracker-data/tracker-service-test.c @@ -43,6 +43,8 @@ const TestInfo tests[] = { { "service/service-union-with-local-2", FALSE }, { "service/service-var-1", FALSE }, { "service/service-var-2", FALSE }, + { "service/service-empty-1", FALSE }, + { "service/service-empty-2", FALSE }, }; static GDBusConnection *dbus_conn = NULL; -- cgit v1.2.1 From b4cdb29f7a07fa73d03c5fb73319547058667225 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sun, 28 Feb 2021 19:56:42 +0100 Subject: libtracker-data: Handle nesting of multivalued property functions Do not convert results to string except in the topmost property function, so things like nmm:artistName(nmm:performer(?u)) work. This allows arbitrary nesting of single and multivalued properties to work consistently. Fixes: https://gitlab.gnome.org/GNOME/tracker/-/issues/259 --- src/libtracker-data/tracker-sparql.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libtracker-data/tracker-sparql.c b/src/libtracker-data/tracker-sparql.c index 10e04bfd1..c4aaaaaa9 100644 --- a/src/libtracker-data/tracker-sparql.c +++ b/src/libtracker-data/tracker-sparql.c @@ -150,6 +150,7 @@ typedef struct gint fts_match_idx; gboolean convert_to_string; + gboolean in_property_function; } TrackerSparqlState; struct _TrackerSparql @@ -7595,8 +7596,13 @@ handle_property_function (TrackerSparql *sparql, GError **error) { TrackerPropertyType type; + gboolean in_property_function; - if (tracker_property_get_multiple_values (property)) { + in_property_function = sparql->current_state->in_property_function; + sparql->current_state->in_property_function = TRUE; + + if (!in_property_function && + tracker_property_get_multiple_values (property)) { TrackerStringBuilder *str, *old; _append_string (sparql, "(SELECT GROUP_CONCAT ("); @@ -7638,12 +7644,13 @@ handle_property_function (TrackerSparql *sparql, } } - _append_string (sparql, "WHERE ID = "); + _append_string (sparql, "WHERE ID IN ("); _call_rule (sparql, NAMED_RULE_ArgList, error); - _append_string_printf (sparql, "AND \"%s\" IS NOT NULL", + _append_string_printf (sparql, ") AND \"%s\" IS NOT NULL", tracker_property_get_name (property)); _append_string (sparql, ") "); + sparql->current_state->in_property_function = in_property_function; sparql->current_state->expression_type = type; return TRUE; -- cgit v1.2.1 From f2d2511b5cba7c77432043444eafeab10156b736 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Mon, 1 Mar 2021 18:55:34 +0100 Subject: tests: Add test for nested multivalued properties as property functions As the group_concat ordering is undefined, test with just one value. --- tests/libtracker-data/functions/data-5.ttl | 19 +++++++++++++++++++ .../functions/functions-property-2.out | 3 +++ .../libtracker-data/functions/functions-property-2.rq | 8 ++++++++ tests/libtracker-data/tracker-sparql-test.c | 1 + 4 files changed, 31 insertions(+) create mode 100644 tests/libtracker-data/functions/data-5.ttl create mode 100644 tests/libtracker-data/functions/functions-property-2.out create mode 100644 tests/libtracker-data/functions/functions-property-2.rq diff --git a/tests/libtracker-data/functions/data-5.ttl b/tests/libtracker-data/functions/data-5.ttl new file mode 100644 index 000000000..f82135711 --- /dev/null +++ b/tests/libtracker-data/functions/data-5.ttl @@ -0,0 +1,19 @@ +@prefix : . +@prefix xsd: . + +:x a :A . +:x :s "first" . + +:q a :A . +:q :s "second" . + +:ba a :B . +:ba :o "First group" . +:ba :a :x . + +:bb a :B . +:bb :o "Second group" . +:bb :a :q . + +:bc a :B . +:bc :o "Third group" . diff --git a/tests/libtracker-data/functions/functions-property-2.out b/tests/libtracker-data/functions/functions-property-2.out new file mode 100644 index 000000000..0831c62bc --- /dev/null +++ b/tests/libtracker-data/functions/functions-property-2.out @@ -0,0 +1,3 @@ +"http://example/ba" "first" +"http://example/bb" "second" +"http://example/bc" diff --git a/tests/libtracker-data/functions/functions-property-2.rq b/tests/libtracker-data/functions/functions-property-2.rq new file mode 100644 index 000000000..ec3193db5 --- /dev/null +++ b/tests/libtracker-data/functions/functions-property-2.rq @@ -0,0 +1,8 @@ +PREFIX ex: +PREFIX ns: + +SELECT ?b ex:s(ex:a(?b)) +{ + ?b a ex:B ; +} +ORDER BY ?b diff --git a/tests/libtracker-data/tracker-sparql-test.c b/tests/libtracker-data/tracker-sparql-test.c index 6a92ba744..9552d0aa4 100644 --- a/tests/libtracker-data/tracker-sparql-test.c +++ b/tests/libtracker-data/tracker-sparql-test.c @@ -118,6 +118,7 @@ const TestInfo tests[] = { { "expr-ops/query-unplus-1", "expr-ops/data", FALSE }, { "expr-ops/query-res-1", "expr-ops/data", FALSE }, { "functions/functions-property-1", "functions/data-1", FALSE }, + { "functions/functions-property-2", "functions/data-5", FALSE }, { "functions/functions-tracker-1", "functions/data-1", FALSE }, { "functions/functions-tracker-2", "functions/data-2", FALSE }, { "functions/functions-tracker-3", "functions/data-2", FALSE }, -- cgit v1.2.1