[tracker/wip/carlosg/property-paths: 3/6] libtracker-data: Handle current path operators through WITH clause



commit 0b80cc612020229ce15e0931a0612ae2874227e6
Author: Carlos Garnacho <carlosg gnome org>
Date:   Mon Dec 24 16:00:04 2018 +0100

    libtracker-data: Handle current path operators through WITH clause
    
    Using the WITH clause is already a pre-requisite for * and + operators
    (as those queries need to be recursive). It then struck me that using
    it for all path operators is simpler and easier to read both in code and
    SQL, for starters:
      - We were going long ways to invert the processing order of VerbPath and
        ObjectList, so the latter would be handled within the former. This was
        necessary so we could eg. invert subject and object in ^.
      - Each property path element results in a rather simple SELECT in the
        WITH clause, and nesting those comes off naturally. This is handy as
        a property path is actually an expression tree, which we weren't
        handling that well.
    
    As a first step, add this infrastructure for property paths using the WITH
    clause and use it for the currently supported operators.
    
    We still fall through the usual code paths (no subqueries in WITH clause)
    if the property path is formed of a single literal with no operators
    (i.e. the good old "?s foo:bar ?o" case). This is still necessary for
    fts:match (which is not really a property, thus can't be used in property
    paths) and rdfs:domain special casing (which just applies if the predicate
    is a variable, not the case with property paths).

 src/libtracker-data/tracker-sparql-types.c | 136 ++++++++++++++++
 src/libtracker-data/tracker-sparql-types.h |  44 ++++-
 src/libtracker-data/tracker-sparql.c       | 251 +++++++++++++++++++++--------
 3 files changed, 364 insertions(+), 67 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 */
@@ -197,6 +198,14 @@ tracker_token_parameter_init (TrackerToken *token,
        token->content.parameter = g_strdup (parameter);
 }
 
+void
+tracker_token_path_init (TrackerToken       *token,
+                         TrackerPathElement *path)
+{
+       token->type = TOKEN_TYPE_PATH;
+       token->content.path = path;
+}
+
 void
 tracker_token_unset (TrackerToken *token)
 {
@@ -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..a1befd7f5 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,55 @@ _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;
+       default:
+               g_assert_not_reached ();
+               break;
+       }
+
+       tracker_sparql_swap_builder (sparql, old);
+}
+
 static inline gchar *
 _extract_node_string (TrackerParserNode *node,
                       TrackerSparql     *sparql)
@@ -1082,6 +1133,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 +1178,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 +1284,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 +1315,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 +2800,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 +4038,6 @@ translate_PropertyListPathNotEmpty (TrackerSparql  *sparql,
 {
        TrackerGrammarNamedRule rule;
        TrackerToken old_predicate, *prev_token;
-       TrackerParserNode *verb;
 
        /* PropertyListPathNotEmpty ::= ( VerbPath | VerbSimple ) ObjectListPath ( ';' ( ( VerbPath | 
VerbSimple ) ObjectList )? )*
         */
@@ -3977,30 +4047,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 +4080,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 +4114,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 +4150,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;
 }
 
@@ -4094,34 +4174,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 +4225,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,17 +4263,7 @@ 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
@@ -4201,10 +4287,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 +6637,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 +6815,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;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]