Commit 41ee9d36 authored by Carlos Garnacho's avatar Carlos Garnacho

Merge branch 'wip/carlosg/property-paths'

parents fd083be7 7e09abe1
Pipeline #50995 passed with stage
in 1 minute and 56 seconds
......@@ -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)
......
......@@ -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;
......
......@@ -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);
}