using System; using System.Collections; using System.IO; using System.Text; using SemWeb; using SemWeb.Stores; namespace SemWeb.Query { public class RSquary : QueryEngine { // TODO: Optional statements // TODO: Grouping and disjunctions public static Entity qSelect = "http://purl.oclc.org/NET/rsquary/select"; public static Entity qLimit = "http://purl.oclc.org/NET/rsquary/returnLimit"; public static Entity qStart = "http://purl.oclc.org/NET/rsquary/returnStart"; public static Entity qDistinctFrom = "http://purl.oclc.org/NET/rsquary/distinctFrom"; public static Entity qOptional = "http://purl.oclc.org/NET/rsquary/optional"; public RSquary(Store queryModel, string queryUri) : this(queryModel, queryUri, null) { } public RSquary(Store queryModel, string queryUri, Hashtable extraValueFilters) { Entity query = new Entity(queryUri); // Find the query options ReturnStart = GetIntOption(queryModel, query, qStart); ReturnLimit = GetIntOption(queryModel, query, qLimit); // Search the query for 'select' foreach (Resource r in queryModel.SelectObjects(query, qSelect)) { if (!(r is Entity)) throw new QueryException("Query variables cannot be literals."); Select((Entity)r); } // Search the query for 'distinct' predicates between variables. foreach (Statement s in queryModel.Select(new Statement(null, qDistinctFrom, null))) { if (!(s.Object is Entity)) throw new QueryException("The distinctFrom predicate cannot have a literal as its object."); MakeDistinct(s.Subject, (Entity)s.Object); } // Add all statements except the query predicates and value filters into a // new store with just the statements relevant to the search. foreach (Statement s in queryModel.Select(new Statement(null,null,null))) { if (IsQueryPredicate(s.Predicate)) continue; if (s.Predicate.Uri != null && extraValueFilters != null && extraValueFilters.ContainsKey(s.Predicate.Uri)) { ValueFilterFactory f = (ValueFilterFactory)extraValueFilters[s.Predicate.Uri]; AddValueFilter(s.Subject, f.GetValueFilter(s.Predicate.Uri, s.Object)); continue; } else { ValueFilter f = ValueFilter.GetValueFilter(s.Predicate, s.Object); if (f != null) { AddValueFilter(s.Subject, f); continue; } } if (s.Meta == Statement.DefaultMeta) AddFilter(s); else if (queryModel.Contains(new Statement(query, qOptional, s.Meta))) AddOptionalFilter(s); } } private int GetIntOption(Store queryModel, Entity query, Entity predicate) { Resource[] rr = queryModel.SelectObjects(query, predicate); if (rr.Length == 0) return -1; Resource r = rr[0]; if (r == null || !(r is Literal)) return -1; try { return int.Parse(((Literal)r).Value); } catch (Exception e) { throw new QueryException("Invalid integer value for <" + predicate + ">, '" + ((Literal)r).Value + "'.", e); } } private bool IsQueryPredicate(Entity e) { if (e == qSelect) return true; if (e == qDistinctFrom) return true; if (e == qLimit) return true; if (e == qStart) return true; if (e == qOptional) return true; return false; } } public class PrintQuerySink : QueryResultSink { public override void Init(Entity[] variables) { } public override void Finished() { } public override bool Add(VariableBinding[] result) { foreach (VariableBinding var in result) if (var.Variable.Uri != null && var.Target != null) Console.WriteLine(var.Variable + " ==> " + var.Target.ToString()); Console.WriteLine(); return true; } } public class HTMLQuerySink : QueryResultSink { TextWriter output; public HTMLQuerySink(TextWriter output) { this.output = output; } public override void Init(Entity[] variables) { output.WriteLine(""); foreach (Entity var in variables) if (var.Uri != null) output.WriteLine("" + var + ""); output.WriteLine(""); } public override void Finished() { } public override bool Add(VariableBinding[] result) { output.WriteLine(""); foreach (VariableBinding var in result) { if (var.Variable.Uri == null) continue; string t = var.Target.ToString(); if (var.Target is Literal) t = ((Literal)var.Target).Value; output.WriteLine("" + t + ""); } output.WriteLine(""); return true; } } public class SQLQuerySink : QueryResultSink { TextWriter output; string table; public SQLQuerySink(TextWriter output, string table) { this.output = output; this.table = table; } public override void Finished() { } private string GetFieldType(string datatype) { switch (datatype) { case "http://www.w3.org/2001/XMLSchema#string": case "http://www.w3.org/2001/XMLSchema#normalizedString": return "TEXT"; case "http://www.w3.org/2001/XMLSchema#float": return "FLOAT"; case "http://www.w3.org/2001/XMLSchema#double": return "DOUBLE PRECISION"; case "http://www.w3.org/2001/XMLSchema#decimal": return "DECIMAL"; case "http://www.w3.org/2001/XMLSchema#integer": case "http://www.w3.org/2001/XMLSchema#nonPositiveInteger": case "http://www.w3.org/2001/XMLSchema#negativeInteger": case "http://www.w3.org/2001/XMLSchema#int": case "http://www.w3.org/2001/XMLSchema#short": return "INT"; case "http://www.w3.org/2001/XMLSchema#long": return "BIGINT"; case "http://www.w3.org/2001/XMLSchema#boolean": case "http://www.w3.org/2001/XMLSchema#byte": case "http://www.w3.org/2001/XMLSchema#unsignedByte": return "SMALLINT"; case "http://www.w3.org/2001/XMLSchema#nonNegativeInteger": case "http://www.w3.org/2001/XMLSchema#unsignedInt": case "http://www.w3.org/2001/XMLSchema#unsignedShort": case "http://www.w3.org/2001/XMLSchema#positiveInteger": return "UNSIGNED INT"; case "http://www.w3.org/2001/XMLSchema#unsignedLong": return "UNSIGNED BIGINT"; case "http://www.w3.org/2001/XMLSchema#dateTime": return "DATETIME"; case "http://www.w3.org/2001/XMLSchema#date": return "DATE"; case "http://www.w3.org/2001/XMLSchema#time": case "http://www.w3.org/2001/XMLSchema#duration": return "TIME"; case "http://www.w3.org/2001/XMLSchema#base64Binary": return "BLOB"; case "http://www.w3.org/2001/XMLSchema#anyURI": // shouldn't be case-insensitive, but using BLOB // instead seems to make things too complex. return "TEXT"; } return "TEXT"; } public override void Init(Entity[] variables) { output.Write("CREATE TABLE " + table + " ("); bool f = true; foreach (Entity var in variables) { if (var.Uri == null) continue; string name; int hash = var.Uri.LastIndexOf("#"); if (hash == -1) name = "`" + var.Uri + "`"; else name = var.Uri.Substring(hash+1); string type = "BLOB"; //if (var.Target is Literal && ((Literal)var.Target).DataType != null) // type = GetFieldType(((Literal)var.Target).DataType); if (!f) { output.Write(", "); } f = false; output.Write(name + " " + type); } output.WriteLine(");"); } public override bool Add(VariableBinding[] result) { output.Write("INSERT INTO " + table + " VALUES ("); bool firstx = true; foreach (VariableBinding var in result) { if (var.Variable.Uri == null) continue; if (!firstx) { output.Write(", "); } firstx = false; if (var.Target == null) output.Write("NULL"); else if (var.Target is Literal) output.Write(Escape(((Literal)var.Target).Value)); else if (var.Target.Uri != null) output.Write("\"" + var.Target.Uri + "\""); else output.Write("\"\""); } output.WriteLine(");"); return true; } private string Escape(string str) { if (str == null) return "NULL"; return "\"" + EscapeUnquoted(str) + "\""; } StringBuilder EscapeUnquotedBuffer = new StringBuilder(); private string EscapeUnquoted(string str) { StringBuilder b = EscapeUnquotedBuffer; b.Length = 0; b.Append(str); SQLStore.Escape(b); return b.ToString(); } } public class SparqlXmlQuerySink : QueryResultSink { System.Xml.XmlWriter output; string variableNamespace; int blankNodeCounter = 0; Hashtable blankNodes = new Hashtable(); private static System.Xml.XmlWriter GetWriter(System.IO.TextWriter writer) { System.Xml.XmlTextWriter w = new System.Xml.XmlTextWriter(writer); w.Formatting = System.Xml.Formatting.Indented; return w; } public SparqlXmlQuerySink(TextWriter output, string variableNamespace) : this(GetWriter(output), variableNamespace) { } public SparqlXmlQuerySink(System.Xml.XmlWriter output, string variableNamespace) { this.output = output; this.variableNamespace = variableNamespace; output.WriteStartElement("sparql"); output.WriteAttributeString("xmlns", "http://www.w3.org/2001/sw/DataAccess/rf1/result"); output.WriteStartElement("head"); } string GetName(string uri) { if (uri.StartsWith(variableNamespace)) uri = uri.Substring(variableNamespace.Length); if (uri.StartsWith("?") || uri.StartsWith("$")) uri = uri.Substring(1); return uri; } public override void Init(Entity[] variables) { foreach (Entity var in variables) { if (var.Uri == null) continue; output.WriteStartElement("variable"); output.WriteAttributeString("name", GetName(var.Uri)); output.WriteEndElement(); } output.WriteEndElement(); // head output.WriteStartElement("results"); } public override bool Add(VariableBinding[] result) { output.WriteStartElement("result"); foreach (VariableBinding var in result) { if (var.Variable.Uri == null) continue; output.WriteStartElement(GetName(var.Variable.Uri)); if (var.Target == null) { output.WriteAttributeString("bound", "false"); } else if (var.Target.Uri != null) { output.WriteAttributeString("uri", var.Target.Uri); } else if (var.Target is Literal) { Literal literal = (Literal)var.Target; if (literal.DataType != null) output.WriteAttributeString("datatype", literal.DataType); if (literal.Language != null) output.WriteAttributeString("language", literal.Language); output.WriteString(literal.Value); } else { string id; if (blankNodes.ContainsKey(var.Target)) id = (string)blankNodes[var.Target]; else { id = "r" + (++blankNodeCounter); blankNodes[var.Target] = id; } output.WriteAttributeString("bnodeid", id); } output.WriteEndElement(); } output.WriteEndElement(); return true; } public override void Finished() { output.WriteEndElement(); // results output.WriteEndElement(); // sparql output.Close(); } } }