[tracker] SPARQL: Fix case sensitivity of variables
- From: Jürg Billeter <juergbi src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [tracker] SPARQL: Fix case sensitivity of variables
- Date: Wed, 21 Oct 2009 12:36:13 +0000 (UTC)
commit 6f6ff52054edc622e4f80a3c43f569f698831949
Author: Jürg Billeter <j bitron ch>
Date: Wed Oct 21 14:33:35 2009 +0200
SPARQL: Fix case sensitivity of variables
Work around SQLite bug to allow case sensitive variables in SPARQL.
Fixes NB#135969.
src/libtracker-data/tracker-sparql-query.vala | 201 +++++++++++++++----------
1 files changed, 119 insertions(+), 82 deletions(-)
---
diff --git a/src/libtracker-data/tracker-sparql-query.vala b/src/libtracker-data/tracker-sparql-query.vala
index a786405..cdf5de6 100644
--- a/src/libtracker-data/tracker-sparql-query.vala
+++ b/src/libtracker-data/tracker-sparql-query.vala
@@ -58,7 +58,7 @@ public class Tracker.SparqlQuery : Object {
// Represents a mapping of a SPARQL variable to a SQL table and column
class VariableBinding : DataBinding {
- public string variable;
+ public Variable variable;
// Specified whether SQL column may contain NULL entries
public bool maybe_null;
public bool in_simple_optional;
@@ -69,6 +69,17 @@ public class Tracker.SparqlQuery : Object {
public List<VariableBinding> list;
}
+ class Variable : Object {
+ public string name { get; private set; }
+ public string sql_expression { get; private set; }
+ public VariableBinding binding;
+
+ public Variable (string name, string sql_identifier) {
+ this.name = name;
+ this.sql_expression = "\"%s\"".printf (sql_identifier);
+ }
+ }
+
// Represents a variable used as a predicate
class PredicateVariable : Object {
public string? subject;
@@ -269,15 +280,18 @@ public class Tracker.SparqlQuery : Object {
List<LiteralBinding> pattern_bindings;
// All SPARQL variables
- HashTable<string,VariableBinding> var_map;
- List<string> pattern_variables;
- HashTable<string,VariableBindingList> pattern_var_map;
+ HashTable<string,Variable> var_map;
+ List<Variable> pattern_variables;
+ HashTable<Variable,VariableBindingList> pattern_var_map;
// All SPARQL variables within a subgraph pattern (used by UNION)
- HashTable<string,int> subgraph_var_set;
+ HashTable<Variable,int> subgraph_var_set;
// Variables used as predicates
- HashTable<string,PredicateVariable> predicate_variable_map;
+ HashTable<Variable,PredicateVariable> predicate_variable_map;
+
+ // Keep track of used sql identifiers to avoid using the same for multiple SPARQL variables
+ HashTable<string,bool> used_sql_identifiers;
bool delete_statements;
@@ -290,7 +304,7 @@ public class Tracker.SparqlQuery : Object {
public SparqlQuery (string query) {
tokens = new TokenInfo[BUFFER_SIZE];
prefix_map = new HashTable<string,string>.full (str_hash, str_equal, g_free, g_free);
- subgraph_var_set = new HashTable<string,int>.full (str_hash, str_equal, g_free, null);
+ subgraph_var_set = new HashTable<Variable,int>.full (direct_hash, direct_equal, g_object_unref, null);
base_uuid = new uchar[16];
uuid_generate (base_uuid);
@@ -394,6 +408,25 @@ public class Tracker.SparqlQuery : Object {
return "'%s'".printf (string.joinv ("''", literal.split ("'")));
}
+ Variable get_variable (string name) {
+ var result = var_map.lookup (name);
+ if (result == null) {
+ // use lowercase as SQLite is never case sensitive (not conforming to SQL)
+ string sql_identifier = "%s_u".printf (name).down ();
+
+ // ensure SQL identifier is unique to avoid conflicts between
+ // case sensitive SPARQL and case insensitive SQLite
+ for (int i = 1; used_sql_identifiers.lookup (sql_identifier); i++) {
+ sql_identifier = "%s_%d_u".printf (name, i).down ();
+ }
+ used_sql_identifiers.insert (sql_identifier, true);
+
+ result = new Variable (name, sql_identifier);
+ var_map.insert (name, result);
+ }
+ return result;
+ }
+
void parse_prologue () throws SparqlError {
if (accept (SparqlTokenType.BASE)) {
expect (SparqlTokenType.IRI_REF);
@@ -468,25 +501,23 @@ public class Tracker.SparqlQuery : Object {
}
}
- void check_binding (VariableBinding? binding, string variable_name) throws SparqlError {
- if (binding == null) {
- throw get_error ("`%s' is not a valid variable".printf (variable_name));
+ void check_binding (Variable variable) throws SparqlError {
+ if (variable.binding == null) {
+ throw get_error ("`%s' is not a valid variable".printf (variable.name));
}
}
- string get_sql_for_variable (string variable_name) throws SparqlError {
- var binding = var_map.lookup (variable_name);
-
- check_binding (binding, variable_name);
+ string get_sql_for_variable (Variable variable) throws SparqlError {
+ check_binding (variable);
- if (binding.data_type == PropertyType.RESOURCE) {
- return "(SELECT Uri FROM \"rdfs:Resource\" WHERE ID = \"%s_u\")".printf (variable_name);
- } else if (binding.data_type == PropertyType.BOOLEAN) {
- return "(CASE \"%s_u\" WHEN 1 THEN 'true' WHEN 0 THEN 'false' ELSE NULL END)".printf (variable_name);
- } else if (binding.data_type == PropertyType.DATETIME) {
- return "strftime (\"%%Y-%%m-%%dT%%H:%%M:%%SZ\", \"%s_u\", \"unixepoch\")".printf (variable_name);
+ if (variable.binding.data_type == PropertyType.RESOURCE) {
+ return "(SELECT Uri FROM \"rdfs:Resource\" WHERE ID = %s)".printf (variable.sql_expression);
+ } else if (variable.binding.data_type == PropertyType.BOOLEAN) {
+ return "(CASE %s WHEN 1 THEN 'true' WHEN 0 THEN 'false' ELSE NULL END)".printf (variable.sql_expression);
+ } else if (variable.binding.data_type == PropertyType.DATETIME) {
+ return "strftime (\"%%Y-%%m-%%dT%%H:%%M:%%SZ\", %s, \"unixepoch\")".printf (variable.sql_expression);
} else {
- return "\"%s_u\"".printf (variable_name);
+ return variable.sql_expression;
}
}
@@ -595,8 +626,9 @@ public class Tracker.SparqlQuery : Object {
// SELECT query
var pattern_sql = new StringBuilder ();
- var_map = new HashTable<string,VariableBinding>.full (str_hash, str_equal, g_free, g_object_unref);
- predicate_variable_map = new HashTable<string,PredicateVariable>.full (str_hash, str_equal, g_free, g_object_unref);
+ var_map = new HashTable<string,Variable>.full (str_hash, str_equal, g_free, g_object_unref);
+ predicate_variable_map = new HashTable<Variable,PredicateVariable>.full (direct_hash, direct_equal, g_object_unref, g_object_unref);
+ used_sql_identifiers = new HashTable<string,bool>.full (str_hash, str_equal, g_free, null);
// build SQL
var sql = new StringBuilder ();
@@ -628,13 +660,13 @@ public class Tracker.SparqlQuery : Object {
bool first = true;
if (accept (SparqlTokenType.STAR)) {
- foreach (string variable_name in var_map.get_keys ()) {
+ foreach (var variable in var_map.get_values ()) {
if (!first) {
sql.append (", ");
} else {
first = false;
}
- sql.append (get_sql_for_variable (variable_name));
+ sql.append (get_sql_for_variable (variable));
}
} else {
while (true) {
@@ -773,8 +805,9 @@ public class Tracker.SparqlQuery : Object {
// ASK query
var pattern_sql = new StringBuilder ();
- var_map = new HashTable<string,VariableBinding>.full (str_hash, str_equal, g_free, g_object_unref);
- predicate_variable_map = new HashTable<string,PredicateVariable>.full (str_hash, str_equal, g_free, g_object_unref);
+ var_map = new HashTable<string,Variable>.full (str_hash, str_equal, g_free, g_object_unref);
+ predicate_variable_map = new HashTable<Variable,PredicateVariable>.full (direct_hash, direct_equal, g_object_unref, g_object_unref);
+ used_sql_identifiers = new HashTable<string,bool>.full (str_hash, str_equal, g_free, null);
// build SQL
var sql = new StringBuilder ();
@@ -810,8 +843,9 @@ public class Tracker.SparqlQuery : Object {
// INSERT or DELETE
var pattern_sql = new StringBuilder ();
- var_map = new HashTable<string,VariableBinding>.full (str_hash, str_equal, g_free, g_object_unref);
- predicate_variable_map = new HashTable<string,PredicateVariable>.full (str_hash, str_equal, g_free, g_object_unref);
+ var_map = new HashTable<string,Variable>.full (str_hash, str_equal, g_free, g_object_unref);
+ predicate_variable_map = new HashTable<Variable,PredicateVariable>.full (direct_hash, direct_equal, g_object_unref, g_object_unref);
+ used_sql_identifiers = new HashTable<string,bool>.full (str_hash, str_equal, g_free, null);
var sql = new StringBuilder ();
@@ -827,14 +861,14 @@ public class Tracker.SparqlQuery : Object {
// build SQL
sql.append ("SELECT ");
bool first = true;
- foreach (VariableBinding binding in var_map.get_values ()) {
+ foreach (var variable in var_map.get_values ()) {
if (!first) {
sql.append (", ");
} else {
first = false;
}
- sql.append (get_sql_for_variable (binding.variable));
+ sql.append (get_sql_for_variable (variable));
}
if (first) {
@@ -1145,18 +1179,18 @@ public class Tracker.SparqlQuery : Object {
if (accept (SparqlTokenType.VAR)) {
string variable_name = get_last_string().substring(1);
- var binding = var_map.lookup (variable_name);
+ var variable = get_variable (variable_name);
- check_binding (binding, variable_name);
+ check_binding (variable);
- if (binding.data_type == PropertyType.RESOURCE || binding.type == null) {
+ if (variable.binding.data_type == PropertyType.RESOURCE || variable.binding.type == null) {
throw get_error ("Invalid FILTER");
}
sql.append ("(SELECT ID FROM \"rdfs:Resource\" WHERE Uri = ?)");
var new_binding = new LiteralBinding ();
- new_binding.literal = binding.type;
+ new_binding.literal = variable.binding.type;
bindings.append (new_binding);
} else {
@@ -1376,13 +1410,13 @@ public class Tracker.SparqlQuery : Object {
case SparqlTokenType.VAR:
next ();
string variable_name = get_last_string ().substring (1);
- sql.append_printf ("\"%s_u\"", variable_name);
+ var variable = get_variable (variable_name);
+ sql.append (variable.sql_expression);
- var binding = var_map.lookup (variable_name);
- if (binding == null) {
+ if (variable.binding == null) {
return PropertyType.UNKNOWN;
} else {
- return binding.data_type;
+ return variable.binding.data_type;
}
case SparqlTokenType.STR:
translate_str (sql);
@@ -1647,8 +1681,9 @@ public class Tracker.SparqlQuery : Object {
case SparqlTokenType.ISLITERAL:
case SparqlTokenType.REGEX:
return translate_primary_expression (sql);
+ default:
+ return translate_bracketted_expression (sql);
}
- return translate_bracketted_expression (sql);
}
void translate_filter (StringBuilder sql) throws SparqlError {
@@ -1673,6 +1708,8 @@ public class Tracker.SparqlQuery : Object {
case SparqlTokenType.REGEX:
next ();
break;
+ default:
+ break;
}
expect (SparqlTokenType.OPEN_PARENS);
@@ -1852,8 +1889,8 @@ public class Tracker.SparqlQuery : Object {
tables = new List<DataTable> ();
table_map = new HashTable<string,DataTable>.full (str_hash, str_equal, g_free, g_object_unref);
- pattern_variables = new List<string> ();
- pattern_var_map = new HashTable<string,VariableBindingList>.full (str_hash, str_equal, g_free, g_object_unref);
+ pattern_variables = new List<Variable> ();
+ pattern_var_map = new HashTable<Variable,VariableBindingList>.full (direct_hash, direct_equal, g_object_unref, g_object_unref);
pattern_bindings = new List<LiteralBinding> ();
@@ -1880,7 +1917,7 @@ public class Tracker.SparqlQuery : Object {
sql.append_printf (" AS \"%s\"", table.sql_query_tablename);
}
- foreach (string variable in pattern_variables) {
+ foreach (var variable in pattern_variables) {
bool maybe_null = true;
bool in_simple_optional = false;
string last_name = null;
@@ -1913,7 +1950,7 @@ public class Tracker.SparqlQuery : Object {
sql.append (" WHERE ");
first_where = false;
}
- sql.append_printf ("\"%s_u\" IS NOT NULL", variable);
+ sql.append_printf ("%s IS NOT NULL", variable.sql_expression);
}
}
foreach (LiteralBinding binding in pattern_bindings) {
@@ -2003,7 +2040,7 @@ public class Tracker.SparqlQuery : Object {
return false;
}
string var_name = get_last_string ().substring (1);
- if (subgraph_var_set.lookup (var_name) != VariableState.BOUND) {
+ if (subgraph_var_set.lookup (get_variable (var_name)) != VariableState.BOUND) {
return false;
}
@@ -2033,7 +2070,7 @@ public class Tracker.SparqlQuery : Object {
return false;
}
var_name = get_last_string ().substring (1);
- if (subgraph_var_set.lookup (var_name) != 0) {
+ if (subgraph_var_set.lookup (get_variable (var_name)) != 0) {
// object variable already used before in this graph pattern
return false;
}
@@ -2103,7 +2140,7 @@ public class Tracker.SparqlQuery : Object {
sql.append_printf (") AS t%d_g LEFT JOIN (", left_index);
var old_subgraph_var_set = subgraph_var_set;
- subgraph_var_set = new HashTable<string,int>.full (str_hash, str_equal, g_free, null);
+ subgraph_var_set = new HashTable<Variable,int>.full (direct_hash, direct_equal, g_object_unref, null);
translate_group_graph_pattern (sql);
@@ -2111,7 +2148,7 @@ public class Tracker.SparqlQuery : Object {
bool first = true;
bool first_common = true;
- foreach (string v in subgraph_var_set.get_keys ()) {
+ foreach (var v in subgraph_var_set.get_keys ()) {
if (first) {
first = false;
} else {
@@ -2122,7 +2159,7 @@ public class Tracker.SparqlQuery : Object {
if (old_state == 0) {
// first used in optional part
old_subgraph_var_set.insert (v, VariableState.OPTIONAL);
- select.append_printf ("t%d_g.\"%s_u\"", right_index, v);
+ select.append_printf ("t%d_g.%s", right_index, v.sql_expression);
} else {
if (first_common) {
sql.append (" ON ");
@@ -2133,16 +2170,16 @@ public class Tracker.SparqlQuery : Object {
if (old_state == VariableState.BOUND) {
// variable definitely bound in non-optional part
- sql.append_printf ("t%d_g.\"%s_u\" = t%d_g.\"%s_u\"", left_index, v, right_index, v);
- select.append_printf ("t%d_g.\"%s_u\"", left_index, v);
+ sql.append_printf ("t%d_g.%s = t%d_g.%s", left_index, v.sql_expression, right_index, v.sql_expression);
+ select.append_printf ("t%d_g.%s", left_index, v.sql_expression);
} else if (old_state == VariableState.OPTIONAL) {
// variable maybe bound in non-optional part
- sql.append_printf ("(t%d_g.\"%s_u\" IS NULL OR t%d_g.\"%s_u\" = t%d_g.\"%s_u\")", left_index, v, left_index, v, right_index, v);
- select.append_printf ("COALESCE (t%d_g.\"%s_u\", t%d_g.\"%s_u\") AS \"%s_u\"", left_index, v, right_index, v, v);
+ sql.append_printf ("(t%d_g.%s IS NULL OR t%d_g.%s = t%d_g.%s)", left_index, v.sql_expression, left_index, v.sql_expression, right_index, v.sql_expression);
+ select.append_printf ("COALESCE (t%d_g.%s, t%d_g.%s) AS %s", left_index, v.sql_expression, right_index, v.sql_expression, v.sql_expression);
}
}
}
- foreach (string v in old_subgraph_var_set.get_keys ()) {
+ foreach (var v in old_subgraph_var_set.get_keys ()) {
if (subgraph_var_set.lookup (v) == 0) {
// only used in non-optional part
if (first) {
@@ -2151,7 +2188,7 @@ public class Tracker.SparqlQuery : Object {
select.append (", ");
}
- select.append_printf ("t%d_g.\"%s_u\"", left_index, v);
+ select.append_printf ("t%d_g.%s", left_index, v.sql_expression);
}
}
if (first) {
@@ -2237,14 +2274,14 @@ public class Tracker.SparqlQuery : Object {
void translate_group_or_union_graph_pattern (StringBuilder sql) throws SparqlError {
var old_subgraph_var_set = subgraph_var_set;
- string[] all_vars = { };
- HashTable<string,int> all_var_set = new HashTable<string,int>.full (str_hash, str_equal, g_free, null);
+ Variable[] all_vars = { };
+ HashTable<Variable,int> all_var_set = new HashTable<Variable,int>.full (direct_hash, direct_equal, g_object_unref, null);
- HashTable<string,int>[] var_sets = { };
+ HashTable<Variable,int>[] var_sets = { };
long[] offsets = { };
do {
- subgraph_var_set = new HashTable<string,int>.full (str_hash, str_equal, g_free, null);
+ subgraph_var_set = new HashTable<Variable,int>.full (direct_hash, direct_equal, g_object_unref, null);
var_sets += subgraph_var_set;
offsets += sql.len;
translate_group_graph_pattern (sql);
@@ -2255,7 +2292,7 @@ public class Tracker.SparqlQuery : Object {
// create union of all variables
foreach (var var_set in var_sets) {
- foreach (string v in var_set.get_keys ()) {
+ foreach (var v in var_set.get_keys ()) {
if (all_var_set.lookup (v) == 0) {
all_vars += v;
all_var_set.insert (v, VariableState.BOUND);
@@ -2271,13 +2308,13 @@ public class Tracker.SparqlQuery : Object {
projection.append (") UNION ALL ");
}
projection.append ("SELECT ");
- foreach (string v in all_vars) {
+ foreach (var v in all_vars) {
if (var_sets[i].lookup (v) == 0) {
// variable not used in this subgraph
// use NULL
projection.append ("NULL AS ");
}
- projection.append_printf ("\"%s_u\", ", v);
+ projection.append_printf ("%s, ", v.sql_expression);
}
// delete last comma and space
projection.truncate (projection.len - 2);
@@ -2288,7 +2325,7 @@ public class Tracker.SparqlQuery : Object {
}
sql.append (")");
} else {
- foreach (string key in subgraph_var_set.get_keys ()) {
+ foreach (var key in subgraph_var_set.get_keys ()) {
old_subgraph_var_set.insert (key, VariableState.BOUND);
}
}
@@ -2338,10 +2375,10 @@ public class Tracker.SparqlQuery : Object {
if (domain == null) {
throw new SparqlError.UNKNOWN_CLASS ("Unknown class `%s'".printf (object));
}
- var pv = predicate_variable_map.lookup (current_subject);
+ var pv = predicate_variable_map.lookup (get_variable (current_subject));
if (pv == null) {
pv = new PredicateVariable ();
- predicate_variable_map.insert (current_subject, pv);
+ predicate_variable_map.insert (get_variable (current_subject), pv);
}
pv.domain = domain;
}
@@ -2360,10 +2397,10 @@ public class Tracker.SparqlQuery : Object {
// variable in predicate
newtable = true;
table = new DataTable ();
- table.predicate_variable = predicate_variable_map.lookup (current_predicate);
+ table.predicate_variable = predicate_variable_map.lookup (get_variable (current_predicate));
if (table.predicate_variable == null) {
table.predicate_variable = new PredicateVariable ();
- predicate_variable_map.insert (current_predicate, table.predicate_variable);
+ predicate_variable_map.insert (get_variable (current_predicate), table.predicate_variable);
}
if (!current_subject_is_var) {
// single subject
@@ -2379,7 +2416,7 @@ public class Tracker.SparqlQuery : Object {
// add to variable list
var binding = new VariableBinding ();
binding.data_type = PropertyType.RESOURCE;
- binding.variable = current_predicate;
+ binding.variable = get_variable (current_predicate);
binding.table = table;
binding.sql_db_column_name = "predicate";
var binding_list = pattern_var_map.lookup (binding.variable);
@@ -2388,16 +2425,16 @@ public class Tracker.SparqlQuery : Object {
pattern_variables.append (binding.variable);
pattern_var_map.insert (binding.variable, binding_list);
- sql.append_printf ("\"%s\".\"%s\" AS \"%s_u\", ",
+ sql.append_printf ("\"%s\".\"%s\" AS %s, ",
binding.table.sql_query_tablename,
binding.sql_db_column_name,
- binding.variable);
+ binding.variable.sql_expression);
subgraph_var_set.insert (binding.variable, VariableState.BOUND);
}
binding_list.list.append (binding);
- if (var_map.lookup (binding.variable) == null) {
- var_map.insert (binding.variable, binding);
+ if (binding.variable.binding == null) {
+ binding.variable.binding = binding;
}
}
@@ -2405,7 +2442,7 @@ public class Tracker.SparqlQuery : Object {
if (current_subject_is_var) {
var binding = new VariableBinding ();
binding.data_type = PropertyType.RESOURCE;
- binding.variable = current_subject;
+ binding.variable = get_variable (current_subject);
binding.table = table;
if (is_fts_match) {
binding.sql_db_column_name = "rowid";
@@ -2418,16 +2455,16 @@ public class Tracker.SparqlQuery : Object {
pattern_variables.append (binding.variable);
pattern_var_map.insert (binding.variable, binding_list);
- sql.append_printf ("\"%s\".\"%s\" AS \"%s_u\", ",
+ sql.append_printf ("\"%s\".\"%s\" AS %s, ",
binding.table.sql_query_tablename,
binding.sql_db_column_name,
- binding.variable);
+ binding.variable.sql_expression);
subgraph_var_set.insert (binding.variable, VariableState.BOUND);
}
binding_list.list.append (binding);
- if (var_map.lookup (binding.variable) == null) {
- var_map.insert (binding.variable, binding);
+ if (binding.variable.binding == null) {
+ binding.variable.binding = binding;
}
} else {
var binding = new LiteralBinding ();
@@ -2443,7 +2480,7 @@ public class Tracker.SparqlQuery : Object {
if (!rdftype) {
if (object_is_var) {
var binding = new VariableBinding ();
- binding.variable = object;
+ binding.variable = get_variable (object);
binding.table = table;
if (prop != null) {
@@ -2469,16 +2506,16 @@ public class Tracker.SparqlQuery : Object {
pattern_variables.append (binding.variable);
pattern_var_map.insert (binding.variable, binding_list);
- sql.append_printf ("\"%s\".\"%s\" AS \"%s_u\", ",
+ sql.append_printf ("\"%s\".\"%s\" AS %s, ",
binding.table.sql_query_tablename,
binding.sql_db_column_name,
- binding.variable);
+ binding.variable.sql_expression);
subgraph_var_set.insert (binding.variable, in_simple_optional ? VariableState.OPTIONAL : VariableState.BOUND);
}
binding_list.list.append (binding);
- if (var_map.lookup (binding.variable) == null) {
- var_map.insert (binding.variable, binding);
+ if (binding.variable.binding == null) {
+ binding.variable.binding = binding;
}
} else if (is_fts_match) {
var binding = new LiteralBinding ();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]