diff options
author | Carlos Garnacho <carlosg@gnome.org> | 2019-01-13 14:41:19 +0100 |
---|---|---|
committer | Carlos Garnacho <carlosg@gnome.org> | 2019-01-13 14:41:19 +0100 |
commit | 41ee9d363a4b670891d712ba37a045847e2a706b (patch) | |
tree | a3b464bb3b8ee57c4b88045395875024af060c4d | |
parent | fd083be75fc9c46613d4d392f638c2b37e71a22a (diff) | |
parent | 7e09abe116b4dcedf177f3cf8f66ce347a4a8075 (diff) | |
download | tracker-41ee9d363a4b670891d712ba37a045847e2a706b.tar.gz |
Merge branch 'wip/carlosg/property-paths'
31 files changed, 528 insertions, 73 deletions
diff --git a/src/libtracker-data/tracker-sparql-types.c b/src/libtracker-data/tracker-sparql-types.c index 1c6125f0f..c3c8eae3c 100644 --- a/src/libtracker-data/tracker-sparql-types.c +++ b/src/libtracker-data/tracker-sparql-types.c @@ -27,6 +27,7 @@ enum { TOKEN_TYPE_LITERAL, TOKEN_TYPE_VARIABLE, TOKEN_TYPE_PARAMETER, + TOKEN_TYPE_PATH, }; /* Helper structs */ @@ -198,6 +199,14 @@ tracker_token_parameter_init (TrackerToken *token, } void +tracker_token_path_init (TrackerToken *token, + TrackerPathElement *path) +{ + token->type = TOKEN_TYPE_PATH; + token->content.path = path; +} + +void tracker_token_unset (TrackerToken *token) { if (token->type == TOKEN_TYPE_LITERAL) @@ -237,6 +246,14 @@ tracker_token_get_parameter (TrackerToken *token) return NULL; } +TrackerPathElement * +tracker_token_get_path (TrackerToken *token) +{ + if (token->type == TOKEN_TYPE_PATH) + return token->content.path; + return NULL; +} + const gchar * tracker_token_get_idstring (TrackerToken *token) { @@ -244,6 +261,8 @@ tracker_token_get_idstring (TrackerToken *token) return token->content.literal; else if (token->type == TOKEN_TYPE_VARIABLE) return token->content.var->sql_expression; + else if (token->type == TOKEN_TYPE_PATH) + return token->content.path->name; else return NULL; } @@ -530,6 +549,86 @@ tracker_variable_binding_get_class (TrackerVariableBinding *binding) return binding->type; } +/* Path element */ +static void +tracker_path_element_free (TrackerPathElement *elem) +{ + g_free (elem->name); + g_free (elem); +} + +TrackerPathElement * +tracker_path_element_property_new (TrackerProperty *prop) +{ + TrackerPathElement *elem; + + g_return_val_if_fail (TRACKER_IS_PROPERTY (prop), NULL); + + elem = g_new0 (TrackerPathElement, 1); + elem->op = TRACKER_PATH_OPERATOR_NONE; + elem->type = tracker_property_get_data_type (prop); + elem->data.property = prop; + + return elem; +} + +TrackerPathElement * +tracker_path_element_operator_new (TrackerPathOperator op, + TrackerPathElement *child1, + TrackerPathElement *child2) +{ + TrackerPathElement *elem; + + g_return_val_if_fail (op != TRACKER_PATH_OPERATOR_NONE, NULL); + g_return_val_if_fail (child1 != NULL, NULL); + g_return_val_if_fail (child2 == NULL || + op == TRACKER_PATH_OPERATOR_SEQUENCE || + op == TRACKER_PATH_OPERATOR_ALTERNATIVE, NULL); + + elem = g_new0 (TrackerPathElement, 1); + elem->op = op; + elem->data.composite.child1 = child1; + elem->data.composite.child2 = child2; + elem->type = child2 ? child2->type : child1->type; + + return elem; +} + +static void +tracker_path_element_set_unique_name (TrackerPathElement *elem, + gint id) +{ + const gchar *name = NULL; + + switch (elem->op) { + case TRACKER_PATH_OPERATOR_NONE: + name = tracker_property_get_name (elem->data.property); + break; + case TRACKER_PATH_OPERATOR_INVERSE: + name = "inv"; + break; + case TRACKER_PATH_OPERATOR_SEQUENCE: + name = "seq"; + break; + case TRACKER_PATH_OPERATOR_ALTERNATIVE: + name = "alt"; + break; + case TRACKER_PATH_OPERATOR_ZEROORONE: + name = "zeroorone"; + break; + case TRACKER_PATH_OPERATOR_ZEROORMORE: + name = "zeroormore"; + break; + case TRACKER_PATH_OPERATOR_ONEORMORE: + name = "oneormore"; + break; + default: + g_assert_not_reached (); + } + + elem->name = g_strdup_printf ("p%d_%s", id, name); +} + /* Context */ G_DEFINE_TYPE (TrackerContext, tracker_context, G_TYPE_INITIALLY_UNOWNED) @@ -627,6 +726,7 @@ tracker_select_context_finalize (GObject *object) g_clear_pointer (&context->predicate_variables, g_hash_table_unref); g_clear_pointer (&context->generated_variables, g_ptr_array_unref); g_clear_pointer (&context->literal_bindings, g_ptr_array_unref); + g_clear_pointer (&context->path_elements, g_ptr_array_unref); G_OBJECT_CLASS (tracker_select_context_parent_class)->finalize (object); } @@ -759,6 +859,42 @@ tracker_select_context_get_literal_binding_index (TrackerSelectContext *context return -1; } +void +tracker_select_context_add_path_element (TrackerSelectContext *context, + TrackerPathElement *path_elem) +{ + if (!context->path_elements) { + context->path_elements = + g_ptr_array_new_with_free_func ((GDestroyNotify) tracker_path_element_free); + } + + g_ptr_array_add (context->path_elements, path_elem); + tracker_path_element_set_unique_name (path_elem, + context->path_elements->len); +} + +TrackerPathElement * +tracker_select_context_lookup_path_element_for_property (TrackerSelectContext *context, + TrackerProperty *property) +{ + guint i; + + if (!context->path_elements) + return NULL; + + for (i = 0; i < context->path_elements->len; i++) { + TrackerPathElement *path_elem; + + path_elem = g_ptr_array_index (context->path_elements, i); + + if (path_elem->op == TRACKER_PATH_OPERATOR_NONE && + path_elem->data.property == property) + return path_elem; + } + + return NULL; +} + /* Triple context */ G_DEFINE_TYPE (TrackerTripleContext, tracker_triple_context, TRACKER_TYPE_CONTEXT) diff --git a/src/libtracker-data/tracker-sparql-types.h b/src/libtracker-data/tracker-sparql-types.h index 1df06764c..bf3484475 100644 --- a/src/libtracker-data/tracker-sparql-types.h +++ b/src/libtracker-data/tracker-sparql-types.h @@ -70,6 +70,7 @@ typedef struct _TrackerDataTable TrackerDataTable; typedef struct _TrackerVariable TrackerVariable; typedef struct _TrackerToken TrackerToken; typedef struct _TrackerSolution TrackerSolution; +typedef struct _TrackerPathElement TrackerPathElement; typedef struct _TrackerPredicateVariable TrackerPredicateVariable; struct _TrackerDataTable { @@ -135,6 +136,7 @@ struct _TrackerToken { gchar *literal; gchar *parameter; TrackerVariable *var; + TrackerPathElement *path; } content; }; @@ -153,6 +155,30 @@ struct _TrackerSolution { int n_cols; }; +typedef enum { + TRACKER_PATH_OPERATOR_NONE, + TRACKER_PATH_OPERATOR_INVERSE, /* ^ */ + TRACKER_PATH_OPERATOR_SEQUENCE, /* / */ + TRACKER_PATH_OPERATOR_ALTERNATIVE, /* | */ + TRACKER_PATH_OPERATOR_ZEROORONE, /* ? */ + TRACKER_PATH_OPERATOR_ONEORMORE, /* + */ + TRACKER_PATH_OPERATOR_ZEROORMORE, /* * */ +} TrackerPathOperator; + +struct _TrackerPathElement { + TrackerPathOperator op; + TrackerPropertyType type; + gchar *name; + + union { + TrackerProperty *property; + struct { + TrackerPathElement *child1; + TrackerPathElement *child2; + } composite; + } data; +}; + struct _TrackerContext { GInitiallyUnowned parent_instance; TrackerContext *parent; @@ -190,6 +216,9 @@ struct _TrackerSelectContext { /* Type to propagate upwards */ TrackerPropertyType type; + + /* Property path elements */ + GPtrArray *path_elements; }; struct _TrackerSelectContextClass { @@ -275,7 +304,9 @@ void tracker_token_literal_init (TrackerToken *token, void tracker_token_variable_init (TrackerToken *token, TrackerVariable *variable); void tracker_token_parameter_init (TrackerToken *token, - const gchar *pameter); + const gchar *parameter); +void tracker_token_path_init (TrackerToken *token, + TrackerPathElement *path_elem); void tracker_token_unset (TrackerToken *token); gboolean tracker_token_is_empty (TrackerToken *token); @@ -283,6 +314,7 @@ const gchar * tracker_token_get_literal (TrackerToken *token); TrackerVariable * tracker_token_get_variable (TrackerToken *token); const gchar * tracker_token_get_idstring (TrackerToken *token); const gchar * tracker_token_get_parameter (TrackerToken *token); +TrackerPathElement * tracker_token_get_path (TrackerToken *token); /* Predicate variable */ TrackerPredicateVariable *tracker_predicate_variable_new (void); @@ -306,6 +338,11 @@ void tracker_solution_add_value (TrackerSolution *solution, const gchar *str); GHashTable * tracker_solution_get_bindings (TrackerSolution *solution); +/* Property path element */ +TrackerPathElement * tracker_path_element_property_new (TrackerProperty *prop); +TrackerPathElement * tracker_path_element_operator_new (TrackerPathOperator op, + TrackerPathElement *child1, + TrackerPathElement *child2); /* Context */ GType tracker_context_get_type (void) G_GNUC_CONST; @@ -338,6 +375,11 @@ void tracker_select_context_add_literal_binding (TrackerSelectContext *context, TrackerLiteralBinding *binding); guint tracker_select_context_get_literal_binding_index (TrackerSelectContext *context, TrackerLiteralBinding *binding); +void tracker_select_context_add_path_element (TrackerSelectContext *context, + TrackerPathElement *path_elem); +TrackerPathElement * + tracker_select_context_lookup_path_element_for_property (TrackerSelectContext *context, + TrackerProperty *property); /* Triple context */ GType tracker_triple_context_get_type (void) G_GNUC_CONST; diff --git a/src/libtracker-data/tracker-sparql.c b/src/libtracker-data/tracker-sparql.c index db60aa822..d123c6330 100644 --- a/src/libtracker-data/tracker-sparql.c +++ b/src/libtracker-data/tracker-sparql.c @@ -126,9 +126,9 @@ struct _TrackerSparql TrackerContext *context; TrackerContext *select_context; TrackerStringBuilder *sql; + TrackerStringBuilder *with_clauses; TrackerParserNode *node; TrackerParserNode *prev_node; - TrackerParserNode *object_list; TrackerToken graph; TrackerToken subject; @@ -137,6 +137,8 @@ struct _TrackerSparql TrackerToken *token; + TrackerPathElement *path; + GHashTable *blank_node_map; const gchar *expression_list_separator; @@ -527,6 +529,104 @@ _append_variable_sql (TrackerSparql *sparql, } } +static void +_prepend_path_element (TrackerSparql *sparql, + TrackerPathElement *path_elem) +{ + TrackerStringBuilder *old; + + 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, ", "); + + switch (path_elem->op) { + case TRACKER_PATH_OPERATOR_NONE: + /* A simple property */ + _append_string_printf (sparql, + "\"%s\" (ID, value, graph) AS " + "(SELECT ID, \"%s\", \"%s:graph\" FROM \"%s\") ", + path_elem->name, + tracker_property_get_name (path_elem->data.property), + tracker_property_get_name (path_elem->data.property), + tracker_property_get_table_name (path_elem->data.property)); + break; + case TRACKER_PATH_OPERATOR_INVERSE: + _append_string_printf (sparql, + "\"%s\" (ID, value, graph) AS " + "(SELECT value, ID, graph FROM \"%s\" WHERE value IS NOT NULL) ", + path_elem->name, + path_elem->data.composite.child1->name); + break; + case TRACKER_PATH_OPERATOR_SEQUENCE: + _append_string_printf (sparql, + "\"%s\" (ID, value, graph) AS " + "(SELECT a.ID, b.value, b.graph " + "FROM \"%s\" AS a, \"%s\" AS b " + "WHERE a.value = b.ID) ", + path_elem->name, + path_elem->data.composite.child1->name, + path_elem->data.composite.child2->name); + break; + case TRACKER_PATH_OPERATOR_ALTERNATIVE: + _append_string_printf (sparql, + "\"%s\" (ID, value, graph) AS " + "(SELECT ID, value, graph " + "FROM \"%s\" " + "UNION ALL " + "SELECT ID, value, graph " + "FROM \"%s\") ", + path_elem->name, + path_elem->data.composite.child1->name, + path_elem->data.composite.child2->name); + break; + case TRACKER_PATH_OPERATOR_ZEROORMORE: + _append_string_printf (sparql, + "\"%s\" (ID, value, graph) AS " + "(SELECT ID, ID, graph " + "FROM \"%s\" " + "UNION " + "SELECT a.ID, b.value, b.graph " + "FROM \"%s\" AS a, \"%s\" AS b " + "WHERE b.ID = a.value) ", + path_elem->name, + path_elem->data.composite.child1->name, + path_elem->data.composite.child1->name, + path_elem->name); + break; + case TRACKER_PATH_OPERATOR_ONEORMORE: + _append_string_printf (sparql, + "\"%s\" (ID, value, graph) AS " + "(SELECT ID, value, graph " + "FROM \"%s\" " + "UNION " + "SELECT a.ID, b.value, b.graph " + "FROM \"%s\" AS a, \"%s\" AS b " + "WHERE b.ID = a.value) ", + path_elem->name, + path_elem->data.composite.child1->name, + path_elem->data.composite.child1->name, + path_elem->name); + break; + case TRACKER_PATH_OPERATOR_ZEROORONE: + _append_string_printf (sparql, + "\"%s\" (ID, value, graph) AS " + "(SELECT ID, ID, graph " + "FROM \"%s\" " + "UNION ALL " + "SELECT ID, value, graph " + "FROM \"%s\") ", + path_elem->name, + path_elem->data.composite.child1->name, + path_elem->data.composite.child1->name); + break; + } + + tracker_sparql_swap_builder (sparql, old); +} + static inline gchar * _extract_node_string (TrackerParserNode *node, TrackerSparql *sparql) @@ -1082,6 +1182,11 @@ _add_quad (TrackerSparql *sparql, if (!tracker_token_is_empty (graph)) pred_var->return_graph = TRUE; + } else if (tracker_token_get_path (predicate)) { + table = tracker_triple_context_add_table (triple_context, + "value", + tracker_token_get_idstring (predicate)); + new_table = TRUE; } else { /* The parser disallows parameter predicates */ g_assert_not_reached (); @@ -1122,6 +1227,13 @@ _add_quad (TrackerSparql *sparql, tracker_binding_set_data_type (binding, TRACKER_PROPERTY_TYPE_STRING); tracker_binding_set_db_column_name (binding, "object"); tracker_variable_binding_set_nullable (TRACKER_VARIABLE_BINDING (binding), TRUE); + } else if (tracker_token_get_path (predicate)) { + TrackerPathElement *path; + + path = tracker_token_get_path (predicate); + tracker_binding_set_data_type (binding, path->type); + tracker_binding_set_db_column_name (binding, "value"); + tracker_variable_binding_set_nullable (TRACKER_VARIABLE_BINDING (binding), TRUE); } else { g_assert (property != NULL); tracker_binding_set_data_type (binding, tracker_property_get_data_type (property)); @@ -1221,6 +1333,12 @@ _add_quad (TrackerSparql *sparql, if (tracker_token_get_variable (predicate)) { tracker_binding_set_db_column_name (binding, "object"); + } else if (tracker_token_get_path (predicate)) { + TrackerPathElement *path; + + path = tracker_token_get_path (predicate); + tracker_binding_set_db_column_name (binding, "value"); + tracker_binding_set_data_type (binding, path->type); } else { g_assert (property != NULL); tracker_binding_set_data_type (binding, tracker_property_get_data_type (property)); @@ -1246,7 +1364,8 @@ _add_quad (TrackerSparql *sparql, tracker_binding_set_data_type (binding, TRACKER_PROPERTY_TYPE_RESOURCE); - if (tracker_token_get_variable (predicate)) { + if (tracker_token_get_variable (predicate) || + tracker_token_get_path (predicate)) { tracker_binding_set_db_column_name (binding, "graph"); } else { gchar *column_name; @@ -2730,6 +2849,7 @@ get_solution_for_pattern (TrackerSparql *sparql, g_clear_pointer (&sparql->sql, tracker_string_builder_free); sparql->sql = tracker_string_builder_new (); tracker_sparql_swap_builder (sparql, sparql->sql); + sparql->current_state.with_clauses = _prepend_placeholder (sparql); retval = prepare_solution_select (sparql, pattern, error); tracker_sparql_pop_context (sparql, FALSE); @@ -3967,7 +4087,6 @@ translate_PropertyListPathNotEmpty (TrackerSparql *sparql, { TrackerGrammarNamedRule rule; TrackerToken old_predicate, *prev_token; - TrackerParserNode *verb; /* PropertyListPathNotEmpty ::= ( VerbPath | VerbSimple ) ObjectListPath ( ';' ( ( VerbPath | VerbSimple ) ObjectList )? )* */ @@ -3977,30 +4096,24 @@ translate_PropertyListPathNotEmpty (TrackerSparql *sparql, sparql->current_state.token = &sparql->current_state.object; if (rule == NAMED_RULE_VerbPath || rule == NAMED_RULE_VerbSimple) { - verb = _skip_rule (sparql, rule); + _call_rule (sparql, rule, error); } else { g_assert_not_reached (); } - sparql->current_state.object_list = _skip_rule (sparql, NAMED_RULE_ObjectListPath); - if (!_postprocess_rule (sparql, verb, NULL, error)) - return FALSE; - + _call_rule (sparql, NAMED_RULE_ObjectListPath, error); tracker_token_unset (&sparql->current_state.predicate); while (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_SEMICOLON)) { rule = _current_rule (sparql); if (rule == NAMED_RULE_VerbPath || rule == NAMED_RULE_VerbSimple) { - verb = _skip_rule (sparql, rule); + _call_rule (sparql, rule, error); } else { break; } - sparql->current_state.object_list = _skip_rule (sparql, NAMED_RULE_ObjectList); - if (!_postprocess_rule (sparql, verb, NULL, error)) - return FALSE; - + _call_rule (sparql, NAMED_RULE_ObjectList, error); tracker_token_unset (&sparql->current_state.predicate); } @@ -4016,7 +4129,27 @@ translate_VerbPath (TrackerSparql *sparql, { /* VerbPath ::= Path */ - _call_rule (sparql, NAMED_RULE_Path, error); + + /* If this path consists of a single element, do not set + * up a property path. Just set the property token to + * be the only property literal and let _add_quad() + * apply its optimizations. + */ + if (g_node_n_nodes ((GNode *) sparql->current_state.node, + G_TRAVERSE_LEAVES) == 1) { + TrackerParserNode *prop; + gchar *str; + + prop = tracker_sparql_parser_tree_find_first (sparql->current_state.node, TRUE); + str = _extract_node_string (prop, sparql); + tracker_token_literal_init (&sparql->current_state.predicate, str); + g_free (str); + + _skip_rule (sparql, NAMED_RULE_Path); + } else { + _call_rule (sparql, NAMED_RULE_Path, error); + sparql->current_state.path = NULL; + } return TRUE; } @@ -4030,11 +4163,6 @@ translate_VerbSimple (TrackerSparql *sparql, _call_rule (sparql, NAMED_RULE_Var, error); _init_token (&sparql->current_state.predicate, sparql->current_state.prev_node, sparql); - - if (!_postprocess_rule (sparql, sparql->current_state.object_list, - NULL, error)) - return FALSE; - return TRUE; } @@ -4071,7 +4199,8 @@ translate_Path (TrackerSparql *sparql, /* Path ::= PathAlternative */ _call_rule (sparql, NAMED_RULE_PathAlternative, error); - + tracker_token_path_init (&sparql->current_state.predicate, + sparql->current_state.path); return TRUE; } @@ -4079,14 +4208,47 @@ static gboolean translate_PathAlternative (TrackerSparql *sparql, GError **error) { + GPtrArray *path_elems; + + path_elems = g_ptr_array_new (); + /* PathAlternative ::= PathSequence ( '|' PathSequence )* */ _call_rule (sparql, NAMED_RULE_PathSequence, error); + g_ptr_array_add (path_elems, sparql->current_state.path); - if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_PATH_ALTERNATIVE)) { - _unimplemented ("Alternative property path"); + while (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_PATH_ALTERNATIVE)) { + _call_rule (sparql, NAMED_RULE_PathSequence, error); + g_ptr_array_add (path_elems, sparql->current_state.path); } + if (path_elems->len > 1) { + TrackerPathElement *path_elem; + gint i; + + path_elem = tracker_path_element_operator_new (TRACKER_PATH_OPERATOR_ALTERNATIVE, + g_ptr_array_index (path_elems, 0), + g_ptr_array_index (path_elems, 1)); + tracker_select_context_add_path_element (TRACKER_SELECT_CONTEXT (sparql->context), + path_elem); + _prepend_path_element (sparql, path_elem); + + for (i = 2; i < path_elems->len; i++) { + TrackerPathElement *child; + + child = g_ptr_array_index (path_elems, i); + path_elem = tracker_path_element_operator_new (TRACKER_PATH_OPERATOR_ALTERNATIVE, + child, path_elem); + tracker_select_context_add_path_element (TRACKER_SELECT_CONTEXT (sparql->context), + path_elem); + _prepend_path_element (sparql, path_elem); + } + + sparql->current_state.path = path_elem; + } + + g_ptr_array_unref (path_elems); + return TRUE; } @@ -4094,34 +4256,49 @@ static gboolean translate_PathSequence (TrackerSparql *sparql, GError **error) { - TrackerToken old_object, old_subject; - TrackerVariable *var; - TrackerParserNode *rule; + GPtrArray *path_elems; + + path_elems = g_ptr_array_new (); /* PathSequence ::= PathEltOrInverse ( '/' PathEltOrInverse )* */ - old_object = sparql->current_state.object; - old_subject = sparql->current_state.subject; - - rule = _skip_rule (sparql, NAMED_RULE_PathEltOrInverse); + _call_rule (sparql, NAMED_RULE_PathEltOrInverse, error); + g_ptr_array_add (path_elems, sparql->current_state.path); while (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_PATH_SEQUENCE)) { - var = tracker_select_context_add_generated_variable (TRACKER_SELECT_CONTEXT (sparql->context)); - tracker_token_variable_init (&sparql->current_state.object, var); + _call_rule (sparql, NAMED_RULE_PathEltOrInverse, error); + g_ptr_array_add (path_elems, sparql->current_state.path); + } - if (!_postprocess_rule (sparql, rule, NULL, error)) - return FALSE; + if (path_elems->len > 1) { + TrackerPathElement *path_elem; + gint i; - rule = _skip_rule (sparql, NAMED_RULE_PathEltOrInverse); - sparql->current_state.subject = sparql->current_state.object; - tracker_token_unset (&sparql->current_state.object); - } + /* We must handle path elements in inverse order, paired to + * the path element created in the previous step. + */ + path_elem = tracker_path_element_operator_new (TRACKER_PATH_OPERATOR_SEQUENCE, + g_ptr_array_index (path_elems, path_elems->len - 2), + g_ptr_array_index (path_elems, path_elems->len - 1)); + tracker_select_context_add_path_element (TRACKER_SELECT_CONTEXT (sparql->context), + path_elem); + _prepend_path_element (sparql, path_elem); + + for (i = ((gint) path_elems->len) - 3; i >= 0; i--) { + TrackerPathElement *child; + + child = g_ptr_array_index (path_elems, i); + path_elem = tracker_path_element_operator_new (TRACKER_PATH_OPERATOR_SEQUENCE, + child, path_elem); + tracker_select_context_add_path_element (TRACKER_SELECT_CONTEXT (sparql->context), + path_elem); + _prepend_path_element (sparql, path_elem); + } - if (!_postprocess_rule (sparql, rule, NULL, error)) - return FALSE; + sparql->current_state.path = path_elem; + } - sparql->current_state.subject = old_subject; - sparql->current_state.object = old_object; + g_ptr_array_unref (path_elems); return TRUE; } @@ -4130,25 +4307,26 @@ static gboolean translate_PathEltOrInverse (TrackerSparql *sparql, GError **error) { - TrackerToken old_object, old_subject, *old_token; + gboolean inverse = FALSE; /* PathEltOrInverse ::= PathElt | '^' PathElt */ - old_object = sparql->current_state.object; - old_subject = sparql->current_state.subject; - old_token = sparql->current_state.token; - - if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_PATH_INVERSE)) { - sparql->current_state.object = old_subject; - sparql->current_state.subject = old_object; - sparql->current_state.token = &sparql->current_state.subject; - } + if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_PATH_INVERSE)) + inverse = TRUE; _call_rule (sparql, NAMED_RULE_PathElt, error); - sparql->current_state.subject = old_subject; - sparql->current_state.object = old_object; - sparql->current_state.token = old_token; + if (inverse) { + TrackerPathElement *path_elem; + + path_elem = tracker_path_element_operator_new (TRACKER_PATH_OPERATOR_INVERSE, + sparql->current_state.path, + NULL); + tracker_select_context_add_path_element (TRACKER_SELECT_CONTEXT (sparql->context), + path_elem); + _prepend_path_element (sparql, path_elem); + sparql->current_state.path = path_elem; + } return TRUE; } @@ -4167,26 +4345,34 @@ translate_PathElt (TrackerSparql *sparql, _call_rule (sparql, NAMED_RULE_PathMod, error); } - if (!tracker_token_is_empty (sparql->current_state.token)) { - return _add_quad (sparql, - &sparql->current_state.graph, - &sparql->current_state.subject, - &sparql->current_state.predicate, - &sparql->current_state.object, - error); - } else { - return _postprocess_rule (sparql, sparql->current_state.object_list, - NULL, error); - } + return TRUE; } static gboolean translate_PathMod (TrackerSparql *sparql, GError **error) { + TrackerPathElement *path_elem; + TrackerPathOperator op; + /* PathMod ::= '?' | '*' | '+' */ - _unimplemented ("Path modifiers"); + if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_PATH_STAR)) { + op = TRACKER_PATH_OPERATOR_ZEROORMORE; + } else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_PATH_PLUS)) { + op = TRACKER_PATH_OPERATOR_ONEORMORE; + } else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_PATH_OPTIONAL)) { + op = TRACKER_PATH_OPERATOR_ZEROORONE; + } else { + return TRUE; + } + + path_elem = tracker_path_element_operator_new (op, sparql->current_state.path, NULL); + tracker_select_context_add_path_element (TRACKER_SELECT_CONTEXT (sparql->context), + path_elem); + _prepend_path_element (sparql, path_elem); + sparql->current_state.path = path_elem; + return TRUE; } static gboolean @@ -4201,10 +4387,41 @@ translate_PathPrimary (TrackerSparql *sparql, _call_rule (sparql, NAMED_RULE_Path, error); _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS); - } else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_A)) { + } else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_A) || + _check_in_rule (sparql, NAMED_RULE_iri)) { + TrackerOntologies *ontologies; + TrackerProperty *prop; + TrackerPathElement *path_elem; + gchar *str; - } else if (_check_in_rule (sparql, NAMED_RULE_iri)) { - _call_rule (sparql, NAMED_RULE_iri, error); + if (_check_in_rule (sparql, NAMED_RULE_iri)) + _call_rule (sparql, NAMED_RULE_iri, error); + + str = _dup_last_string (sparql); + ontologies = tracker_data_manager_get_ontologies (sparql->data_manager); + prop = tracker_ontologies_get_property_by_uri (ontologies, str); + + if (!prop) { + g_set_error (error, TRACKER_SPARQL_ERROR, + TRACKER_SPARQL_ERROR_UNKNOWN_PROPERTY, + "Unknown property '%s'", str); + g_free (str); + return FALSE; + } + + path_elem = + tracker_select_context_lookup_path_element_for_property (TRACKER_SELECT_CONTEXT (sparql->context), + prop); + + if (!path_elem) { + path_elem = tracker_path_element_property_new (prop); + tracker_select_context_add_path_element (TRACKER_SELECT_CONTEXT (sparql->context), + path_elem); + _prepend_path_element (sparql, path_elem); + } + + sparql->current_state.path = path_elem; + g_free (str); } else { g_assert_not_reached (); } @@ -6520,6 +6737,7 @@ tracker_sparql_new (TrackerDataManager *manager, sparql->current_state.node = tracker_node_tree_get_root (sparql->tree); sparql->current_state.sql = sparql->sql; + sparql->current_state.with_clauses = _prepend_placeholder (sparql); } return sparql; @@ -6697,6 +6915,7 @@ tracker_sparql_new_update (TrackerDataManager *manager, sparql->current_state.node = tracker_node_tree_get_root (sparql->tree); sparql->current_state.sql = sparql->sql; + sparql->current_state.with_clauses = _prepend_placeholder (sparql); } return sparql; diff --git a/tests/libtracker-data/property-paths/alternative-path-1.out b/tests/libtracker-data/property-paths/alternative-path-1.out new file mode 100644 index 000000000..d4ba27789 --- /dev/null +++ b/tests/libtracker-data/property-paths/alternative-path-1.out @@ -0,0 +1,5 @@ +"Alice" "Bob" +"Alice" "Foo" +"Bob" "Alice" +"Bob" "Foo" +"Eve" "Fred" diff --git a/tests/libtracker-data/property-paths/alternative-path-1.rq b/tests/libtracker-data/property-paths/alternative-path-1.rq new file mode 100644 index 000000000..ddd3a0732 --- /dev/null +++ b/tests/libtracker-data/property-paths/alternative-path-1.rq @@ -0,0 +1 @@ +select foaf:name(?u) foaf:name(?a) { ?u a foaf:Person . ?u foaf:knows|foaf:member ?a } order by foaf:name(?u) foaf:name(?a)
\ No newline at end of file diff --git a/tests/libtracker-data/property-paths/alternative-path-2.out b/tests/libtracker-data/property-paths/alternative-path-2.out new file mode 100644 index 000000000..ef0350743 --- /dev/null +++ b/tests/libtracker-data/property-paths/alternative-path-2.out @@ -0,0 +1 @@ +"Alice" diff --git a/tests/libtracker-data/property-paths/alternative-path-2.rq b/tests/libtracker-data/property-paths/alternative-path-2.rq new file mode 100644 index 000000000..b90ecc092 --- /dev/null +++ b/tests/libtracker-data/property-paths/alternative-path-2.rq @@ -0,0 +1 @@ +select foaf:name(?u) { ?u a foaf:Person . ?u foaf:name|(foaf:member/foaf:name) "Alice" } order by foaf:name(?u)
\ No newline at end of file diff --git a/tests/libtracker-data/property-paths/inverse-path-1.out b/tests/libtracker-data/property-paths/inverse-path-1.out index 0a9f74d4f..9f47bc3b5 100644 --- a/tests/libtracker-data/property-paths/inverse-path-1.out +++ b/tests/libtracker-data/property-paths/inverse-path-1.out @@ -1,3 +1,3 @@ -"Bob" "Alice" "Alice" "Bob" +"Bob" "Alice" "Fred" "Eve" diff --git a/tests/libtracker-data/property-paths/inverse-path-1.rq b/tests/libtracker-data/property-paths/inverse-path-1.rq index 1aaf070ce..6b643dfd7 100644 --- a/tests/libtracker-data/property-paths/inverse-path-1.rq +++ b/tests/libtracker-data/property-paths/inverse-path-1.rq @@ -1 +1 @@ -select foaf:name(?a) foaf:name(?b) { ?a ^foaf:knows ?b }
\ No newline at end of file +select foaf:name(?a) foaf:name(?b) { ?a ^foaf:knows ?b } order by foaf:name(?a) foaf:name(?b)
\ No newline at end of file diff --git a/tests/libtracker-data/property-paths/mixed-inverse-and-sequence-3.out b/tests/libtracker-data/property-paths/mixed-inverse-and-sequence-3.out new file mode 100644 index 000000000..d12a7b1c3 --- /dev/null +++ b/tests/libtracker-data/property-paths/mixed-inverse-and-sequence-3.out @@ -0,0 +1,2 @@ +"Alice" "Alice" +"Bob" "Bob" diff --git a/tests/libtracker-data/property-paths/mixed-inverse-and-sequence-3.rq b/tests/libtracker-data/property-paths/mixed-inverse-and-sequence-3.rq new file mode 100644 index 000000000..3383f36b8 --- /dev/null +++ b/tests/libtracker-data/property-paths/mixed-inverse-and-sequence-3.rq @@ -0,0 +1 @@ +select foaf:name(?a) foaf:name(?b) { ?a ^(foaf:knows/foaf:knows) ?b } order by foaf:name(?a) foaf:name(?b)
\ No newline at end of file diff --git a/tests/libtracker-data/property-paths/mixed-recursive-and-alternative-1.out b/tests/libtracker-data/property-paths/mixed-recursive-and-alternative-1.out new file mode 100644 index 000000000..878be5329 --- /dev/null +++ b/tests/libtracker-data/property-paths/mixed-recursive-and-alternative-1.out @@ -0,0 +1,7 @@ +"Alice" "Alice" +"Alice" "Bob" +"Alice" "Foo" +"Bob" "Alice" +"Bob" "Bob" +"Bob" "Foo" +"Eve" "Fred" diff --git a/tests/libtracker-data/property-paths/mixed-recursive-and-alternative-1.rq b/tests/libtracker-data/property-paths/mixed-recursive-and-alternative-1.rq new file mode 100644 index 000000000..44d2d7f88 --- /dev/null +++ b/tests/libtracker-data/property-paths/mixed-recursive-and-alternative-1.rq @@ -0,0 +1 @@ +select foaf:name(?u) foaf:name(?a) { ?u foaf:knows+|foaf:member ?a } order by foaf:name(?u) foaf:name(?a)
\ No newline at end of file diff --git a/tests/libtracker-data/property-paths/mixed-recursive-and-alternative-2.out b/tests/libtracker-data/property-paths/mixed-recursive-and-alternative-2.out new file mode 100644 index 000000000..878be5329 --- /dev/null +++ b/tests/libtracker-data/property-paths/mixed-recursive-and-alternative-2.out @@ -0,0 +1,7 @@ +"Alice" "Alice" +"Alice" "Bob" +"Alice" "Foo" +"Bob" "Alice" +"Bob" "Bob" +"Bob" "Foo" +"Eve" "Fred" diff --git a/tests/libtracker-data/property-paths/mixed-recursive-and-alternative-2.rq b/tests/libtracker-data/property-paths/mixed-recursive-and-alternative-2.rq new file mode 100644 index 000000000..7f4c969c5 --- /dev/null +++ b/tests/libtracker-data/property-paths/mixed-recursive-and-alternative-2.rq @@ -0,0 +1 @@ +select foaf:name(?u) foaf:name(?a) { ?u (foaf:knows|foaf:member)+ ?a } order by foaf:name(?u) foaf:name(?a)
\ No newline at end of file diff --git a/tests/libtracker-data/property-paths/mixed-recursive-and-inverse-1.out b/tests/libtracker-data/property-paths/mixed-recursive-and-inverse-1.out new file mode 100644 index 000000000..0c64aed0a --- /dev/null +++ b/tests/libtracker-data/property-paths/mixed-recursive-and-inverse-1.out @@ -0,0 +1 @@ +"Fred" "Eve" diff --git a/tests/libtracker-data/property-paths/mixed-recursive-and-inverse-1.rq b/tests/libtracker-data/property-paths/mixed-recursive-and-inverse-1.rq new file mode 100644 index 000000000..a43469234 --- /dev/null +++ b/tests/libtracker-data/property-paths/mixed-recursive-and-inverse-1.rq @@ -0,0 +1 @@ +select foaf:name(?u) foaf:name(?a) { ?u ^foaf:knows+ ?a . ?u foaf:name 'Fred' }
\ No newline at end of file diff --git a/tests/libtracker-data/property-paths/mixed-recursive-and-inverse-2.out b/tests/libtracker-data/property-paths/mixed-recursive-and-inverse-2.out new file mode 100644 index 000000000..0c64aed0a --- /dev/null +++ b/tests/libtracker-data/property-paths/mixed-recursive-and-inverse-2.out @@ -0,0 +1 @@ +"Fred" "Eve" diff --git a/tests/libtracker-data/property-paths/mixed-recursive-and-inverse-2.rq b/tests/libtracker-data/property-paths/mixed-recursive-and-inverse-2.rq new file mode 100644 index 000000000..ebda97c36 --- /dev/null +++ b/tests/libtracker-data/property-paths/mixed-recursive-and-inverse-2.rq @@ -0,0 +1 @@ +select foaf:name(?u) foaf:name(?a) { ?u ^(foaf:knows+) ?a . ?u foaf:name 'Fred' }
\ No newline at end of file diff --git a/tests/libtracker-data/property-paths/mixed-recursive-and-inverse-3.out b/tests/libtracker-data/property-paths/mixed-recursive-and-inverse-3.out new file mode 100644 index 000000000..0c64aed0a --- /dev/null +++ b/tests/libtracker-data/property-paths/mixed-recursive-and-inverse-3.out @@ -0,0 +1 @@ +"Fred" "Eve" diff --git a/tests/libtracker-data/property-paths/mixed-recursive-and-inverse-3.rq b/tests/libtracker-data/property-paths/mixed-recursive-and-inverse-3.rq new file mode 100644 index 000000000..3b5416657 --- /dev/null +++ b/tests/libtracker-data/property-paths/mixed-recursive-and-inverse-3.rq @@ -0,0 +1 @@ +select foaf:name(?u) foaf:name(?a) { ?u (^foaf:knows)+ ?a . ?u foaf:name 'Fred' }
\ No newline at end of file diff --git a/tests/libtracker-data/property-paths/mixed-recursive-and-sequence-1.out b/tests/libtracker-data/property-paths/mixed-recursive-and-sequence-1.out new file mode 100644 index 000000000..ac2dc970e --- /dev/null +++ b/tests/libtracker-data/property-paths/mixed-recursive-and-sequence-1.out @@ -0,0 +1,2 @@ +"Alice" +"Bob" diff --git a/tests/libtracker-data/property-paths/mixed-recursive-and-sequence-1.rq b/tests/libtracker-data/property-paths/mixed-recursive-and-sequence-1.rq new file mode 100644 index 000000000..1771d11d2 --- /dev/null +++ b/tests/libtracker-data/property-paths/mixed-recursive-and-sequence-1.rq @@ -0,0 +1 @@ +select foaf:name(?u) { ?u foaf:knows*/foaf:name 'Alice' } order by foaf:name(?u)
\ No newline at end of file diff --git a/tests/libtracker-data/property-paths/optional-path-1.out b/tests/libtracker-data/property-paths/optional-path-1.out new file mode 100644 index 000000000..525aee442 --- /dev/null +++ b/tests/libtracker-data/property-paths/optional-path-1.out @@ -0,0 +1,2 @@ +"Alice" "Alice" +"Alice" "Bob" diff --git a/tests/libtracker-data/property-paths/optional-path-1.rq b/tests/libtracker-data/property-paths/optional-path-1.rq new file mode 100644 index 000000000..d1da57abb --- /dev/null +++ b/tests/libtracker-data/property-paths/optional-path-1.rq @@ -0,0 +1 @@ +select foaf:name(?u) foaf:name(?a) { ?u foaf:name 'Alice'; foaf:knows? ?a } order by foaf:name(?u) foaf:name(?a)
\ No newline at end of file diff --git a/tests/libtracker-data/property-paths/recursive-path-1.out b/tests/libtracker-data/property-paths/recursive-path-1.out new file mode 100644 index 000000000..525aee442 --- /dev/null +++ b/tests/libtracker-data/property-paths/recursive-path-1.out @@ -0,0 +1,2 @@ +"Alice" "Alice" +"Alice" "Bob" diff --git a/tests/libtracker-data/property-paths/recursive-path-1.rq b/tests/libtracker-data/property-paths/recursive-path-1.rq new file mode 100644 index 000000000..9bd80f372 --- /dev/null +++ b/tests/libtracker-data/property-paths/recursive-path-1.rq @@ -0,0 +1 @@ +select foaf:name(?u) foaf:name(?a) { ?u foaf:knows* ?a . FILTER (foaf:name(?u) = 'Alice') }
\ No newline at end of file diff --git a/tests/libtracker-data/property-paths/recursive-path-2.out b/tests/libtracker-data/property-paths/recursive-path-2.out new file mode 100644 index 000000000..a02d506ff --- /dev/null +++ b/tests/libtracker-data/property-paths/recursive-path-2.out @@ -0,0 +1 @@ +"Alice" "Bob" diff --git a/tests/libtracker-data/property-paths/recursive-path-2.rq b/tests/libtracker-data/property-paths/recursive-path-2.rq new file mode 100644 index 000000000..1ed508ee4 --- /dev/null +++ b/tests/libtracker-data/property-paths/recursive-path-2.rq @@ -0,0 +1 @@ +select foaf:name(?u) foaf:name(?a) { ?u foaf:knows+ ?a . FILTER (?u != ?a && foaf:name(?u) = 'Alice') } order by foaf:name(?u) foaf:name(?a)
\ No newline at end of file diff --git a/tests/libtracker-data/property-paths/sequence-path-1.rq b/tests/libtracker-data/property-paths/sequence-path-1.rq index 8900f9d99..a703adf55 100644 --- a/tests/libtracker-data/property-paths/sequence-path-1.rq +++ b/tests/libtracker-data/property-paths/sequence-path-1.rq @@ -1 +1 @@ -select foaf:name(?a) foaf:name(?b) { ?a foaf:knows/foaf:knows ?b }
\ No newline at end of file +select foaf:name(?a) foaf:name(?b) { ?a foaf:knows/foaf:knows ?b } order by foaf:name(?a) foaf:name(?b)
\ No newline at end of file diff --git a/tests/libtracker-data/tracker-sparql-test.c b/tests/libtracker-data/tracker-sparql-test.c index 3982fc125..2db5166ae 100644 --- a/tests/libtracker-data/tracker-sparql-test.c +++ b/tests/libtracker-data/tracker-sparql-test.c @@ -155,8 +155,20 @@ const TestInfo tests[] = { { "property-paths/sequence-path-1", "property-paths/data", FALSE }, { "property-paths/sequence-path-2", "property-paths/data", FALSE }, { "property-paths/sequence-path-3", "property-paths/data", FALSE }, + { "property-paths/optional-path-1", "property-paths/data", FALSE }, + { "property-paths/recursive-path-1", "property-paths/data", FALSE }, + { "property-paths/recursive-path-2", "property-paths/data", FALSE }, + { "property-paths/alternative-path-1", "property-paths/data", FALSE }, + { "property-paths/alternative-path-2", "property-paths/data", FALSE }, { "property-paths/mixed-inverse-and-sequence-1", "property-paths/data", FALSE }, { "property-paths/mixed-inverse-and-sequence-2", "property-paths/data", FALSE }, + { "property-paths/mixed-inverse-and-sequence-3", "property-paths/data", FALSE }, + { "property-paths/mixed-recursive-and-sequence-1", "property-paths/data", FALSE }, + { "property-paths/mixed-recursive-and-alternative-1", "property-paths/data", FALSE }, + { "property-paths/mixed-recursive-and-alternative-2", "property-paths/data", FALSE }, + { "property-paths/mixed-recursive-and-inverse-1", "property-paths/data", FALSE }, + { "property-paths/mixed-recursive-and-inverse-2", "property-paths/data", FALSE }, + { "property-paths/mixed-recursive-and-inverse-3", "property-paths/data", FALSE }, /* Update tests */ { "update/insert-data-query-1", "update/insert-data-1", FALSE, FALSE }, { "update/insert-data-query-2", "update/insert-data-2", FALSE, TRUE }, @@ -361,6 +373,8 @@ test_sparql_query (TestInfo *test_info, g_free (results_filename); g_object_unref (test_schemas); g_object_unref (data_location); + + tracker_data_manager_shutdown (manager); g_object_unref (manager); } |