class TestYuzuQL(unittest.TestCase): def __init__(self, *args, **kwargs): super(TestYuzuQL, self).__init__(*args, **kwargs) self.syntax = YuzuQLSyntax() def check_good(self, q, sql): select = self.syntax.parse(q, {}) qb = QueryBuilder(select) sql2 = qb.build() self.assertEqual(sql, sql2) def check_bad(self, q): try: self.syntax.parse(q, {}) self.fail("Should fail") except: pass def test_simple(self): self.check_good("select * { ?s <foo> $o }", "SELECT table0.subject, table0.object FROM triples AS " "table0 WHERE table0.property=\"<foo>\"") def test_literal(self): self.check_good("select * { ?s <foo> \"bar\" }", "SELECT table0.subject FROM triples AS table0 " "WHERE table0.property=\"<foo>\" AND " "table0.object=\"\"\"bar\"\"\"") def test_lang_literal(self): self.check_good("select * { ?s <foo> \"bar\"@en }", "SELECT table0.subject FROM triples AS table0 " "WHERE table0.property=\"<foo>\" AND " "table0.object=\"\"\"bar\"\"@en\"") def test_lc_lang(self): self.check_good("select * { ?s <foo> \"bar\"@EN }", "SELECT table0.subject FROM triples AS table0 " "WHERE table0.property=\"<foo>\" AND " "table0.object=\"\"\"bar\"\"@en\"") def test_implicit_join(self): self.check_good("select ?s { ?s <foo> ?o ; <bar> ?o }", "SELECT table1.subject " "FROM triples AS table0 " "JOIN triples AS table1 " "ON table0.sid=table1.sid " "WHERE table0.property=\"<foo>\" " "AND table1.property=\"<bar>\" " "AND table0.object=table1.object") def test_typed_literal(self): self.check_good("select * { ?s <foo> \"bar\"^^<baz> }", "SELECT table0.subject FROM triples AS table0 " "WHERE table0.property=\"<foo>\" AND " "table0.object=\"\"\"bar\"\"^^<baz>\"") def test_with_vars(self): self.check_good("select ?s ?o where { ?s <foo> ?o }", "SELECT table0.subject, table0.object FROM triples AS " "table0 WHERE table0.property=\"<foo>\"") def test_with_offset_limit(self): self.check_good("select * { ?s <foo> ?o } order by str(?o) limit " "10 offset 20", "SELECT table0.subject, table0.object FROM triples AS " "table0 WHERE table0.property=\"<foo>\" " "ORDER BY table0.object LIMIT 10 OFFSET 20") def test_asc(self): self.check_good("select ?s { ?s <foo> ?o } order by asc(str(?o))", "SELECT table0.subject " "FROM triples AS table0 " "WHERE table0.property=\"<foo>\" " "ORDER BY table0.object ASC") def test_desc(self): self.check_good("select ?s { ?s <foo> ?o } order by desc(str(?o))", "SELECT table0.subject " "FROM triples AS table0 " "WHERE table0.property=\"<foo>\" " "ORDER BY table0.object DESC") def test_case(self): self.check_good("SELECT * { ?s <foo> ?o } ORDER BY str(?o) " "LIMIT 10 OFFSET 20", "SELECT table0.subject, table0.object FROM triples AS " "table0 WHERE table0.property=\"<foo>\" " "ORDER BY table0.object LIMIT 10 OFFSET 20") def test_a(self): self.check_good("select * { ?s a ?o }", "SELECT table0.subject, table0.object FROM triples AS " "table0 WHERE table0.property=\"<http://www.w3.org/" "1999/02/22-rdf-syntax-ns#type>\"") def test_graph(self): self.check_bad("select * from <foo> { ?s a ?o }") def test_ask(self): self.check_bad("ask { ?s a ?o }") def test_describe(self): self.check_bad("describe ?s { ?s a ?o }") def test_construct(self): self.check_bad("construct { ?s a ?o } where { ?s a ?o }") def test_order_by(self): self.check_good("select * { ?s <foo> ?o } order by str(?s)", "SELECT table0.subject, table0.object FROM triples " "AS table0 WHERE table0.property=\"<foo>\" " "ORDER BY table0.subject") def test_multiple_order_by(self): self.check_good("select * { ?s <foo> ?o } order by str(?s) str(?o)", "SELECT table0.subject, table0.object FROM triples " "AS table0 WHERE table0.property=\"<foo>\" " "ORDER BY table0.subject, table0.object") def test_order_by_expr(self): self.check_bad("select * { ?s a ?o } order by int(?s)") def test_limit_offset(self): self.check_good("select * { ?s <foo> ?o } limit 10 offset 20", "SELECT table0.subject, table0.object FROM triples " "AS table0 WHERE table0.property=\"<foo>\" " "LIMIT 10 OFFSET 20") def test_exists(self): self.check_bad("select * { filter exists { ?s a ?o } }") def test_not_exists(self): self.check_bad("select * { filter not exists { ?s a ?o } }") def test_as(self): self.check_bad("select (?s as ?foo) { ?s a ?o }") def test_values(self): self.check_bad("select * { values ?foo { 'x' } . ?s a ?o }") def test_graph2(self): self.check_bad("select * { graph ?g { ?s a ?o } }") def test_service(self): self.check_bad("select * { service <foo> { ?s a ?o } }") def test_bind(self): self.check_bad("select * { bind('x' as ?foo) ?s a ?o }") def test_minus(self): self.check_bad("select * { minus { ?s a ?o } }") def test_filter(self): self.check_bad("select * { ?s a ?o filter(?s > 5) }") def test_union(self): self.check_bad("select * { { ?s a ?o } union { ?s a ?o } }") def test_optional(self): self.check_bad("select * { optional { ?s a ?o } }") def test_count(self): self.check_bad("select (count(?s) as ?c) { ?s a ?o }") def test_2trips(self): self.check_bad("select * { ?s a ?o . ?s2 a ?o2 }") def test_ssjoin(self): self.check_good("select * { ?s <foo> ?o ; <bar> ?o2 }", "SELECT table1.subject, table1.object, table0.object " "FROM triples AS table0 JOIN triples AS table1 ON " "table0.sid=table1.sid WHERE " "table0.property=\"<foo>\" AND " "table1.property=\"<bar>\"") def test_objjoin(self): self.check_good("select * { ?s <foo> ?o , ?o2 . }", "SELECT table1.subject, table1.object, table0.object " "FROM triples AS table0 JOIN triples AS table1 ON " "table0.sid=table1.sid WHERE " "table0.property=\"<foo>\" AND " "table1.property=\"<foo>\"") def test_var_prop(self): self.check_bad("select * { ?s ?p ?o }") def test_path(self): self.check_bad("select * { ?s <foo>/<bar> ?o }") def test_optional4(self): self.check_bad("select * { ?s <foo>|<bar> ?o }") def test_unbracketed_optional(self): self.check_bad("select * { ?s <foo>? ?o }") def test_bnode_query(self): self.check_good("select * { ?s <foo> [ <bar> ?x ; <baz> ?y ] }", "SELECT table2.object, table1.object, table0.subject " "FROM triples AS table0 " "JOIN triples AS table1 " "ON table0.oid=table1.sid " "JOIN triples AS table2 " "ON table1.sid=table2.sid " "WHERE table0.property=\"<foo>\" " "AND table1.property=\"<bar>\" " "AND table2.property=\"<baz>\"") def test_non_blank_join(self): self.check_bad("select * { ?s <foo> ?o . ?o <bar> ?x }") def test_left_join(self): self.check_bad("select * { ?s <foo> ?o ; (<bar>?) ?o }") def test_optional2(self): self.check_bad("select * { ?s <baz> ?o ; <foo>|(<bar>?) ?o }") def test_optional3(self): self.check_bad("select * { ?s <baz> ?o ; ((<foo>|<bar>)?) ?o }") def test_zero_or_more(self): self.check_bad("select * { ?s <foo>* ?o }") def test_one_or_more(self): self.check_bad("select * { ?s <foo>+ ?o }") def test_double_optional(self): self.check_bad("select * { ?s (<foo>?) ?o ; (<bar>?) ?o }") def test_prefix(self): self.check_good("prefix dc: <http://purl.org/dc/elements/1.1/>" "select * { ?s dc:language ?l }", "SELECT table0.subject, table0.object " "FROM triples AS table0 " "WHERE table0.property=\"<http://purl.org/dc/" "elements/1.1/language>\"") def test_prefix2(self): self.check_good("PREFIX dc : <http://purl.org/dc/elements/1.1/> " "PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>\n" "select ?s { ?s dc:language \"english\"^^xsd:string ; " " dc:type dc:media }", "SELECT table1.subject " "FROM triples AS table0 " "JOIN triples AS table1 " "ON table0.sid=table1.sid " "WHERE table0.property=\"<http://purl.org/dc/" "elements/1.1/language>\" " "AND table0.object=\"\"\"english\"\"^^<http://" "www.w3.org/2001/XMLSchema#string>\" " "AND table1.property=\"<http://purl.org/dc/" "elements/1.1/type>\" " "AND table1.object=\"<http://purl.org/dc/" "elements/1.1/media>\"") def test_prefix3(self): self.check_good("PREFIX : <http://www.example.org/> " "select * { :s :p ?o }", "SELECT table0.object " "FROM triples AS table0 " "WHERE table0.property=\"<http://www.example.org/p>\" " "AND table0.subject=\"<http://www.example.org/s>\"") def test_pns(self): self.check_good("PREFIX b-.c: <foo> " "select ?s { ?s b-.c:foo._x ?o }", "SELECT table0.subject " "FROM triples AS table0 " "WHERE table0.property=\"<foofoo._x>\"") def test_distinct(self): self.check_good("select distinct ?s { ?s <foo> ?o }", "SELECT DISTINCT table0.subject " "FROM triples AS table0 " "WHERE table0.property=\"<foo>\"") def test_count_mode(self): self.check_good("select (count(*) as ?count) ?o where " "{ ?s <foo> ?o }", "SELECT COUNT(*), table0.object " "FROM triples AS table0 " "WHERE table0.property=\"<foo>\" " "GROUP BY table0.object") def test_count_mode2(self): self.check_good("select (count(*) as ?count) where { ?s <foo> ?o }", "SELECT COUNT(*) FROM triples AS table0 " "WHERE table0.property=\"<foo>\"") def test_count_mode3(self): self.check_good("select (count(*) as ?count) ?o where { ?s <foo> ?o }" "group by ?o", "SELECT COUNT(*), table0.object " "FROM triples AS table0 " "WHERE table0.property=\"<foo>\" " "GROUP BY table0.object") def test_prefix_lookup(self): self.check_good("select ?s where { ?s rdfs:label ?o }", "SELECT table0.subject " "FROM triples AS table0 " "WHERE table0.property=\"<http://www.w3.org/2000/01/" "rdf-schema#label>\"") ## Non-SPARQL extensions def test_optional_clause(self): self.check_good("select ?s { ?s <foo> ?o ; (<bar> ?o2) }", "SELECT table1.subject " "FROM triples AS table0 " "LEFT JOIN triples AS table1 " "ON table0.sid=table1.sid " "WHERE table0.property=\"<foo>\" " "AND table1.property=\"<bar>\"") def test_alternatives(self): self.check_good("select ?s { ?s <foo> \"bar\" | <baz> \"bing\" }", "SELECT table0.subject " "FROM triples AS table0 " "WHERE (table0.property=\"<foo>\" " "AND table0.object=\"\"\"bar\"\"\" " "OR table0.property=\"<baz>\" " "AND table0.object=\"\"\"bing\"\"\")") def test_optional_alternatives(self): self.check_good("select ?s { ?s <foo> \"x\" ; (<foo> \"bar\" |" "<baz> \"bing\") }", "SELECT table1.subject " "FROM triples AS table0 " "LEFT JOIN triples AS table1 " "ON table0.sid=table1.sid " "WHERE table0.property=\"<foo>\" " "AND table0.object=\"\"\"x\"\"\" " "AND (table1.property=\"<foo>\" " "AND table1.object=\"\"\"bar\"\"\" " "OR table1.property=\"<baz>\" " "AND table1.object=\"\"\"bing\"\"\")") def test_optional_harmony(self): self.check_bad("select ?s { ?s <foo> \"x\" | <bar> ?s }")
def sparql_query(self, q, mime_type, default_graph_uri, timeout): """Execute a SPARQL query @param query The query string @param mime_type The requested MIME type @param default_graph_uri The default graph URI @param start_response The response object @param timeout The timeout (in seconds) on the query """ try: syntax = YuzuQLSyntax() try: select = syntax.parse(q, { PREFIX1_QN: FullURI("<%s>" % PREFIX1_URI), PREFIX2_QN: FullURI("<%s>" % PREFIX2_URI), PREFIX3_QN: FullURI("<%s>" % PREFIX3_URI), PREFIX4_QN: FullURI("<%s>" % PREFIX4_URI), PREFIX5_QN: FullURI("<%s>" % PREFIX5_URI), PREFIX6_QN: FullURI("<%s>" % PREFIX6_URI), PREFIX7_QN: FullURI("<%s>" % PREFIX7_URI), PREFIX8_QN: FullURI("<%s>" % PREFIX8_URI), PREFIX9_QN: FullURI("<%s>" % PREFIX9_URI), "rdf": FullURI("<http://www.w3.org/1999/02/22-" "rdf-syntax-ns#>"), "rdfs": FullURI("<http://www.w3.org/2000/01/rdf-schema#>"), "owl": FullURI("<http://www.w3.org/2002/07/owl#>"), "dc": FullURI("<http://purl.org/dc/elements/1.1/>"), "dct": FullURI("<http://purl.org/dc/terms>"), "xsd": FullURI("<http://www.w3.org/2001/XMLSchema#>")}) except YuzuQLError as e: return False, 'error', e.value if select.limit < 0 or (select.limit >= YUZUQL_LIMIT and YUZUQL_LIMIT >= 0): return False, 'error', YZ_QUERY_LIMIT_EXCEEDED % YUZUQL_LIMIT qb = QueryBuilder(select) sql_query = qb.build() conn = sqlite3.connect(self.db) cursor = conn.cursor() cursor.execute(sql_query) vars = qb.vars() if mime_type == "sparql-json": results = sql_results_to_sparql_json(cursor.fetchall(), vars) else: results = sql_results_to_sparql_xml(cursor.fetchall(), vars) conn.close() return False, 'sparql', results except Exception as e: if SPARQL_ENDPOINT: graph = Graph('SPARQLStore') try: graph.open(SPARQL_ENDPOINT) try: if default_graph_uri: qres = graph.query(q, initNs=default_graph_uri) else: qres = graph.query(q) except Exception as e: traceback.print_exc() print(e) return False, 'error', YZ_BAD_REQUEST if qres.type == "CONSTRUCT" or qres.type == "DESCRIBE": if mime_type == "html" or mime_type == "json-ld": mime_type == "pretty-xml" return (False, mime_type, qres.serialize(format=mime_type)) elif (self.mime_type == 'sparql' or self.mime_type == 'sparql-json' or self.mime_type == 'html'): return False, 'sparql', qres.serialize() else: return False, 'error', YZ_BAD_MIME finally: graph.close() else: traceback.print_exc() print(e) return False, 'error', ""