summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Garnacho <carlosg@gnome.org>2018-09-03 18:11:04 +0200
committerCarlos Garnacho <carlosg@gnome.org>2018-11-13 11:17:31 +0100
commit40054c464174ae3de5d372092d780e3e31460884 (patch)
tree16f55ce4063d8759c4b4e23de877c88d6c83ccd5
parenta46f5e29503dbd6feceedefd545433bebd32751d (diff)
downloadtracker-40054c464174ae3de5d372092d780e3e31460884.tar.gz
libtracker-data: Drop old SPARQL parser
-rw-r--r--src/libtracker-data/meson.build3
-rw-r--r--src/libtracker-data/tracker-sparql-expression.vala1727
-rw-r--r--src/libtracker-data/tracker-sparql-pattern.vala1659
-rw-r--r--src/libtracker-data/tracker-sparql-query.vala1102
4 files changed, 0 insertions, 4491 deletions
diff --git a/src/libtracker-data/meson.build b/src/libtracker-data/meson.build
index 9eaef5bf1..18f1a481e 100644
--- a/src/libtracker-data/meson.build
+++ b/src/libtracker-data/meson.build
@@ -3,9 +3,6 @@
# libtracker-sparql-query.
libtracker_data_vala = static_library('tracker-sparql-query',
'tracker-vala-namespace.vala',
- 'tracker-sparql-query.vala',
- 'tracker-sparql-expression.vala',
- 'tracker-sparql-pattern.vala',
'tracker-sparql-scanner.vala',
'tracker-turtle-reader.vala',
'../libtracker-common/libtracker-common.vapi',
diff --git a/src/libtracker-data/tracker-sparql-expression.vala b/src/libtracker-data/tracker-sparql-expression.vala
deleted file mode 100644
index ed2a2c74a..000000000
--- a/src/libtracker-data/tracker-sparql-expression.vala
+++ /dev/null
@@ -1,1727 +0,0 @@
-/*
- * Copyright (C) 2008-2010, Nokia
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-class Tracker.Sparql.Expression : Object {
- weak Query query;
-
- const int MAX_VARIABLES_FOR_IN = 20;
- const string XSD_NS = "http://www.w3.org/2001/XMLSchema#";
- const string FN_NS = "http://www.w3.org/2005/xpath-functions#";
- const string FTS_NS = "http://www.tracker-project.org/ontologies/fts#";
- const string TRACKER_NS = "http://www.tracker-project.org/ontologies/tracker#";
-
- enum TimeFormatType {
- SECONDS,
- MINUTES,
- HOURS
- }
-
- string? fts_sql;
-
- Data.Manager manager;
-
- public Expression (Query query) {
- this.query = query;
- this.manager = query.manager;
- }
-
- Context context {
- get { return query.context; }
- }
-
- Pattern pattern {
- get { return query.pattern; }
- }
-
- private inline bool next () throws Sparql.Error {
- return query.next ();
- }
-
- private inline SparqlTokenType current () {
- return query.current ();
- }
-
- private inline SparqlTokenType last () {
- return query.last ();
- }
-
- private inline bool accept (SparqlTokenType type) throws Sparql.Error {
- return query.accept (type);
- }
-
- private Sparql.Error get_error (string msg) {
- return query.get_error (msg);
- }
-
- private bool expect (SparqlTokenType type) throws Sparql.Error {
- return query.expect (type);
- }
-
- private string get_last_string (int strip = 0) {
- return query.get_last_string (strip);
- }
-
- private string escape_sql_string_literal (string literal) {
- return "'%s'".printf (string.joinv ("''", literal.split ("'")));
- }
-
- private bool maybe_numeric (PropertyType type) {
- return (type == PropertyType.INTEGER || type == PropertyType.DOUBLE || type == PropertyType.DATE || type == PropertyType.DATETIME || type == PropertyType.UNKNOWN);
- }
-
- private void append_collate (StringBuilder sql) {
- sql.append_printf (" COLLATE %s", COLLATION_NAME);
- }
-
- private void skip_bracketted_expression () throws Sparql.Error {
- expect (SparqlTokenType.OPEN_PARENS);
- while (true) {
- switch (current ()) {
- case SparqlTokenType.OPEN_PARENS:
- // skip nested bracketted expression
- skip_bracketted_expression ();
- continue;
- case SparqlTokenType.CLOSE_PARENS:
- case SparqlTokenType.EOF:
- break;
- default:
- next ();
- continue;
- }
- break;
- }
- expect (SparqlTokenType.CLOSE_PARENS);
- }
-
- internal void skip_select_variables () throws Sparql.Error {
- while (true) {
- switch (current ()) {
- case SparqlTokenType.OPEN_PARENS:
- skip_bracketted_expression ();
- continue;
- case SparqlTokenType.FROM:
- case SparqlTokenType.WHERE:
- case SparqlTokenType.OPEN_BRACE:
- case SparqlTokenType.GROUP:
- case SparqlTokenType.ORDER:
- case SparqlTokenType.LIMIT:
- case SparqlTokenType.OFFSET:
- case SparqlTokenType.EOF:
- break;
- default:
- next ();
- continue;
- }
- break;
- }
- }
-
- internal PropertyType translate_select_expression (StringBuilder sql, bool subquery, int variable_index) throws Sparql.Error {
- Variable variable = null;
- bool expect_close_parens = false;
- bool as_handled = false;
-
- long begin = sql.len;
- var type = PropertyType.UNKNOWN;
- if (current () == SparqlTokenType.VAR) {
- type = translate_expression (sql);
- // we need variable name in case of compositional subqueries
- variable = context.get_variable (get_last_string ().substring (1));
-
- if (variable.binding == null) {
- throw get_error ("use of undefined variable `%s'".printf (variable.name));
- }
- } else if (accept (SparqlTokenType.OPEN_PARENS)) {
- if (current () == SparqlTokenType.SELECT) {
- // no parenthesis around expression
- // deprecated but supported for backward compatibility
- sql.append ("(");
- var select_context = pattern.translate_select (sql, true, true);
- sql.append (")");
-
- expect (SparqlTokenType.CLOSE_PARENS);
- type = select_context.type;
- } else {
- type = translate_expression (sql);
- if (accept (SparqlTokenType.CLOSE_PARENS)) {
- // missing AS
- // deprecated but supported for backward compatibility
- } else {
- // syntax from SPARQL 1.1 Draft
- // (Expression AS Var)
- expect_close_parens = true;
- }
- }
- } else {
- // no parenthesis around expression
- // deprecated but supported for backward compatibility
- type = translate_expression (sql);
- }
-
- if (!subquery) {
- convert_expression_to_string (sql, type, begin);
- }
-
- if (accept (SparqlTokenType.AS)) {
- // (...) AS ?foo
- expect (SparqlTokenType.VAR);
- variable = context.get_variable (get_last_string ().substring (1));
- sql.append_printf (" AS %s", variable.sql_expression);
- as_handled = true;
-
- if (subquery) {
- var binding = new VariableBinding ();
- binding.data_type = type;
- binding.variable = variable;
- binding.sql_expression = variable.sql_expression;
- pattern.add_variable_binding (new StringBuilder (), binding, VariableState.BOUND);
- }
- }
-
- if (pattern.fts_subject != null) {
- if (variable == null) {
- // FTS matches still need aliases as the outer MATCH query
- // will fetch futher values from the joined select
- variable = context.get_variable ("var%d".printf (variable_index + 1));
- }
-
- if (fts_sql == null) {
- pattern.fts_variables += variable.sql_expression;
-
- if (as_handled == false) {
- sql.append_printf (" AS %s", variable.sql_expression);
- }
- } else {
- pattern.fts_variables += fts_sql;
- pattern.queries_fts_data = true;
- }
- }
-
- if (expect_close_parens) {
- expect (SparqlTokenType.CLOSE_PARENS);
- }
-
- if (variable != null) {
- int state = context.var_set.lookup (variable);
- if (state == 0) {
- state = VariableState.BOUND;
- }
- context.select_var_set.insert (variable, state);
-
- ((SelectContext) context).variable_names += variable.name;
- } else {
- ((SelectContext) context).variable_names += "var%d".printf (variable_index + 1);
- }
-
- fts_sql = null;
-
- return type;
- }
-
- private void translate_expression_as_order_condition (StringBuilder sql) throws Sparql.Error {
- long begin = sql.len;
- if (translate_expression (sql) == PropertyType.RESOURCE) {
- // ID => Uri
- sql.insert (begin, "(SELECT Uri FROM Resource WHERE ID = ");
- sql.append (")");
- }
- }
-
- internal void translate_order_condition (StringBuilder sql) throws Sparql.Error {
- if (accept (SparqlTokenType.ASC)) {
- translate_expression_as_order_condition (sql);
- sql.append (" ASC");
- } else if (accept (SparqlTokenType.DESC)) {
- translate_expression_as_order_condition (sql);
- sql.append (" DESC");
- } else {
- translate_expression_as_order_condition (sql);
- }
- }
-
- private void translate_bound_call (StringBuilder sql) throws Sparql.Error {
- expect (SparqlTokenType.BOUND);
- expect (SparqlTokenType.OPEN_PARENS);
- sql.append ("(");
- translate_expression (sql);
- sql.append (" IS NOT NULL)");
- expect (SparqlTokenType.CLOSE_PARENS);
- }
-
- private PropertyType translate_if_call (StringBuilder sql) throws Sparql.Error {
- expect (SparqlTokenType.IF);
- expect (SparqlTokenType.OPEN_PARENS);
-
- // condition
- sql.append ("(CASE ");
- translate_expression (sql);
-
- // if condition is true
- sql.append (" WHEN 1 THEN ");
- expect (SparqlTokenType.COMMA);
- var type = translate_expression (sql);
-
- // if condition is false
- sql.append (" WHEN 0 THEN ");
- expect (SparqlTokenType.COMMA);
- translate_expression (sql);
-
- sql.append (" ELSE NULL END)");
-
- expect (SparqlTokenType.CLOSE_PARENS);
-
- return type;
- }
-
- private void translate_regex (StringBuilder sql) throws Sparql.Error {
- expect (SparqlTokenType.REGEX);
- expect (SparqlTokenType.OPEN_PARENS);
- sql.append ("SparqlRegex(");
- translate_expression_as_string (sql);
- sql.append (", ");
- expect (SparqlTokenType.COMMA);
- // SQLite's sqlite3_set_auxdata doesn't work correctly with bound
- // strings for the regex in function_sparql_regex.
- // translate_expression (sql);
- sql.append (escape_sql_string_literal (parse_string_literal ()));
- sql.append (", ");
- if (accept (SparqlTokenType.COMMA)) {
- // Same as above
- // translate_expression (sql);
- sql.append (escape_sql_string_literal (parse_string_literal ()));
- } else {
- sql.append ("''");
- }
- sql.append (")");
- expect (SparqlTokenType.CLOSE_PARENS);
- }
-
- private void translate_exists (StringBuilder sql) throws Sparql.Error {
- sql.append ("(");
- pattern.translate_exists (sql);
- sql.append (")");
- }
-
- internal static void append_expression_as_string (StringBuilder sql, string expression, PropertyType type) {
- long begin = sql.len;
- sql.append (expression);
- convert_expression_to_string (sql, type, begin);
- }
-
- private static void convert_expression_to_string (StringBuilder sql, PropertyType type, long begin) {
- switch (type) {
- case PropertyType.STRING:
- case PropertyType.INTEGER:
- // nothing to convert
- // do not use CAST to convert integers to strings as this breaks use
- // of index when sorting by variable introduced in select expression
- break;
- case PropertyType.RESOURCE:
- // ID => Uri
- sql.insert (begin, "(SELECT Uri FROM Resource WHERE ID = ");
- sql.append (")");
- break;
- case PropertyType.BOOLEAN:
- // 0/1 => false/true
- sql.insert (begin, "CASE ");
- sql.append (" WHEN 1 THEN 'true' WHEN 0 THEN 'false' ELSE NULL END");
- break;
- case PropertyType.DATE:
- // ISO 8601 format
- sql.insert (begin, "strftime (\"%Y-%m-%d\", ");
- sql.append (", \"unixepoch\")");
- break;
- case PropertyType.DATETIME:
- // ISO 8601 format
- sql.insert (begin, "SparqlFormatTime (");
- sql.append (")");
- break;
- default:
- // let sqlite convert the expression to string
- sql.insert (begin, "CAST (");
- sql.append (" AS TEXT)");
- break;
- }
- }
-
- private void translate_expression_as_string (StringBuilder sql) throws Sparql.Error {
- switch (current ()) {
- case SparqlTokenType.IRI_REF:
- case SparqlTokenType.PN_PREFIX:
- case SparqlTokenType.COLON:
- // handle IRI literals separately as it wouldn't work for unknown IRIs otherwise
- var binding = new LiteralBinding ();
- bool is_var;
- binding.literal = pattern.parse_var_or_term (null, out is_var);
- if (accept (SparqlTokenType.OPEN_PARENS)) {
- // function call
- long begin = sql.len;
- var type = translate_function (sql, binding.literal);
- expect (SparqlTokenType.CLOSE_PARENS);
- convert_expression_to_string (sql, type, begin);
- } else {
- sql.append ("?");
- query.bindings.append (binding);
- }
- break;
- default:
- long begin = sql.len;
- var type = translate_expression (sql);
- convert_expression_to_string (sql, type, begin);
- break;
- }
- }
-
- private void translate_str (StringBuilder sql) throws Sparql.Error {
- expect (SparqlTokenType.STR);
- expect (SparqlTokenType.OPEN_PARENS);
-
- translate_expression_as_string (sql);
-
- expect (SparqlTokenType.CLOSE_PARENS);
- }
-
- private void translate_isuri (StringBuilder sql) throws Sparql.Error {
- if (!accept (SparqlTokenType.ISURI)) {
- expect (SparqlTokenType.ISIRI);
- }
-
- expect (SparqlTokenType.OPEN_PARENS);
-
- sql.append ("?");
- var new_binding = new LiteralBinding ();
- new_binding.data_type = PropertyType.INTEGER;
-
- if (current() == SparqlTokenType.IRI_REF) {
- new_binding.literal = "1";
- next ();
- } else if (translate_expression (new StringBuilder ()) == PropertyType.RESOURCE) {
- new_binding.literal = "1";
- } else {
- new_binding.literal = "0";
- }
-
- query.bindings.append (new_binding);
-
- expect (SparqlTokenType.CLOSE_PARENS);
- }
-
- private void translate_datatype (StringBuilder sql) throws Sparql.Error {
- expect (SparqlTokenType.DATATYPE);
- expect (SparqlTokenType.OPEN_PARENS);
-
- if (accept (SparqlTokenType.VAR)) {
- string variable_name = get_last_string().substring(1);
- var variable = context.get_variable (variable_name);
-
- if (variable.binding == null) {
- throw get_error ("`%s' is not a valid variable".printf (variable.name));
- }
-
- if (variable.binding.data_type == PropertyType.RESOURCE || variable.binding.type == null) {
- throw get_error ("Invalid FILTER");
- }
-
- sql.append ("(SELECT ID FROM Resource WHERE Uri = ?)");
-
- var new_binding = new LiteralBinding ();
- new_binding.literal = variable.binding.type.uri;
- query.bindings.append (new_binding);
-
- } else {
- throw get_error ("Invalid FILTER");
- }
-
- expect (SparqlTokenType.CLOSE_PARENS);
- }
-
- private void translate_date (StringBuilder sql, string format) throws Sparql.Error {
- sql.append_printf ("strftime (\"%s\", ", format);
-
- if (accept (SparqlTokenType.VAR)) {
- string variable_name = get_last_string ().substring (1);
- var variable = context.get_variable (variable_name);
- sql.append (variable.get_extra_sql_expression ("localDate"));
- sql.append (" * 24 * 3600");
- } else {
- translate_primary_expression (sql);
- }
-
- sql.append (", \"unixepoch\")");
- }
-
- private void translate_time (StringBuilder sql, TimeFormatType type) throws Sparql.Error {
- sql.append ("(");
- if (accept (SparqlTokenType.VAR)) {
- string variable_name = get_last_string ().substring (1);
- var variable = context.get_variable (variable_name);
- sql.append (variable.get_extra_sql_expression ("localTime"));
- } else {
- translate_primary_expression (sql);
- }
-
- switch (type) {
- case TimeFormatType.SECONDS:
- sql.append ("% 60");
- break;
- case TimeFormatType.MINUTES:
- sql.append (" / 60 % 60");
- break;
- case TimeFormatType.HOURS:
- sql.append (" / 3600 % 24");
- break;
- }
- sql.append (")");
- }
-
- private PropertyType translate_function (StringBuilder sql, string uri) throws Sparql.Error {
- if (uri == XSD_NS + "string") {
- // conversion to string
- translate_expression_as_string (sql);
-
- return PropertyType.STRING;
- } else if (uri == XSD_NS + "integer") {
- // conversion to integer
- sql.append ("CAST (");
- translate_expression_as_string (sql);
- sql.append (" AS INTEGER)");
-
- return PropertyType.INTEGER;
- } else if (uri == XSD_NS + "double") {
- // conversion to double
- sql.append ("CAST (");
- translate_expression_as_string (sql);
- sql.append (" AS REAL)");
-
- return PropertyType.DOUBLE;
- } else if (uri == TRACKER_NS + "case-fold") {
- // conversion to string
- sql.append ("SparqlCaseFold (");
- translate_expression_as_string (sql);
- sql.append (")");
- return PropertyType.STRING;
- } else if (uri == TRACKER_NS + "ascii-lower-case") {
- // conversion to string
- sql.append ("lower (");
- translate_expression_as_string (sql);
- sql.append (")");
- return PropertyType.STRING;
- } else if (uri == TRACKER_NS + "title-order") {
- translate_expression_as_string (sql);
- sql.append_printf (" COLLATE %s", TITLE_COLLATION_NAME);
- return PropertyType.STRING;
- } else if (uri == FN_NS + "lower-case") {
- // conversion to string
- sql.append ("SparqlLowerCase (");
- translate_expression_as_string (sql);
- sql.append (")");
- return PropertyType.STRING;
- } else if (uri == FN_NS + "upper-case") {
- sql.append ("SparqlUpperCase (");
- translate_expression_as_string (sql);
- sql.append (")");
- return PropertyType.STRING;
- } else if (uri == TRACKER_NS + "normalize") {
- // conversion to string
- sql.append ("SparqlNormalize (");
- translate_expression_as_string (sql);
- sql.append (", ");
- expect (SparqlTokenType.COMMA);
- translate_expression_as_string (sql);
- sql.append (")");
- return PropertyType.STRING;
- } else if (uri == TRACKER_NS + "unaccent") {
- // conversion to string
- sql.append ("SparqlUnaccent (");
- translate_expression_as_string (sql);
- sql.append (")");
- return PropertyType.STRING;
- } else if (uri == FN_NS + "contains") {
- // fn:contains('A','B') => 'A' GLOB '*B*'
- sql.append ("(");
- translate_expression_as_string (sql);
- sql.append (" GLOB ");
- expect (SparqlTokenType.COMMA);
-
- sql.append ("?");
- var binding = new LiteralBinding ();
- binding.literal = "*%s*".printf (parse_string_literal ());
- query.bindings.append (binding);
-
- sql.append (")");
-
- return PropertyType.BOOLEAN;
- } else if (uri == FN_NS + "starts-with") {
- // fn:starts-with('A','B') => 'A' BETWEEN 'B' AND 'B\u0010fffd'
- // 0010fffd always sorts last
-
- translate_expression_as_string (sql);
- sql.append (" BETWEEN ");
-
- expect (SparqlTokenType.COMMA);
- string prefix = parse_string_literal ();
-
- sql.append ("?");
- var binding = new LiteralBinding ();
- binding.literal = prefix;
- query.bindings.append (binding);
-
- sql.append (" AND ");
-
- sql.append ("?");
- binding = new LiteralBinding ();
- binding.literal = prefix + COLLATION_LAST_CHAR.to_string ();
- query.bindings.append (binding);
-
- return PropertyType.BOOLEAN;
- } else if (uri == FN_NS + "ends-with") {
- // fn:ends-with('A','B') => 'A' GLOB '*B'
- sql.append ("(");
- translate_expression_as_string (sql);
- sql.append (" GLOB ");
- expect (SparqlTokenType.COMMA);
-
- sql.append ("?");
- var binding = new LiteralBinding ();
- binding.literal = "*%s".printf (parse_string_literal ());
- query.bindings.append (binding);
-
- sql.append (")");
-
- return PropertyType.BOOLEAN;
- } else if (uri == FN_NS + "substring") {
- sql.append ("substr(");
- translate_expression_as_string (sql);
-
- sql.append (", ");
- expect (SparqlTokenType.COMMA);
- translate_expression_as_string (sql);
-
- if (accept (SparqlTokenType.COMMA)) {
- sql.append (", ");
- translate_expression_as_string (sql);
- }
-
- sql.append (")");
-
- return PropertyType.STRING;
- } else if (uri == FN_NS + "concat") {
- translate_expression_as_string (sql);
- sql.append ("||");
- expect (SparqlTokenType.COMMA);
- translate_expression_as_string (sql);
- while (accept (SparqlTokenType.COMMA)) {
- sql.append ("||");
- translate_expression_as_string (sql);
- }
-
- return PropertyType.STRING;
- } else if (uri == FN_NS + "string-join") {
- sql.append ("SparqlStringJoin(");
- expect (SparqlTokenType.OPEN_PARENS);
-
- translate_expression_as_string (sql);
- sql.append (", ");
- expect (SparqlTokenType.COMMA);
- translate_expression_as_string (sql);
- while (accept (SparqlTokenType.COMMA)) {
- sql.append (", ");
- translate_expression_as_string (sql);
- }
-
- expect (SparqlTokenType.CLOSE_PARENS);
- sql.append (",");
- expect (SparqlTokenType.COMMA);
- translate_expression (sql);
- sql.append (")");
-
- return PropertyType.STRING;
- } else if (uri == FN_NS + "year-from-dateTime") {
- translate_date (sql, "%Y");
- return PropertyType.INTEGER;
- } else if (uri == FN_NS + "month-from-dateTime") {
- translate_date (sql, "%m");
- return PropertyType.INTEGER;
- } else if (uri == FN_NS + "day-from-dateTime") {
- translate_date (sql, "%d");
- return PropertyType.INTEGER;
- } else if (uri == FN_NS + "hours-from-dateTime") {
- translate_time (sql, TimeFormatType.HOURS);
- return PropertyType.INTEGER;
- } else if (uri == FN_NS + "minutes-from-dateTime") {
- translate_time (sql, TimeFormatType.MINUTES);
- return PropertyType.INTEGER;
- } else if (uri == FN_NS + "seconds-from-dateTime") {
- translate_time (sql, TimeFormatType.SECONDS);
- return PropertyType.INTEGER;
- } else if (uri == FN_NS + "timezone-from-dateTime") {
- expect (SparqlTokenType.VAR);
- string variable_name = get_last_string ().substring (1);
- var variable = context.get_variable (variable_name);
-
- sql.append ("(");
- sql.append (variable.get_extra_sql_expression ("localDate"));
- sql.append (" * 24 * 3600 + ");
- sql.append (variable.get_extra_sql_expression ("localTime"));
- sql.append ("- ");
- sql.append ("CAST (");
- sql.append (variable.sql_expression);
- sql.append (" AS INTEGER)");
- sql.append (")");
-
- return PropertyType.INTEGER;
- } else if (uri == FN_NS + "replace") {
- sql.append ("SparqlReplace(");
- translate_expression_as_string (sql);
- sql.append (", ");
-
- expect (SparqlTokenType.COMMA);
- translate_expression_as_string (sql);
- sql.append (", ");
-
- expect (SparqlTokenType.COMMA);
- translate_expression_as_string (sql);
-
- if (accept (SparqlTokenType.COMMA)) {
- sql.append (", ");
- sql.append (escape_sql_string_literal (parse_string_literal ()));
- }
- sql.append (")");
- return PropertyType.STRING;
- } else if (uri == FTS_NS + "rank") {
- bool is_var;
- string v = pattern.parse_var_or_term (null, out is_var);
- sql.append_printf ("\"%s_u_rank\"", v);
-
- return PropertyType.DOUBLE;
- } else if (uri == FTS_NS + "offsets") {
- bool is_var;
- string v = pattern.parse_var_or_term (null, out is_var);
- var variable = context.get_variable (v);
-
- sql.append (variable.sql_expression);
- fts_sql = "tracker_offsets(\"fts5\")";
- return PropertyType.STRING;
- } else if (uri == FTS_NS + "snippet") {
- bool is_var;
-
- string v = pattern.parse_var_or_term (null, out is_var);
- var variable = context.get_variable (v);
- var fts = new StringBuilder ();
-
- fts.append_printf ("snippet(\"fts5\"");
-
- // lookup column
- fts.append (", -1");
-
- // "start match" text
- if (accept (SparqlTokenType.COMMA)) {
- fts.append (", ");
- translate_expression_as_string (fts);
-
- // "end match" text
- expect (SparqlTokenType.COMMA);
- fts.append (", ");
- translate_expression_as_string (fts);
- } else {
- fts.append(",'',''");
- }
-
- // "ellipsis" text
- if (accept (SparqlTokenType.COMMA)) {
- fts.append (", ");
- translate_expression_as_string (fts);
- } else {
- fts.append (", '...'");
- }
-
- // Approximate number of words in context
- if (accept (SparqlTokenType.COMMA)) {
- fts.append (", ");
- translate_expression_as_string (fts);
- } else {
- fts.append (", 5");
- }
-
- fts.append (")");
-
- fts_sql = fts.str;
- sql.append (variable.sql_expression);
- return PropertyType.STRING;
- } else if (uri == TRACKER_NS + "id") {
- var type = translate_expression (sql);
- if (type != PropertyType.RESOURCE) {
- throw get_error ("expected resource");
- }
-
- return PropertyType.INTEGER;
- } else if (uri == TRACKER_NS + "uri") {
- var type = translate_expression (sql);
- if (type != PropertyType.INTEGER) {
- throw get_error ("expected integer ID");
- }
-
- return PropertyType.RESOURCE;
- } else if (uri == TRACKER_NS + "cartesian-distance") {
- sql.append ("SparqlCartesianDistance(");
- translate_expression (sql);
- sql.append (", ");
- expect (SparqlTokenType.COMMA);
- translate_expression (sql);
- sql.append (", ");
- expect (SparqlTokenType.COMMA);
- translate_expression (sql);
- sql.append (", ");
- expect (SparqlTokenType.COMMA);
- translate_expression (sql);
- sql.append (")");
-
- return PropertyType.DOUBLE;
- } else if (uri == TRACKER_NS + "haversine-distance") {
- sql.append ("SparqlHaversineDistance(");
- translate_expression (sql);
- sql.append (", ");
- expect (SparqlTokenType.COMMA);
- translate_expression (sql);
- sql.append (", ");
- expect (SparqlTokenType.COMMA);
- translate_expression (sql);
- sql.append (", ");
- expect (SparqlTokenType.COMMA);
- translate_expression (sql);
- sql.append (")");
-
- return PropertyType.DOUBLE;
- } else if (uri == TRACKER_NS + "coalesce") {
- sql.append ("COALESCE(");
- translate_expression_as_string (sql);
- sql.append (", ");
- expect (SparqlTokenType.COMMA);
- translate_expression_as_string (sql);
- while (accept (SparqlTokenType.COMMA)) {
- sql.append (", ");
- translate_expression_as_string (sql);
- }
- sql.append (")");
-
- return PropertyType.STRING;
- } else if (uri == TRACKER_NS + "uri-is-parent") {
- sql.append ("SparqlUriIsParent(");
- translate_expression_as_string (sql);
- sql.append (", ");
- expect (SparqlTokenType.COMMA);
-
- translate_expression_as_string (sql);
- sql.append (")");
-
- return PropertyType.BOOLEAN;
- } else if (uri == TRACKER_NS + "uri-is-descendant") {
- sql.append ("SparqlUriIsDescendant(");
- translate_expression_as_string (sql);
- sql.append (", ");
- expect (SparqlTokenType.COMMA);
-
- translate_expression_as_string (sql);
- while (accept (SparqlTokenType.COMMA)) {
- sql.append (", ");
- translate_expression_as_string (sql);
- }
- sql.append (")");
-
- return PropertyType.BOOLEAN;
- } else if (uri == TRACKER_NS + "string-from-filename") {
- sql.append ("SparqlStringFromFilename(");
- translate_expression_as_string (sql);
- sql.append (")");
-
- return PropertyType.STRING;
- } else {
- // support properties as functions
- var ontologies = manager.get_ontologies ();
- var prop = ontologies.get_property_by_uri (uri);
- if (prop == null) {
- throw get_error ("Unknown function");
- }
-
- var expr = new StringBuilder ();
- translate_expression (expr);
-
- string value_separator = ",";
- string? graph_separator = null;
-
- if (accept (SparqlTokenType.COMMA)) {
- value_separator = parse_string_literal ();
-
- if (accept (SparqlTokenType.COMMA)) {
- graph_separator = parse_string_literal ();
- }
- }
-
- if (prop.multiple_values) {
- // multi-valued property
- sql.append ("(SELECT GROUP_CONCAT(");
- long begin = sql.len;
- sql.append_printf ("\"%s\"", prop.name);
- convert_expression_to_string (sql, prop.data_type, begin);
- if (graph_separator != null) {
- sql.append_printf (" || %s || COALESCE((SELECT Uri FROM Resource WHERE ID = \"%s:graph\"), '')", escape_sql_string_literal (graph_separator), prop.name);
- }
- sql.append_printf (",%s)", escape_sql_string_literal (value_separator));
- sql.append_printf (" FROM \"%s\" WHERE ID = %s)", prop.table_name, expr.str);
-
- return PropertyType.STRING;
- } else {
- // single-valued property
- if (graph_separator == null) {
- sql.append_printf ("(SELECT \"%s\" FROM \"%s\" WHERE ID = %s)", prop.name, prop.table_name, expr.str);
-
- if (prop.data_type == PropertyType.STRING) {
- append_collate (sql);
- }
-
- return prop.data_type;
- } else {
- sql.append ("(SELECT ");
- long begin = sql.len;
- sql.append_printf ("\"%s\"", prop.name);
- convert_expression_to_string (sql, prop.data_type, begin);
- sql.append_printf (" || %s || COALESCE((SELECT Uri FROM Resource WHERE ID = \"%s:graph\"), '')", escape_sql_string_literal (graph_separator), prop.name);
- sql.append_printf (" FROM \"%s\" WHERE ID = %s)", prop.table_name, expr.str);
-
- return PropertyType.STRING;
- }
- }
- }
- }
-
- private PropertyType parse_type_uri () throws Sparql.Error {
- string type_iri;
- PropertyType type;
-
- if (accept (SparqlTokenType.IRI_REF)) {
- type_iri = get_last_string (1);
- } else if (accept (SparqlTokenType.PN_PREFIX)) {
- string ns = get_last_string ();
- expect (SparqlTokenType.COLON);
- type_iri = query.resolve_prefixed_name (ns, get_last_string ().substring (1));
- } else {
- expect (SparqlTokenType.COLON);
- type_iri = query.resolve_prefixed_name ("", get_last_string ().substring (1));
- }
-
- if (type_iri == XSD_NS + "boolean") {
- type = PropertyType.BOOLEAN;
- } else if (type_iri == XSD_NS + "integer" ||
- type_iri == XSD_NS + "nonPositiveInteger" ||
- type_iri == XSD_NS + "negativeInteger" ||
- type_iri == XSD_NS + "long" ||
- type_iri == XSD_NS + "int" ||
- type_iri == XSD_NS + "short" ||
- type_iri == XSD_NS + "byte" ||
- type_iri == XSD_NS + "nonNegativeInteger" ||
- type_iri == XSD_NS + "unsignedLong" ||
- type_iri == XSD_NS + "unsignedInt" ||
- type_iri == XSD_NS + "unsignedShort" ||
- type_iri == XSD_NS + "unsignedByte" ||
- type_iri == XSD_NS + "positiveInteger") {
- type = PropertyType.INTEGER;
- } else if (type_iri == XSD_NS + "double") {
- type = PropertyType.DOUBLE;
- } else if (type_iri == XSD_NS + "date") {
- type = PropertyType.DATE;
- } else if (type_iri == XSD_NS + "dateTime") {
- type = PropertyType.DATETIME;
- } else {
- type = PropertyType.STRING;
- }
-
- return type;
- }
-
- internal string parse_string_literal (out PropertyType type = null) throws Sparql.Error {
- type = PropertyType.STRING;
-
- next ();
- switch (last ()) {
- case SparqlTokenType.STRING_LITERAL1:
- case SparqlTokenType.STRING_LITERAL2:
- var sb = new StringBuilder ();
-
- string s = get_last_string (1);
- string* p = s;
- string* end = p + s.length;
- while ((long) p < (long) end) {
- string* q = Posix.strchr (p, '\\');
- if (q == null) {
- sb.append_len (p, (long) (end - p));
- p = end;
- } else {
- sb.append_len (p, (long) (q - p));
- p = q + 1;
- switch (((char*) p)[0]) {
- case '\'':
- case '"':
- case '\\':
- sb.append_c (((char*) p)[0]);
- break;
- case 'b':
- sb.append_c ('\b');
- break;
- case 'f':
- sb.append_c ('\f');
- break;
- case 'n':
- sb.append_c ('\n');
- break;
- case 'r':
- sb.append_c ('\r');
- break;
- case 't':
- sb.append_c ('\t');
- break;
- case 'u':
- char* ptr = (char*) p + 1;
- unichar c = (((unichar) ptr[0].xdigit_value () * 16 + ptr[1].xdigit_value ()) * 16 + ptr[2].xdigit_value ()) * 16 + ptr[3].xdigit_value ();
- sb.append_unichar (c);
- p += 4;
- break;
- }
- p++;
- }
- }
-
- if (accept (SparqlTokenType.DOUBLE_CIRCUMFLEX)) {
- // typed literal
- type = parse_type_uri ();
- }
-
- return sb.str;
- case SparqlTokenType.STRING_LITERAL_LONG1:
- case SparqlTokenType.STRING_LITERAL_LONG2:
- string result = get_last_string (3);
-
- if (accept (SparqlTokenType.DOUBLE_CIRCUMFLEX)) {
- // typed literal
- type = parse_type_uri ();
- }
-
- return result;
- default:
- throw get_error ("expected string literal");
- }
- }
-
- private PropertyType translate_uri_expression (StringBuilder sql, string uri) throws Sparql.Error {
- if (accept (SparqlTokenType.OPEN_PARENS)) {
- // function
- var result = translate_function (sql, uri);
- expect (SparqlTokenType.CLOSE_PARENS);
- return result;
- } else {
- // resource
- sql.append ("COALESCE((SELECT ID FROM Resource WHERE Uri = ?), 0)");
- var binding = new LiteralBinding ();
- binding.literal = uri;
- query.bindings.append (binding);
- return PropertyType.RESOURCE;
- }
- }
-
- private PropertyType translate_primary_expression (StringBuilder sql) throws Sparql.Error {
- PropertyType type;
-
- switch (current ()) {
- case SparqlTokenType.OPEN_PARENS:
- return translate_bracketted_expression (sql);
- case SparqlTokenType.IRI_REF:
- next ();
- return translate_uri_expression (sql, get_last_string (1));
- case SparqlTokenType.DECIMAL:
- case SparqlTokenType.DOUBLE:
- next ();
-
- if (query.no_cache) {
- sql.append (get_last_string ());
- } else {
- sql.append ("?");
-
- var binding = new LiteralBinding ();
- binding.literal = get_last_string ();
- query.bindings.append (binding);
- }
-
- return PropertyType.DOUBLE;
- case SparqlTokenType.TRUE:
- next ();
-
- if (query.no_cache) {
- sql.append ("1");
- } else {
- sql.append ("?");
-
- var binding = new LiteralBinding ();
- binding.literal = "1";
- binding.data_type = PropertyType.INTEGER;
- query.bindings.append (binding);
- }
-
- return PropertyType.BOOLEAN;
- case SparqlTokenType.FALSE:
- next ();
-
- if (query.no_cache) {
- sql.append ("0");
- } else {
- sql.append ("?");
-
- var binding = new LiteralBinding ();
- binding.literal = "0";
- binding.data_type = PropertyType.INTEGER;
- query.bindings.append (binding);
- }
-
- return PropertyType.BOOLEAN;
- case SparqlTokenType.STRING_LITERAL1:
- case SparqlTokenType.STRING_LITERAL2:
- case SparqlTokenType.STRING_LITERAL_LONG1:
- case SparqlTokenType.STRING_LITERAL_LONG2:
- var literal = parse_string_literal (out type);
-
- switch (type) {
- case PropertyType.INTEGER:
- case PropertyType.BOOLEAN:
- case PropertyType.DOUBLE:
- case PropertyType.DATE:
- case PropertyType.DATETIME:
- if (query.no_cache) {
- sql.append (escape_sql_string_literal (literal));
- } else {
- var binding = new LiteralBinding ();
- binding.literal = literal;
- binding.data_type = type;
- query.bindings.append (binding);
- sql.append ("?");
- }
- return type;
- default:
- if (query.no_cache) {
- sql.append (escape_sql_string_literal (literal));
- } else {
- var binding = new LiteralBinding ();
- binding.literal = literal;
- query.bindings.append (binding);
- sql.append ("?");
- }
- append_collate (sql);
- return PropertyType.STRING;
- }
- case SparqlTokenType.INTEGER:
- next ();
-
- if (query.no_cache) {
- sql.append (get_last_string ());
- } else {
- sql.append ("?");
-
- var binding = new LiteralBinding ();
- binding.literal = get_last_string ();
- binding.data_type = PropertyType.INTEGER;
- query.bindings.append (binding);
- }
-
- return PropertyType.INTEGER;
- case SparqlTokenType.VAR:
- next ();
- string variable_name = get_last_string ().substring (1);
- var variable = context.get_variable (variable_name);
-
- if (context.need_binding_expression && variable.binding != null) {
- sql.append (variable.binding.sql_expression);
- } else {
- sql.append (variable.sql_expression);
- }
-
- if (variable.binding == null) {
- return PropertyType.UNKNOWN;
- } else {
- if (variable.binding.data_type == PropertyType.STRING) {
- append_collate (sql);
- }
- return variable.binding.data_type;
- }
- case SparqlTokenType.STR:
- translate_str (sql);
- return PropertyType.STRING;
- case SparqlTokenType.LANG:
- next ();
- sql.append ("''");
- return PropertyType.STRING;
- case SparqlTokenType.LANGMATCHES:
- next ();
- sql.append ("0");
- return PropertyType.BOOLEAN;
- case SparqlTokenType.DATATYPE:
- translate_datatype (sql);
- return PropertyType.RESOURCE;
- case SparqlTokenType.BOUND:
- translate_bound_call (sql);
- return PropertyType.BOOLEAN;
- case SparqlTokenType.COALESCE:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- var result = translate_function (sql, TRACKER_NS + "coalesce");
- expect (SparqlTokenType.CLOSE_PARENS);
- return result;
- case SparqlTokenType.CONCAT:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- var result = translate_function (sql, FN_NS + "concat");
- expect (SparqlTokenType.CLOSE_PARENS);
- return result;
- case SparqlTokenType.CONTAINS:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- var result = translate_function (sql, FN_NS + "contains");
- expect (SparqlTokenType.CLOSE_PARENS);
- return result;
- case SparqlTokenType.ENCODE_FOR_URI:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- sql.append ("SparqlEncodeForUri (");
- translate_expression_as_string (sql);
- sql.append (")");
- expect (SparqlTokenType.CLOSE_PARENS);
- return PropertyType.STRING;
- case SparqlTokenType.IF:
- return translate_if_call (sql);
- case SparqlTokenType.SAMETERM:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- sql.append ("(");
- translate_expression (sql);
- sql.append (" = ");
- expect (SparqlTokenType.COMMA);
- translate_expression (sql);
- sql.append (")");
- expect (SparqlTokenType.CLOSE_PARENS);
- return PropertyType.BOOLEAN;
- case SparqlTokenType.ISIRI:
- case SparqlTokenType.ISURI:
- translate_isuri (sql);
- return PropertyType.BOOLEAN;
- case SparqlTokenType.ISBLANK:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- next ();
- // TODO: support ISBLANK properly
- sql.append ("0");
- expect (SparqlTokenType.CLOSE_PARENS);
- return PropertyType.BOOLEAN;
- case SparqlTokenType.ISLITERAL:
- next ();
- return PropertyType.BOOLEAN;
- case SparqlTokenType.LCASE:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- var result = translate_function (sql, FN_NS + "lower-case");
- expect (SparqlTokenType.CLOSE_PARENS);
- return result;
- case SparqlTokenType.UCASE:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- var result = translate_function (sql, FN_NS + "upper-case");
- expect (SparqlTokenType.CLOSE_PARENS);
- return result;
- case SparqlTokenType.STRLEN:
- next ();
- sql.append ("LENGTH(");
- type = translate_aggregate_expression (sql);
- sql.append (")");
- return PropertyType.INTEGER;
- case SparqlTokenType.STRSTARTS:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- var result = translate_function (sql, FN_NS + "starts-with");
- expect (SparqlTokenType.CLOSE_PARENS);
- return result;
- case SparqlTokenType.STRENDS:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- var result = translate_function (sql, FN_NS + "ends-with");
- expect (SparqlTokenType.CLOSE_PARENS);
- return result;
- case SparqlTokenType.SUBSTR:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- var result = translate_function (sql, FN_NS + "substring");
- expect (SparqlTokenType.CLOSE_PARENS);
- return result;
- case SparqlTokenType.STRBEFORE:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- sql.append ("SparqlStringBefore (");
- translate_expression_as_string (sql);
- expect (SparqlTokenType.COMMA);
- sql.append (",");
- translate_expression_as_string (sql);
- sql.append (")");
- expect (SparqlTokenType.CLOSE_PARENS);
- return PropertyType.STRING;
- case SparqlTokenType.STRAFTER:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- sql.append ("SparqlStringAfter (");
- translate_expression_as_string (sql);
- expect (SparqlTokenType.COMMA);
- sql.append (",");
- translate_expression_as_string (sql);
- sql.append (")");
- expect (SparqlTokenType.CLOSE_PARENS);
- return PropertyType.STRING;
- case SparqlTokenType.REGEX:
- translate_regex (sql);
- query.no_cache = true;
- return PropertyType.BOOLEAN;
- case SparqlTokenType.EXISTS:
- case SparqlTokenType.NOT:
- translate_exists (sql);
- return PropertyType.BOOLEAN;
- case SparqlTokenType.COUNT:
- next ();
- sql.append ("COUNT(");
- translate_aggregate_expression (sql);
- sql.append (")");
- return PropertyType.INTEGER;
- case SparqlTokenType.SUM:
- next ();
- sql.append ("SUM(");
- type = translate_aggregate_expression (sql);
- sql.append (")");
- return type;
- case SparqlTokenType.AVG:
- next ();
- sql.append ("AVG(");
- type = translate_aggregate_expression (sql);
- sql.append (")");
- return type;
- case SparqlTokenType.MIN:
- next ();
- sql.append ("MIN(");
- type = translate_aggregate_expression (sql);
- sql.append (")");
- return type;
- case SparqlTokenType.MAX:
- next ();
- sql.append ("MAX(");
- type = translate_aggregate_expression (sql);
- sql.append (")");
- return type;
- case SparqlTokenType.ABS:
- next ();
- sql.append ("ABS(");
- type = translate_aggregate_expression (sql);
- sql.append (")");
- return type;
- case SparqlTokenType.ROUND:
- next ();
- sql.append ("ROUND(");
- type = translate_aggregate_expression (sql);
- sql.append (")");
- return type;
- case SparqlTokenType.CEIL:
- next ();
- sql.append ("SparqlCeil(");
- type = translate_aggregate_expression (sql);
- sql.append (")");
- return type;
- case SparqlTokenType.FLOOR:
- next ();
- sql.append ("SparqlFloor(");
- type = translate_aggregate_expression (sql);
- sql.append (")");
- return type;
- case SparqlTokenType.RAND:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- expect (SparqlTokenType.CLOSE_PARENS);
- sql.append ("SparqlRand()");
- return PropertyType.DOUBLE;
- case SparqlTokenType.NOW:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- expect (SparqlTokenType.CLOSE_PARENS);
- sql.append ("strftime('%s', 'now')");
- return PropertyType.DATETIME;
- case SparqlTokenType.SECONDS:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- var result = translate_function (sql, FN_NS + "seconds-from-dateTime");
- expect (SparqlTokenType.CLOSE_PARENS);
- return result;
- case SparqlTokenType.MINUTES:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- var result = translate_function (sql, FN_NS + "minutes-from-dateTime");
- expect (SparqlTokenType.CLOSE_PARENS);
- return result;
- case SparqlTokenType.HOURS:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- var result = translate_function (sql, FN_NS + "hours-from-dateTime");
- expect (SparqlTokenType.CLOSE_PARENS);
- return result;
- case SparqlTokenType.DAY:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- var result = translate_function (sql, FN_NS + "day-from-dateTime");
- expect (SparqlTokenType.CLOSE_PARENS);
- return result;
- case SparqlTokenType.MONTH:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- var result = translate_function (sql, FN_NS + "month-from-dateTime");
- expect (SparqlTokenType.CLOSE_PARENS);
- return result;
- case SparqlTokenType.YEAR:
- next ();
- expect (SparqlTokenType.OPEN_PARENS);
- var result = translate_function (sql, FN_NS + "year-from-dateTime");
- expect (SparqlTokenType.CLOSE_PARENS);
- return result;
- case SparqlTokenType.MD5:
- next ();
- sql.append ("SparqlChecksum(");
- type = translate_aggregate_expression (sql);
- sql.append (", \"md5\")");
- return type;
- case SparqlTokenType.SHA1:
- next ();
- sql.append ("SparqlChecksum(");
- type = translate_aggregate_expression (sql);
- sql.append (", \"sha1\")");
- return type;
- case SparqlTokenType.SHA256:
- next ();
- sql.append ("SparqlChecksum(");
- type = translate_aggregate_expression (sql);
- sql.append (", \"sha256\")");
- return type;
- case SparqlTokenType.SHA512:
- next ();
- sql.append ("SparqlChecksum(");
- type = translate_aggregate_expression (sql);
- sql.append (", \"sha512\")");
- return type;
- case SparqlTokenType.GROUP_CONCAT:
- next ();
- sql.append ("GROUP_CONCAT(");
- expect (SparqlTokenType.OPEN_PARENS);
- translate_expression_as_string (sql);
- sql.append (", ");
- expect (SparqlTokenType.COMMA);
- sql.append (escape_sql_string_literal (parse_string_literal ()));
- sql.append (")");
- expect (SparqlTokenType.CLOSE_PARENS);
- return PropertyType.STRING;
- case SparqlTokenType.PN_PREFIX:
- next ();
- string ns = get_last_string ();
- expect (SparqlTokenType.COLON);
- string uri = query.resolve_prefixed_name (ns, get_last_string ().substring (1));
- return translate_uri_expression (sql, uri);
- case SparqlTokenType.COLON:
- next ();
- string uri = query.resolve_prefixed_name ("", get_last_string ().substring (1));
- return translate_uri_expression (sql, uri);
- default:
- throw get_error ("expected primary expression");
- }
- }
-
- PropertyType translate_unary_expression (StringBuilder sql) throws Sparql.Error {
- if (accept (SparqlTokenType.OP_NEG)) {
- sql.append ("NOT (");
- var optype = translate_primary_expression (sql);
- sql.append (")");
- if (optype != PropertyType.BOOLEAN) {
- throw get_error ("expected boolean expression");
- }
- return PropertyType.BOOLEAN;
- } else if (accept (SparqlTokenType.PLUS)) {
- return translate_primary_expression (sql);
- } else if (accept (SparqlTokenType.MINUS)) {
- sql.append ("-(");
- var optype = translate_primary_expression (sql);
- sql.append (")");
- return optype;
- }
- return translate_primary_expression (sql);
- }
-
- private PropertyType translate_multiplicative_expression (StringBuilder sql) throws Sparql.Error {
- long begin = sql.len;
- var optype = translate_unary_expression (sql);
- while (true) {
- if (accept (SparqlTokenType.STAR)) {
- if (!maybe_numeric (optype)) {
- throw get_error ("expected numeric operand");
- }
- sql.insert (begin, "(");
- sql.append (" * ");
- if (!maybe_numeric (translate_unary_expression (sql))) {
- throw get_error ("expected numeric operand");
- }
- sql.append (")");
- } else if (accept (SparqlTokenType.DIV)) {
- if (!maybe_numeric (optype)) {
- throw get_error ("expected numeric operand");
- }
- sql.insert (begin, "(");
- sql.append (" / ");
- if (!maybe_numeric (translate_unary_expression (sql))) {
- throw get_error ("expected numeric operand");
- }
- sql.append (")");
- } else {
- break;
- }
- }
- return optype;
- }
-
- private PropertyType translate_additive_expression (StringBuilder sql) throws Sparql.Error {
- long begin = sql.len;
- var optype = translate_multiplicative_expression (sql);
- while (true) {
- if (accept (SparqlTokenType.PLUS)) {
- if (!maybe_numeric (optype)) {
- throw get_error ("expected numeric operand");
- }
- sql.insert (begin, "(");
- sql.append (" + ");
- if (!maybe_numeric (translate_multiplicative_expression (sql))) {
- throw get_error ("expected numeric operand");
- }
- sql.append (")");
- } else if (accept (SparqlTokenType.MINUS)) {
- if (!maybe_numeric (optype)) {
- throw get_error ("expected numeric operand");
- }
- sql.insert (begin, "(");
- sql.append (" - ");
- if (!maybe_numeric (translate_multiplicative_expression (sql))) {
- throw get_error ("expected numeric operand");
- }
- sql.append (")");
- } else {
- break;
- }
- }
- return optype;
- }
-
- private PropertyType translate_numeric_expression (StringBuilder sql) throws Sparql.Error {
- return translate_additive_expression (sql);
- }
-
- private PropertyType process_relational_expression (StringBuilder sql, long begin, uint n_bindings, PropertyType op1type, string operator) throws Sparql.Error {
- sql.insert (begin, "(");
- sql.append (operator);
- var op2type = translate_numeric_expression (sql);
- sql.append (")");
- if ((op1type == PropertyType.DATETIME && op2type == PropertyType.STRING)
- || (op1type == PropertyType.STRING && op2type == PropertyType.DATETIME)) {
- // TODO: improve performance (linked list)
- if (query.bindings.length () == n_bindings + 1) {
- // trigger string => datetime conversion
- query.bindings.last ().data.data_type = PropertyType.DATETIME;
- }
- } else if ((op1type == PropertyType.DATE && op2type == PropertyType.STRING)
- || (op1type == PropertyType.STRING && op2type == PropertyType.DATE)) {
- // TODO: improve performance (linked list)
- if (query.bindings.length () == n_bindings + 1) {
- // trigger string => date conversion
- query.bindings.last ().data.data_type = PropertyType.DATE;
- }
- }
- return PropertyType.BOOLEAN;
- }
-
- private PropertyType translate_in (StringBuilder sql, bool not) throws Sparql.Error {
-
- int in_variable_count = 0;
-
- if (not) {
- sql.append (" NOT");
- }
-
- expect (SparqlTokenType.OPEN_PARENS);
- sql.append (" IN (");
- if (!accept (SparqlTokenType.CLOSE_PARENS)) {
- in_variable_count++;
- translate_expression (sql);
- while (accept (SparqlTokenType.COMMA)) {
- sql.append (", ");
-
- in_variable_count++;
-
- if (in_variable_count > MAX_VARIABLES_FOR_IN && !query.no_cache) {
- query.no_cache = true;
- }
-
- translate_expression (sql);
- }
- expect (SparqlTokenType.CLOSE_PARENS);
- }
- sql.append (")");
-
- return PropertyType.BOOLEAN;
- }
-
- private PropertyType translate_relational_expression (StringBuilder sql) throws Sparql.Error {
- long begin = sql.len;
- // TODO: improve performance (linked list)
- uint n_bindings = query.bindings.length ();
- var optype = translate_numeric_expression (sql);
- if (accept (SparqlTokenType.OP_GE)) {
- return process_relational_expression (sql, begin, n_bindings, optype, " >= ");
- } else if (accept (SparqlTokenType.OP_EQ)) {
- return process_relational_expression (sql, begin, n_bindings, optype, " = ");
- } else if (accept (SparqlTokenType.OP_NE)) {
- return process_relational_expression (sql, begin, n_bindings, optype, " <> ");
- } else if (accept (SparqlTokenType.OP_LT)) {
- return process_relational_expression (sql, begin, n_bindings, optype, " < ");
- } else if (accept (SparqlTokenType.OP_LE)) {
- return process_relational_expression (sql, begin, n_bindings, optype, " <= ");
- } else if (accept (SparqlTokenType.OP_GT)) {
- return process_relational_expression (sql, begin, n_bindings, optype, " > ");
- } else if (accept (SparqlTokenType.OP_IN)) {
- return translate_in (sql, false);
- } else if (accept (SparqlTokenType.NOT)) {
- expect (SparqlTokenType.OP_IN);
- return translate_in (sql, true);
- }
- return optype;
- }
-
- private PropertyType translate_value_logical (StringBuilder sql) throws Sparql.Error {
- return translate_relational_expression (sql);
- }
-
- private PropertyType translate_conditional_and_expression (StringBuilder sql) throws Sparql.Error {
- long begin = sql.len;
- var optype = translate_value_logical (sql);
- while (accept (SparqlTokenType.OP_AND)) {
- if (optype != PropertyType.BOOLEAN) {
- throw get_error ("expected boolean expression");
- }
- sql.insert (begin, "(");
- sql.append (" AND ");
- optype = translate_value_logical (sql);
- sql.append (")");
- if (optype != PropertyType.BOOLEAN) {
- throw get_error ("expected boolean expression");
- }
- }
- return optype;
- }
-
- private PropertyType translate_conditional_or_expression (StringBuilder sql) throws Sparql.Error {
- long begin = sql.len;
- var optype = translate_conditional_and_expression (sql);
- while (accept (SparqlTokenType.OP_OR)) {
- if (optype != PropertyType.BOOLEAN) {
- throw get_error ("expected boolean expression");
- }
- sql.insert (begin, "(");
- sql.append (" OR ");
- optype = translate_conditional_and_expression (sql);
- sql.append (")");
- if (optype != PropertyType.BOOLEAN) {
- throw get_error ("expected boolean expression");
- }
- }
- return optype;
- }
-
- internal PropertyType translate_expression (StringBuilder sql) throws Sparql.Error {
- return translate_conditional_or_expression (sql);
- }
-
- private PropertyType translate_bracketted_expression (StringBuilder sql) throws Sparql.Error {
- expect (SparqlTokenType.OPEN_PARENS);
-
- if (current () == SparqlTokenType.SELECT) {
- // scalar subquery
-
- sql.append ("(");
- var select_context = pattern.translate_select (sql, true, true);
- sql.append (")");
-
- expect (SparqlTokenType.CLOSE_PARENS);
- return select_context.type;
- }
-
- var optype = translate_expression (sql);
- expect (SparqlTokenType.CLOSE_PARENS);
- return optype;
- }
-
- private PropertyType translate_aggregate_expression (StringBuilder sql) throws Sparql.Error {
- expect (SparqlTokenType.OPEN_PARENS);
- if (accept (SparqlTokenType.DISTINCT)) {
- sql.append ("DISTINCT ");
- }
-
- bool is_var = (current () == SparqlTokenType.VAR);
- var optype = translate_expression (sql);
-
- if (is_var) {
- var variable = context.get_variable (get_last_string ().substring (1));
- if (variable.binding == null) {
- throw get_error ("use of undefined variable `%s'".printf (variable.name));
- }
- }
-
- expect (SparqlTokenType.CLOSE_PARENS);
- return optype;
- }
-
- internal PropertyType translate_constraint (StringBuilder sql) throws Sparql.Error {
- switch (current ()) {
- case SparqlTokenType.STR:
- case SparqlTokenType.LANG:
- case SparqlTokenType.LANGMATCHES:
- case SparqlTokenType.DATATYPE:
- case SparqlTokenType.BOUND:
- case SparqlTokenType.IF:
- case SparqlTokenType.SAMETERM:
- case SparqlTokenType.ISIRI:
- case SparqlTokenType.ISURI:
- case SparqlTokenType.ISBLANK:
- case SparqlTokenType.ISLITERAL:
- case SparqlTokenType.REGEX:
- case SparqlTokenType.EXISTS:
- case SparqlTokenType.NOT:
- return translate_primary_expression (sql);
- default:
- return translate_bracketted_expression (sql);
- }
- }
-}
diff --git a/src/libtracker-data/tracker-sparql-pattern.vala b/src/libtracker-data/tracker-sparql-pattern.vala
deleted file mode 100644
index 59810da63..000000000
--- a/src/libtracker-data/tracker-sparql-pattern.vala
+++ /dev/null
@@ -1,1659 +0,0 @@
-/*
- * Copyright (C) 2008-2010, Nokia
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-namespace Tracker.Sparql {
- // Represents a variable used as a predicate
- class PredicateVariable : Object {
- public string? subject;
- public string? object;
- public bool return_graph;
-
- public Class? domain;
-
- Data.Manager manager;
-
- public PredicateVariable (Data.Manager manager) {
- this.manager = manager;
- }
-
- public string get_sql_query (Query query) throws Sparql.Error {
- try {
- var sql = new StringBuilder ();
- var ontologies = manager.get_ontologies ();
- var iface = manager.get_db_interface ();
-
- if (subject != null) {
- // single subject
- var subject_id = Tracker.Data.query_resource_id (manager, iface, subject);
-
- DBCursor cursor = null;
- if (subject_id > 0) {
- var stmt = iface.create_statement (DBStatementCacheType.SELECT,
- "SELECT (SELECT Uri FROM Resource WHERE ID = \"rdf:type\") " +
- "FROM \"rdfs:Resource_rdf:type\" WHERE ID = ?");
- stmt.bind_int (0, subject_id);
- cursor = stmt.start_cursor ();
- }
-
- bool first = true;
- if (cursor != null) {
- while (cursor.next ()) {
- var domain = ontologies.get_class_by_uri (cursor.get_string (0));
-
- foreach (Property prop in ontologies.get_properties ()) {
- if (prop.domain == domain) {
- if (first) {
- first = false;
- } else {
- sql.append (" UNION ALL ");
- }
- sql.append_printf ("SELECT ID, (SELECT ID FROM Resource WHERE Uri = '%s') AS \"predicate\", ", prop.uri);
-
- Expression.append_expression_as_string (sql, "\"%s\"".printf (prop.name), prop.data_type);
-
- sql.append (" AS \"object\"");
- if (return_graph) {
- sql.append_printf (", \"%s:graph\" AS \"graph\"", prop.name);
- }
- sql.append_printf (" FROM \"%s\"", prop.table_name);
-
- sql.append (" WHERE ID = ?");
-
- var binding = new LiteralBinding ();
- binding.literal = subject_id.to_string ();
- binding.data_type = PropertyType.INTEGER;
- query.bindings.append (binding);
- }
- }
- }
- }
-
- if (first) {
- /* no match */
- sql.append ("SELECT NULL AS ID, NULL AS \"predicate\", NULL AS \"object\", NULL AS \"graph\"");
- }
- } else if (object != null) {
- // single object
- var object_id = Data.query_resource_id (manager, iface, object);
-
- var stmt = iface.create_statement (DBStatementCacheType.SELECT,
- "SELECT (SELECT Uri FROM Resource WHERE ID = \"rdf:type\") " +
- "FROM \"rdfs:Resource_rdf:type\" WHERE ID = ?");
- stmt.bind_int (0, object_id);
- var cursor = stmt.start_cursor ();
-
- bool first = true;
- if (cursor != null) {
- while (cursor.next ()) {
- var range = ontologies.get_class_by_uri (cursor.get_string (0));
-
- foreach (Property prop in ontologies.get_properties ()) {
- if (prop.range == range) {
- if (first) {
- first = false;
- } else {
- sql.append (" UNION ALL ");
- }
- sql.append_printf ("SELECT ID, (SELECT ID FROM Resource WHERE Uri = '%s') AS \"predicate\", ", prop.uri);
-
- Expression.append_expression_as_string (sql, "\"%s\"".printf (prop.name), prop.data_type);
-
- sql.append (" AS \"object\"");
- if (return_graph) {
- sql.append_printf (", \"%s:graph\" AS \"graph\"", prop.name);
- }
- sql.append_printf (" FROM \"%s\"", prop.table_name);
- }
- }
- }
- }
-
- if (first) {
- /* no match */
- sql.append ("SELECT NULL AS ID, NULL AS \"predicate\", NULL AS \"object\", NULL AS \"graph\"");
- }
- } else if (domain != null) {
- // any subject, predicates limited to a specific domain
- bool first = true;
- foreach (Property prop in ontologies.get_properties ()) {
- if (prop.domain == domain) {
- if (first) {
- first = false;
- } else {
- sql.append (" UNION ALL ");
- }
- sql.append_printf ("SELECT ID, (SELECT ID FROM Resource WHERE Uri = '%s') AS \"predicate\", ", prop.uri);
-
- Expression.append_expression_as_string (sql, "\"%s\"".printf (prop.name), prop.data_type);
-
- sql.append (" AS \"object\"");
- if (return_graph) {
- sql.append_printf (", \"%s:graph\" AS \"graph\"", prop.name);
- }
- sql.append_printf (" FROM \"%s\"", prop.table_name);
- }
- }
- } else {
- // UNION over all properties would exceed SQLite limits
- throw query.get_internal_error ("Unrestricted predicate variables not supported");
- }
- return sql.str;
- } catch (GLib.Error e) {
- throw new Sparql.Error.INTERNAL (e.message);
- }
- }
- }
-}
-
-class Tracker.Sparql.Pattern : Object {
- weak Query query;
- weak Expression expression;
-
- int counter;
-
- int next_table_index;
-
- internal string current_graph;
- bool current_graph_is_var;
- string current_subject;
- bool current_subject_is_var;
- string current_predicate;
- bool current_predicate_is_var;
- public Variable? fts_subject;
- public string[] fts_variables;
- internal StringBuilder? match_str;
- public bool queries_fts_data = false;
-
- Data.Manager manager;
-
- public Pattern (Query query) {
- this.query = query;
- this.manager = query.manager;
- this.expression = query.expression;
- }
-
- Context context {
- get { return query.context; }
- set { query.context = value; }
- }
-
- private inline bool next () throws Sparql.Error {
- return query.next ();
- }
-
- private inline SparqlTokenType current () {
- return query.current ();
- }
-
- private inline bool accept (SparqlTokenType type) throws Sparql.Error {
- return query.accept (type);
- }
-
- private inline void optional (SparqlTokenType type) throws Sparql.Error {
- query.optional (type);
- }
-
- private Sparql.Error get_error (string msg) {
- return query.get_error (msg);
- }
-
- private bool expect (SparqlTokenType type) throws Sparql.Error {
- return query.expect (type);
- }
-
- private SourceLocation get_location () {
- return query.get_location ();
- }
-
- private void set_location (SourceLocation location) {
- query.set_location (location);
- }
-
- private string get_last_string (int strip = 0) {
- return query.get_last_string (strip);
- }
-
- class TripleContext : Context {
- // SQL tables
- public List<DataTable> tables;
- public HashTable<string,DataTable> table_map;
- // SPARQL literals
- public List<LiteralBinding> bindings;
- // SPARQL variables
- public List<Variable> variables;
- public HashTable<Variable,VariableBindingList> var_bindings;
-
- public TripleContext (Query query, Context parent_context) {
- base (query, parent_context);
-
- tables = new List<DataTable> ();
- table_map = new HashTable<string,DataTable>.full (str_hash, str_equal, g_free, g_object_unref);
-
- variables = new List<Variable> ();
- var_bindings = new HashTable<Variable,VariableBindingList>.full (Variable.hash, Variable.equal, g_object_unref, g_object_unref);
-
- bindings = new List<LiteralBinding> ();
- }
- }
-
- TripleContext? triple_context;
-
- internal SelectContext translate_select (StringBuilder sql, bool subquery = false, bool scalar_subquery = false) throws Sparql.Error {
- SelectContext result;
-
- if (scalar_subquery) {
- result = new SelectContext.subquery (query, context);
- } else {
- result = new SelectContext (query, context);
- }
- context = result;
- var type = PropertyType.UNKNOWN;
-
- var pattern_sql = new StringBuilder ();
- var old_bindings = (owned) query.bindings;
-
- sql.append ("SELECT ");
-
- expect (SparqlTokenType.SELECT);
-
- if (accept (SparqlTokenType.DISTINCT)) {
- sql.append ("DISTINCT ");
- } else if (accept (SparqlTokenType.REDUCED)) {
- }
-
- // skip select variables (processed later)
- var select_variables_location = get_location ();
- expression.skip_select_variables ();
-
- if (accept (SparqlTokenType.FROM)) {
- accept (SparqlTokenType.NAMED);
- expect (SparqlTokenType.IRI_REF);
- }
-
- optional (SparqlTokenType.WHERE);
-
- var pattern = translate_group_graph_pattern (pattern_sql);
- foreach (var key in pattern.var_set.get_keys ()) {
- context.var_set.insert (key, VariableState.BOUND);
- }
-
- // process select variables
- var after_where = get_location ();
- set_location (select_variables_location);
-
- // report use of undefined variables
- foreach (var variable in context.var_set.get_keys ()) {
- if (variable.binding == null) {
- throw get_error ("use of undefined variable `%s'".printf (variable.name));
- }
- }
-
- var where_bindings = (owned) query.bindings;
- query.bindings = (owned) old_bindings;
-
- bool first = true;
- if (accept (SparqlTokenType.STAR)) {
- foreach (var variable in context.var_set.get_keys ()) {
- if (!first) {
- sql.append (", ");
- } else {
- first = false;
- }
- if (subquery) {
- // don't convert to string in subqueries
- sql.append (variable.sql_expression);
- } else {
- Expression.append_expression_as_string (sql, variable.sql_expression, variable.binding.data_type);
- sql.append_printf (" AS \"%s\"", variable.name);
- }
- result.types += variable.binding.data_type;
- result.variable_names += variable.name;
- }
- } else {
- for (int i = 0; ; i++) {
- first = false;
-
- if (i > 0) {
- sql.append (", ");
- }
-
- type = expression.translate_select_expression (sql, subquery, i);
- result.types += type;
-
- switch (current ()) {
- case SparqlTokenType.FROM:
- case SparqlTokenType.WHERE:
- case SparqlTokenType.OPEN_BRACE:
- case SparqlTokenType.GROUP:
- case SparqlTokenType.ORDER:
- case SparqlTokenType.LIMIT:
- case SparqlTokenType.OFFSET:
- case SparqlTokenType.EOF:
- break;
- default:
- continue;
- }
- break;
- }
- }
-
- if (queries_fts_data && fts_subject != null) {
- // Ensure there's a rowid to match on in FTS queries
- if (!first) {
- sql.append (", ");
- } else {
- first = false;
- }
-
- sql.append ("%s AS rowid ".printf (fts_subject.sql_expression));
- }
-
- // literals in select expressions need to be bound before literals in the where clause
- foreach (var binding in where_bindings) {
- query.bindings.append (binding);
- }
-
- if (first) {
- sql.append ("NULL");
- }
-
- // select from results of WHERE clause
- sql.append (" FROM (");
- sql.append (pattern_sql.str);
- sql.append (")");
-
- set_location (after_where);
-
- if (accept (SparqlTokenType.GROUP)) {
- expect (SparqlTokenType.BY);
- sql.append (" GROUP BY ");
- bool first_group = true;
- do {
- if (first_group) {
- first_group = false;
- } else {
- sql.append (", ");
- }
- expression.translate_expression (sql);
- } while (current () != SparqlTokenType.HAVING && current () != SparqlTokenType.ORDER && current () != SparqlTokenType.LIMIT && current () != SparqlTokenType.OFFSET && current () != SparqlTokenType.CLOSE_BRACE && current () != SparqlTokenType.CLOSE_PARENS && current () != SparqlTokenType.EOF);
-
- if (accept (SparqlTokenType.HAVING)) {
- sql.append (" HAVING ");
- expression.translate_constraint (sql);
- }
- }
-
- if (accept (SparqlTokenType.ORDER)) {
- expect (SparqlTokenType.BY);
- sql.append (" ORDER BY ");
- bool first_order = true;
- do {
- if (first_order) {
- first_order = false;
- } else {
- sql.append (", ");
- }
- expression.translate_order_condition (sql);
- } while (current () != SparqlTokenType.LIMIT && current () != SparqlTokenType.OFFSET && current () != SparqlTokenType.CLOSE_BRACE && current () != SparqlTokenType.CLOSE_PARENS && current () != SparqlTokenType.EOF);
- }
-
- int limit = -1;
- int offset = -1;
-
- if (accept (SparqlTokenType.LIMIT)) {
- expect (SparqlTokenType.INTEGER);
- limit = int.parse (get_last_string ());
- if (accept (SparqlTokenType.OFFSET)) {
- expect (SparqlTokenType.INTEGER);
- offset = int.parse (get_last_string ());
- }
- } else if (accept (SparqlTokenType.OFFSET)) {
- expect (SparqlTokenType.INTEGER);
- offset = int.parse (get_last_string ());
- if (accept (SparqlTokenType.LIMIT)) {
- expect (SparqlTokenType.INTEGER);
- limit = int.parse (get_last_string ());
- }
- }
-
- // LIMIT and OFFSET
- if (limit >= 0) {
- sql.append (" LIMIT ?");
-
- var binding = new LiteralBinding ();
- binding.literal = limit.to_string ();
- binding.data_type = PropertyType.INTEGER;
- query.bindings.append (binding);
-
- if (offset >= 0) {
- sql.append (" OFFSET ?");
-
- binding = new LiteralBinding ();
- binding.literal = offset.to_string ();
- binding.data_type = PropertyType.INTEGER;
- query.bindings.append (binding);
- }
- } else if (offset >= 0) {
- sql.append (" LIMIT -1 OFFSET ?");
-
- var binding = new LiteralBinding ();
- binding.literal = offset.to_string ();
- binding.data_type = PropertyType.INTEGER;
- query.bindings.append (binding);
- }
-
- if (queries_fts_data && match_str != null && fts_subject != null) {
- var str = new StringBuilder ("SELECT ");
- first = true;
-
- foreach (var fts_var in fts_variables) {
- if (!first) {
- str.append (", ");
- } else {
- first = false;
- }
-
- str.append (fts_var);
- }
-
- str.append (" FROM fts5 JOIN (");
- sql.prepend (str.str);
- sql.append_printf (") AS ranks ON fts5.rowid=rowid WHERE fts5 %s".printf (match_str.str));
- }
-
- context = context.parent_context;
-
- result.type = type;
- match_str = null;
- fts_subject = null;
-
- return result;
- }
-
- internal void translate_exists (StringBuilder sql) throws Sparql.Error {
- bool not = accept (SparqlTokenType.NOT);
- expect (SparqlTokenType.EXISTS);
-
- SelectContext result;
- result = new SelectContext.subquery (query, context);
- context = result;
-
- if (not) {
- // NOT EXISTS
- sql.append ("NOT EXISTS (");
- } else {
- // EXISTS
- sql.append ("EXISTS (");
- }
-
- var pattern = translate_group_graph_pattern (sql);
- foreach (var key in pattern.var_set.get_keys ()) {
- context.var_set.insert (key, VariableState.BOUND);
- }
-
- // report use of undefined variables
- foreach (var variable in context.var_set.get_keys ()) {
- if (variable.binding == null) {
- throw get_error ("use of undefined variable `%s'".printf (variable.name));
- }
- }
-
- sql.append (")");
-
- context = context.parent_context;
- }
-
- internal string parse_var_or_term (StringBuilder? sql, out bool is_var) throws Sparql.Error {
- string result = "";
- is_var = false;
- if (current () == SparqlTokenType.VAR) {
- is_var = true;
- next ();
- result = get_last_string ().substring (1);
- } else if (current () == SparqlTokenType.IRI_REF) {
- next ();
- result = get_last_string (1);
- } else if (current () == SparqlTokenType.PN_PREFIX) {
- // prefixed name with namespace foo:bar
- next ();
- string ns = get_last_string ();
- expect (SparqlTokenType.COLON);
- result = query.resolve_prefixed_name (ns, get_last_string ().substring (1));
- } else if (current () == SparqlTokenType.COLON) {
- // prefixed name without namespace :bar
- next ();
- result = query.resolve_prefixed_name ("", get_last_string ().substring (1));
- } else if (accept (SparqlTokenType.BLANK_NODE)) {
- // _:foo
- expect (SparqlTokenType.COLON);
- result = query.generate_bnodeid (get_last_string ().substring (1));
- } else if (current () == SparqlTokenType.STRING_LITERAL1) {
- result = expression.parse_string_literal ();
- } else if (current () == SparqlTokenType.STRING_LITERAL2) {
- result = expression.parse_string_literal ();
- } else if (current () == SparqlTokenType.STRING_LITERAL_LONG1) {
- result = expression.parse_string_literal ();
- } else if (current () == SparqlTokenType.STRING_LITERAL_LONG2) {
- result = expression.parse_string_literal ();
- } else if (current () == SparqlTokenType.INTEGER) {
- next ();
- result = get_last_string ();
- } else if (current () == SparqlTokenType.DECIMAL) {
- next ();
- result = get_last_string ();
- } else if (current () == SparqlTokenType.DOUBLE) {
- next ();
- result = get_last_string ();
- } else if (current () == SparqlTokenType.TRUE) {
- next ();
- result = "true";
- } else if (current () == SparqlTokenType.FALSE) {
- next ();
- result = "false";
- } else if (current () == SparqlTokenType.OPEN_BRACKET) {
- next ();
-
- result = query.generate_bnodeid (null);
-
- string old_subject = current_subject;
- bool old_subject_is_var = current_subject_is_var;
-
- current_subject = result;
- current_subject_is_var = true;
- parse_property_list_not_empty (sql);
- expect (SparqlTokenType.CLOSE_BRACKET);
-
- current_subject = old_subject;
- current_subject_is_var = old_subject_is_var;
-
- is_var = true;
- } else {
- throw get_error ("expected variable or term");
- }
- return result;
- }
-
- private void parse_object_list (StringBuilder sql, bool in_simple_optional = false) throws Sparql.Error {
- while (true) {
- parse_object (sql, in_simple_optional);
- if (accept (SparqlTokenType.COMMA)) {
- continue;
- }
- break;
- }
- }
-
- private void parse_property_list_not_empty (StringBuilder sql, bool in_simple_optional = false) throws Sparql.Error {
- while (true) {
- var old_predicate = current_predicate;
- var old_predicate_is_var = current_predicate_is_var;
-
- current_predicate = null;
- current_predicate_is_var = false;
- if (current () == SparqlTokenType.VAR) {
- current_predicate_is_var = true;
- next ();
- current_predicate = get_last_string ().substring (1);
- } else if (current () == SparqlTokenType.IRI_REF) {
- next ();
- current_predicate = get_last_string (1);
- } else if (current () == SparqlTokenType.PN_PREFIX) {
- next ();
- string ns = get_last_string ();
- expect (SparqlTokenType.COLON);
- current_predicate = query.resolve_prefixed_name (ns, get_last_string ().substring (1));
- } else if (current () == SparqlTokenType.COLON) {
- next ();
- current_predicate = query.resolve_prefixed_name ("", get_last_string ().substring (1));
- } else if (current () == SparqlTokenType.A) {
- next ();
- current_predicate = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";
- } else {
- throw get_error ("expected non-empty property list");
- }
- parse_object_list (sql, in_simple_optional);
-
- current_predicate = old_predicate;
- current_predicate_is_var = old_predicate_is_var;
-
- if (accept (SparqlTokenType.SEMICOLON)) {
- if (current () == SparqlTokenType.DOT) {
- // semicolon before dot is allowed in both, SPARQL and Turtle
- break;
- }
- continue;
- }
- break;
- }
- }
-
- private void translate_filter (StringBuilder sql) throws Sparql.Error {
- expect (SparqlTokenType.FILTER);
- expression.translate_constraint (sql);
- }
-
- private void skip_filter () throws Sparql.Error {
- expect (SparqlTokenType.FILTER);
-
- switch (current ()) {
- case SparqlTokenType.STR:
- case SparqlTokenType.LANG:
- case SparqlTokenType.LANGMATCHES:
- case SparqlTokenType.DATATYPE:
- case SparqlTokenType.BOUND:
- case SparqlTokenType.SAMETERM:
- case SparqlTokenType.ISIRI:
- case SparqlTokenType.ISURI:
- case SparqlTokenType.ISBLANK:
- case SparqlTokenType.ISLITERAL:
- case SparqlTokenType.REGEX:
- next ();
- break;
- default:
- break;
- }
-
- expect (SparqlTokenType.OPEN_PARENS);
- int n_parens = 1;
- while (n_parens > 0) {
- if (accept (SparqlTokenType.OPEN_PARENS)) {
- n_parens++;
- } else if (accept (SparqlTokenType.CLOSE_PARENS)) {
- n_parens--;
- } else if (current () == SparqlTokenType.EOF) {
- throw get_error ("unexpected end of query, expected )");
- } else {
- // ignore everything else
- next ();
- }
- }
- }
-
- private void start_triples_block (StringBuilder sql) throws Sparql.Error {
- context = triple_context = new TripleContext (query, context);
-
- sql.append ("SELECT ");
- }
-
- private void end_triples_block (StringBuilder sql, ref bool first_where, bool in_group_graph_pattern) throws Sparql.Error {
- // remove last comma and space
- sql.truncate (sql.len - 2);
-
- sql.append (" FROM ");
- bool first = true;
- foreach (DataTable table in triple_context.tables) {
- if (!first) {
- sql.append (", ");
- } else {
- first = false;
- }
- if (table.sql_db_tablename != null) {
- sql.append_printf ("\"%s\"", table.sql_db_tablename);
- } else {
- sql.append_printf ("(%s)", table.predicate_variable.get_sql_query (query));
- }
- sql.append_printf (" AS \"%s\"", table.sql_query_tablename);
- }
-
- foreach (var variable in triple_context.variables) {
- bool maybe_null = true;
- bool in_simple_optional = false;
- PropertyType last_type = PropertyType.UNKNOWN;
- string last_name = null;
- foreach (VariableBinding binding in triple_context.var_bindings.lookup (variable).list) {
- string name;
- if (binding.table != null) {
- name = binding.sql_expression;
- } else {
- // simple optional with inverse functional property
- // always first in loop as variable is required to be unbound
- name = variable.sql_expression;
- }
- var type = binding.data_type;
- if (last_name != null) {
- if (!first_where) {
- sql.append (" AND ");
- } else {
- sql.append (" WHERE ");
- first_where = false;
- }
-
- if (last_type == PropertyType.STRING && type == PropertyType.RESOURCE) {
- sql.append_printf ("(SELECT ID FROM Resource WHERE Uri = %s)", last_name);
- } else {
- sql.append (last_name);
- }
-
- sql.append (" = ");
-
- if (last_type == PropertyType.RESOURCE && type == PropertyType.STRING) {
- sql.append_printf ("(SELECT ID FROM Resource WHERE Uri = %s)", name);
- } else {
- sql.append (name);
- }
- }
- last_name = name;
- last_type = type;
- if (!binding.maybe_null) {
- maybe_null = false;
- }
- in_simple_optional = binding.in_simple_optional;
- }
-
- if (maybe_null && !in_simple_optional) {
- // ensure that variable is bound in case it could return NULL in SQL
- // assuming SPARQL variable is not optional
- if (!first_where) {
- sql.append (" AND ");
- } else {
- sql.append (" WHERE ");
- first_where = false;
- }
- sql.append_printf ("%s IS NOT NULL", variable.sql_expression);
- }
- }
- foreach (LiteralBinding binding in triple_context.bindings) {
- if (!first_where) {
- sql.append (" AND ");
- } else {
- sql.append (" WHERE ");
- first_where = false;
- }
- sql.append (binding.sql_expression);
- if (binding.is_fts_match) {
- // parameters do not work with fts MATCH
- string escaped_literal = string.joinv ("''", binding.literal.split ("'"));
- sql.append_printf (" MATCH '%s'", escaped_literal);
-
- if (match_str == null) {
- match_str = new StringBuilder ();
- match_str.append_printf (" MATCH '%s'", escaped_literal);
- }
- } else {
- sql.append (" = ");
- if (binding.data_type == PropertyType.RESOURCE) {
- sql.append ("(SELECT ID FROM Resource WHERE Uri = ?)");
- } else {
- sql.append ("?");
- }
- query.bindings.append (binding);
- }
- }
-
- if (in_group_graph_pattern) {
- sql.append (")");
- }
-
- foreach (var v in context.var_set.get_keys ()) {
- context.parent_context.var_set.insert (v, VariableState.BOUND);
- }
-
- triple_context = null;
- context = context.parent_context;
- }
-
- private void parse_triples (StringBuilder sql, long group_graph_pattern_start, ref bool in_triples_block, ref bool first_where, ref bool in_group_graph_pattern, bool found_simple_optional) throws Sparql.Error {
- while (true) {
- if (current () != SparqlTokenType.VAR &&
- current () != SparqlTokenType.IRI_REF &&
- current () != SparqlTokenType.PN_PREFIX &&
- current () != SparqlTokenType.COLON &&
- current () != SparqlTokenType.OPEN_BRACKET) {
- break;
- }
- if (in_triples_block && !in_group_graph_pattern && found_simple_optional) {
- // if there is a regular triple pattern after a simple optional
- // we need to use a separate triple block to avoid possible conflicts
- // due to not using a JOIN for the simple optional
- end_triples_block (sql, ref first_where, in_group_graph_pattern);
- in_triples_block = false;
- in_group_graph_pattern = true;
- }
- if (!in_triples_block) {
- if (in_group_graph_pattern) {
- sql.insert (group_graph_pattern_start, "SELECT * FROM (");
- sql.append (") NATURAL INNER JOIN (");
- }
- in_triples_block = true;
- first_where = true;
- start_triples_block (sql);
- }
-
- current_subject = parse_var_or_term (sql, out current_subject_is_var);
- parse_property_list_not_empty (sql);
-
- if (!accept (SparqlTokenType.DOT)) {
- break;
- }
- }
- }
-
- private bool is_subclass (Class class1, Class class2) {
- if (class1 == class2) {
- return true;
- }
- foreach (var superclass in class1.get_super_classes ()) {
- if (is_subclass (superclass, class2)) {
- return true;
- }
- }
- return false;
- }
-
- private bool is_simple_optional () {
- var optional_start = get_location ();
- try {
- // check that we have { ?v foo:bar ?o }
- // where ?v is an already BOUND variable
- // foo:bar is a single-valued property
- // that is known to be in domain of ?v
- // ?o has not been used before
- // or
- // where ?v has not been used before
- // foo:bar is an inverse functional property
- // ?o is an already ?BOUND variable
-
- expect (SparqlTokenType.OPEN_BRACE);
-
- // check subject
- if (!accept (SparqlTokenType.VAR)) {
- return false;
- }
- var left_variable = context.get_variable (get_last_string ().substring (1));
- var left_variable_state = context.var_set.lookup (left_variable);
-
- // check predicate
- string predicate;
- if (accept (SparqlTokenType.IRI_REF)) {
- predicate = get_last_string (1);
- } else if (accept (SparqlTokenType.PN_PREFIX)) {
- string ns = get_last_string ();
- expect (SparqlTokenType.COLON);
- predicate = query.resolve_prefixed_name (ns, get_last_string ().substring (1));
- } else if (accept (SparqlTokenType.COLON)) {
- predicate = query.resolve_prefixed_name ("", get_last_string ().substring (1));
- } else {
- return false;
- }
- var ontologies = manager.get_ontologies ();
- var prop = ontologies.get_property_by_uri (predicate);
- if (prop == null) {
- return false;
- }
-
- // check object
- if (!accept (SparqlTokenType.VAR)) {
- return false;
- }
- var right_variable = context.get_variable (get_last_string ().substring (1));
- var right_variable_state = context.var_set.lookup (right_variable);
-
- optional (SparqlTokenType.DOT);
-
- // check it is only one triple pattern
- if (!accept (SparqlTokenType.CLOSE_BRACE)) {
- return false;
- }
-
- if (left_variable_state == VariableState.BOUND && !prop.multiple_values && right_variable_state == 0) {
- bool in_domain = false;
- foreach (VariableBinding binding in triple_context.var_bindings.lookup (left_variable).list) {
- if (binding.type != null && is_subclass (binding.type, prop.domain)) {
- in_domain = true;
- break;
- }
- }
-
- if (in_domain) {
- // first valid case described in above comment
- return true;
- }
- } else if (left_variable_state == 0 && prop.is_inverse_functional_property && right_variable_state == VariableState.BOUND) {
- // second valid case described in above comment
- return true;
- }
-
- // no match
- return false;
- } catch (Sparql.Error e) {
- return false;
- } finally {
- // in any case, go back to the start of the optional
- set_location (optional_start);
- }
- }
-
- internal Context translate_group_graph_pattern (StringBuilder sql) throws Sparql.Error {
- expect (SparqlTokenType.OPEN_BRACE);
-
- if (current () == SparqlTokenType.SELECT) {
- var result = translate_select (sql, true);
- context = result;
-
- // only export selected variables
- context.var_set = context.select_var_set;
- context.select_var_set = new HashTable<Variable,int>.full (Variable.hash, Variable.equal, g_object_unref, null);
-
- expect (SparqlTokenType.CLOSE_BRACE);
-
- context = context.parent_context;
- return result;
- }
-
- var result = new Context (query, context);
- context = result;
-
- SourceLocation[] filters = { };
-
- bool in_triples_block = false;
- bool in_group_graph_pattern = false;
- bool first_where = true;
- bool found_simple_optional = false;
- long group_graph_pattern_start = sql.len;
-
- // optional TriplesBlock
- parse_triples (sql, group_graph_pattern_start, ref in_triples_block, ref first_where, ref in_group_graph_pattern, found_simple_optional);
-
- while (true) {
- // check whether we have GraphPatternNotTriples | Filter
- if (accept (SparqlTokenType.OPTIONAL)) {
- if (!in_group_graph_pattern && is_simple_optional ()) {
- // perform join-less optional (like non-optional except for the IS NOT NULL check)
- found_simple_optional = true;
- expect (SparqlTokenType.OPEN_BRACE);
-
- current_subject = parse_var_or_term (sql, out current_subject_is_var);
- parse_property_list_not_empty (sql, true);
-
- accept (SparqlTokenType.DOT);
- expect (SparqlTokenType.CLOSE_BRACE);
- } else {
- if (!in_triples_block && !in_group_graph_pattern) {
- // expand { OPTIONAL { ... } } into { { } OPTIONAL { ... } }
- // empty graph pattern => return one result without bound variables
- sql.append ("SELECT 1");
- } else if (in_triples_block) {
- end_triples_block (sql, ref first_where, in_group_graph_pattern);
- in_triples_block = false;
- }
- if (!in_group_graph_pattern) {
- in_group_graph_pattern = true;
- }
-
- var select = new StringBuilder ("SELECT ");
-
- int left_index = ++next_table_index;
- int right_index = ++next_table_index;
-
- sql.append_printf (") AS t%d_g LEFT JOIN (", left_index);
-
- context = translate_group_graph_pattern (sql);
-
- sql.append_printf (") AS t%d_g", right_index);
-
- bool first = true;
- bool first_common = true;
- foreach (var v in context.var_set.get_keys ()) {
- if (first) {
- first = false;
- } else {
- select.append (", ");
- }
-
- var old_state = context.parent_context.var_set.lookup (v);
- if (old_state == 0) {
- // first used in optional part
- context.parent_context.var_set.insert (v, VariableState.OPTIONAL);
- select.append_printf ("t%d_g.%s", right_index, v.sql_expression);
-
- if (v.binding.data_type == PropertyType.DATETIME) {
- select.append_printf (", t%d_g.%s", right_index, v.get_extra_sql_expression ("localDate"));
- select.append_printf (", t%d_g.%s", right_index, v.get_extra_sql_expression ("localTime"));
- }
- } else {
- if (first_common) {
- sql.append (" ON ");
- first_common = false;
- } else {
- sql.append (" AND ");
- }
-
- if (old_state == VariableState.BOUND) {
- // variable definitely bound in non-optional part
- 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);
-
- if (v.binding.data_type == PropertyType.DATETIME) {
- select.append_printf (", t%d_g.%s", left_index, v.get_extra_sql_expression ("localDate"));
- select.append_printf (", t%d_g.%s", left_index, v.get_extra_sql_expression ("localTime"));
- }
- } else if (old_state == VariableState.OPTIONAL) {
- // variable maybe bound in non-optional part
- 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);
-
- if (v.binding.data_type == PropertyType.DATETIME) {
- select.append_printf (", COALESCE (t%d_g.%s, t%d_g.%s) AS %s", left_index, v.get_extra_sql_expression ("localDate"), right_index, v.get_extra_sql_expression ("localDate"), v.get_extra_sql_expression ("localDate"));
- select.append_printf (", COALESCE (t%d_g.%s, t%d_g.%s) AS %s", left_index, v.get_extra_sql_expression ("localTime"), right_index, v.get_extra_sql_expression ("localTime"), v.get_extra_sql_expression ("localTime"));
- }
- }
- }
- }
- foreach (var v in context.parent_context.var_set.get_keys ()) {
- if (context.var_set.lookup (v) == 0) {
- // only used in non-optional part
- if (first) {
- first = false;
- } else {
- select.append (", ");
- }
-
- select.append_printf ("t%d_g.%s", left_index, v.sql_expression);
-
- if (v.binding.data_type == PropertyType.DATETIME) {
- select.append_printf (", t%d_g.%s", left_index, v.get_extra_sql_expression ("localDate"));
- select.append_printf (", t%d_g.%s", left_index, v.get_extra_sql_expression ("localTime"));
- }
- }
- }
- if (first) {
- // no variables used at all
- select.append ("1");
- }
-
- context = context.parent_context;
-
- select.append (" FROM (");
- sql.insert (group_graph_pattern_start, select.str);
-
- // surround with SELECT * FROM (...) to avoid ambiguous column names
- // in SQL generated for FILTER (triggered by using table aliases for join sources)
- sql.insert (group_graph_pattern_start, "SELECT * FROM (");
- sql.append (")");
- }
- } else if (accept (SparqlTokenType.GRAPH)) {
- var old_graph = current_graph;
- var old_graph_is_var = current_graph_is_var;
- current_graph = parse_var_or_term (sql, out current_graph_is_var);
-
- if (!in_triples_block && !in_group_graph_pattern) {
- in_group_graph_pattern = true;
-
- sql.insert (group_graph_pattern_start, "SELECT * FROM (");
- translate_group_or_union_graph_pattern (sql);
- sql.append (")");
- } else {
- if (in_triples_block) {
- end_triples_block (sql, ref first_where, in_group_graph_pattern);
- in_triples_block = false;
- }
- if (!in_group_graph_pattern) {
- in_group_graph_pattern = true;
- }
-
- sql.insert (group_graph_pattern_start, "SELECT * FROM (");
- sql.append (") NATURAL INNER JOIN (");
- translate_group_or_union_graph_pattern (sql);
- sql.append (")");
- }
-
- current_graph = old_graph;
- current_graph_is_var = old_graph_is_var;
- } else if (accept (SparqlTokenType.BIND)) {
- var binding = new VariableBinding ();
- var bind_sql = new StringBuilder ();
-
- expect (SparqlTokenType.OPEN_PARENS);
-
- // We only need the binding sql expression when
- // we are not in a group graph pattern, both
- // cases are handled differently below.
- context.need_binding_expression = !in_group_graph_pattern;
- expression.translate_expression (bind_sql);
- context.need_binding_expression = false;
- binding.sql_expression = bind_sql.str;
-
- expect (SparqlTokenType.AS);
- expect (SparqlTokenType.VAR);
-
- var as_var = context.get_variable (get_last_string ().substring (1));
-
- if (as_var.binding != null) {
- throw query.get_internal_error ("Expected undefined variable in BIND alias");
- }
-
- binding.variable = as_var;
-
- if (in_group_graph_pattern) {
- // Surround the entire group graph pattern with
- // SELECT $binding , * FROM (...)
- var binding_sql = new StringBuilder ("SELECT ");
- add_variable_binding (binding_sql, binding, VariableState.BOUND);
- binding_sql.append (" * FROM (");
- sql.insert (group_graph_pattern_start, binding_sql.str);
- sql.append (")");
- } else {
- // This is the "simple" case, where we are
- // still constructing the SELECT ... part,
- // just add the binding sql in this case.
- add_variable_binding (sql, binding, VariableState.BOUND);
- }
-
- expect (SparqlTokenType.CLOSE_PARENS);
- } else if (current () == SparqlTokenType.OPEN_BRACE) {
- if (!in_triples_block && !in_group_graph_pattern) {
- in_group_graph_pattern = true;
-
- sql.insert (group_graph_pattern_start, "SELECT * FROM (");
- translate_group_or_union_graph_pattern (sql);
- sql.append (")");
- } else {
- if (in_triples_block) {
- end_triples_block (sql, ref first_where, in_group_graph_pattern);
- in_triples_block = false;
- }
- if (!in_group_graph_pattern) {
- in_group_graph_pattern = true;
- }
-
- sql.insert (group_graph_pattern_start, "SELECT * FROM (");
- sql.append (") NATURAL INNER JOIN (");
- translate_group_or_union_graph_pattern (sql);
- sql.append (")");
- }
- } else if (current () == SparqlTokenType.FILTER) {
- filters += get_location ();
- skip_filter ();
- } else {
- break;
- }
-
- optional (SparqlTokenType.DOT);
-
- // optional TriplesBlock
- parse_triples (sql, group_graph_pattern_start, ref in_triples_block, ref first_where, ref in_group_graph_pattern, found_simple_optional);
- }
-
- expect (SparqlTokenType.CLOSE_BRACE);
-
- if (!in_triples_block && !in_group_graph_pattern) {
- // empty graph pattern => return one result without bound variables
- sql.append ("SELECT 1");
- } else if (in_triples_block) {
- end_triples_block (sql, ref first_where, in_group_graph_pattern);
- in_triples_block = false;
- }
-
- if (in_group_graph_pattern) {
- first_where = true;
- }
-
- // handle filters last, they apply to the pattern as a whole
- if (filters.length > 0) {
- var end = get_location ();
-
- foreach (var filter_location in filters) {
- if (!first_where) {
- sql.append (" AND ");
- } else {
- sql.append (" WHERE ");
- first_where = false;
- }
-
- set_location (filter_location);
- translate_filter (sql);
- }
-
- set_location (end);
- }
-
- context = context.parent_context;
- return result;
- }
-
- private void translate_group_or_union_graph_pattern (StringBuilder sql) throws Sparql.Error {
- Variable[] all_vars = { };
- HashTable<Variable,int> all_var_set = new HashTable<Variable,int>.full (Variable.hash, Variable.equal, g_object_unref, null);
-
- Context[] contexts = { };
- long[] offsets = { };
-
- do {
- offsets += sql.len;
- contexts += translate_group_graph_pattern (sql);
- } while (accept (SparqlTokenType.UNION));
-
- if (contexts.length > 1) {
- // union graph pattern
-
- // create union of all variables
- foreach (var sub_context in contexts) {
- foreach (var v in sub_context.var_set.get_keys ()) {
- if (all_var_set.lookup (v) == 0) {
- all_vars += v;
- all_var_set.insert (v, VariableState.BOUND);
- context.var_set.insert (v, VariableState.BOUND);
- }
- }
- }
-
- long extra_offset = 0;
- for (int i = 0; i < contexts.length; i++) {
- var projection = new StringBuilder ();
- if (i > 0) {
- projection.append (") UNION ALL ");
- }
- projection.append ("SELECT ");
- foreach (var v in all_vars) {
- if (contexts[i].var_set.lookup (v) == 0) {
- // variable not used in this subgraph
- // use NULL
- projection.append ("NULL AS ");
- }
- projection.append_printf ("%s, ", v.sql_expression);
- }
- // delete last comma and space
- projection.truncate (projection.len - 2);
- projection.append (" FROM (");
-
- sql.insert (offsets[i] + extra_offset, projection.str);
- extra_offset += projection.len;
- }
- sql.append (")");
- } else {
- foreach (var key in contexts[0].var_set.get_keys ()) {
- context.var_set.insert (key, VariableState.BOUND);
- }
- }
- }
-
- private VariableBindingList? get_variable_binding_list (Variable variable) {
- VariableBindingList binding_list = null;
- if (triple_context != null) {
- binding_list = triple_context.var_bindings.lookup (variable);
- }
- if (binding_list == null && variable.binding != null) {
- // might be in scalar subquery: check variables of outer queries
- var current_context = context;
- while (current_context != null) {
- // only allow access to variables of immediate parent context of the subquery
- // allowing access to other variables leads to invalid SQL or wrong results
- if (current_context.scalar_subquery && current_context.parent_context.var_set.lookup (variable) != 0) {
- // capture outer variable
- var binding = new VariableBinding ();
- binding.data_type = variable.binding.data_type;
- binding.variable = context.get_variable (variable.name);
- binding.type = variable.binding.type;
- binding.sql_expression = variable.sql_expression;
- binding_list = new VariableBindingList ();
- if (triple_context != null) {
- triple_context.variables.append (variable);
- triple_context.var_bindings.insert (variable, binding_list);
- }
-
- context.var_set.insert (variable, VariableState.BOUND);
- binding_list.list.append (binding);
- break;
- }
- current_context = current_context.parent_context;
- }
- }
- return binding_list;
- }
-
- internal void add_variable_binding (StringBuilder sql, VariableBinding binding, VariableState variable_state) {
- var binding_list = get_variable_binding_list (binding.variable);
- if (binding_list == null) {
- binding_list = new VariableBindingList ();
- if (triple_context != null) {
- triple_context.variables.append (binding.variable);
- triple_context.var_bindings.insert (binding.variable, binding_list);
- }
-
- sql.append_printf ("%s AS %s, ",
- binding.sql_expression,
- binding.variable.sql_expression);
-
- if (binding.data_type == PropertyType.DATETIME) {
- sql.append_printf ("%s AS %s, ",
- binding.get_extra_sql_expression ("localDate"),
- binding.variable.get_extra_sql_expression ("localDate"));
- sql.append_printf ("%s AS %s, ",
- binding.get_extra_sql_expression ("localTime"),
- binding.variable.get_extra_sql_expression ("localTime"));
- }
-
- context.var_set.insert (binding.variable, variable_state);
- }
- binding_list.list.append (binding);
- if (binding.variable.binding == null) {
- binding.variable.binding = binding;
- }
- }
-
- private void parse_object (StringBuilder sql, bool in_simple_optional = false) throws Sparql.Error {
- long begin_sql_len = sql.len;
-
- bool object_is_var;
- string object = parse_var_or_term (sql, out object_is_var);
-
- string db_table = null;
- bool rdftype = false;
- bool share_table = true;
- bool is_fts_match = false;
-
- bool newtable;
- DataTable table;
- Property prop = null;
-
- Class subject_type = null;
-
- var ontologies = manager.get_ontologies ();
-
- if (!current_predicate_is_var) {
- prop = ontologies.get_property_by_uri (current_predicate);
-
- if (current_predicate == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
- && !object_is_var && current_graph == null) {
- // rdf:type query
- // avoid special casing if GRAPH is used as graph matching is not supported when using class tables
- rdftype = true;
- var cl = ontologies.get_class_by_uri (object);
- if (cl == null) {
- throw new Sparql.Error.UNKNOWN_CLASS ("Unknown class `%s'".printf (object));
- }
- db_table = cl.name;
- subject_type = cl;
- } else if (prop == null) {
- if (current_predicate == "http://www.tracker-project.org/ontologies/fts#match") {
- // fts:match
- db_table = "fts5";
- share_table = false;
- is_fts_match = true;
- fts_subject = context.get_variable (current_subject);
- } else {
- throw new Sparql.Error.UNKNOWN_PROPERTY ("Unknown property `%s'".printf (current_predicate));
- }
- } else {
- if (current_predicate == "http://www.w3.org/2000/01/rdf-schema#domain"
- && current_subject_is_var
- && !object_is_var) {
- // rdfs:domain
- var domain = ontologies.get_class_by_uri (object);
- if (domain == null) {
- throw new Sparql.Error.UNKNOWN_CLASS ("Unknown class `%s'".printf (object));
- }
- var pv = context.predicate_variable_map.lookup (context.get_variable (current_subject));
- if (pv == null) {
- pv = new PredicateVariable (manager);
- context.predicate_variable_map.insert (context.get_variable (current_subject), pv);
- }
- pv.domain = domain;
- }
-
- if (current_subject_is_var) {
- // Domain specific index might be a possibility, let's check
- Variable v = context.get_variable (current_subject);
- VariableBindingList list = triple_context.var_bindings.lookup (v);
-
- if (list != null && list.list != null) {
- bool stop = false;
- foreach (Class cl in prop.get_domain_indexes ()) {
- foreach (VariableBinding b in list.list) {
- if (b.type == cl) {
- db_table = cl.name;
- stop = true;
- break;
- }
- }
- if (stop) {
- break;
- }
- }
- }
- }
-
- if (db_table == null)
- db_table = prop.table_name;
-
- if (prop.multiple_values) {
- // we can never share the table with multiple triples
- // for multi value properties as a property may consist of multiple rows
- share_table = false;
- }
- subject_type = prop.domain;
-
- if (in_simple_optional && context.var_set.lookup (context.get_variable (current_subject)) == 0) {
- // use subselect instead of join in simple optional where the subject is the unbound variable
- // this can only happen with inverse functional properties
- var binding = new VariableBinding ();
- binding.data_type = PropertyType.RESOURCE;
- binding.variable = context.get_variable (current_subject);
-
- assert (triple_context.var_bindings.lookup (binding.variable) == null);
- var binding_list = new VariableBindingList ();
- triple_context.variables.append (binding.variable);
- triple_context.var_bindings.insert (binding.variable, binding_list);
-
- // need to use table and column name for object, can't refer to variable in nested select
- var object_binding = triple_context.var_bindings.lookup (context.get_variable (object)).list.data;
-
- sql.append_printf ("(SELECT ID FROM \"%s\" WHERE \"%s\" = %s) AS %s, ",
- db_table,
- prop.name,
- object_binding.sql_expression,
- binding.variable.sql_expression);
-
- context.var_set.insert (binding.variable, VariableState.OPTIONAL);
- binding_list.list.append (binding);
-
- assert (binding.variable.binding == null);
- binding.variable.binding = binding;
-
- return;
- }
- }
- table = get_table (current_subject, db_table, share_table, out newtable);
- } else {
- // variable in predicate
- newtable = true;
- table = new DataTable ();
- table.predicate_variable = context.predicate_variable_map.lookup (context.get_variable (current_predicate));
- if (table.predicate_variable == null) {
- table.predicate_variable = new PredicateVariable (manager);
- context.predicate_variable_map.insert (context.get_variable (current_predicate), table.predicate_variable);
- }
- if (!current_subject_is_var) {
- // single subject
- table.predicate_variable.subject = current_subject;
- }
- if (!object_is_var) {
- // single object
- table.predicate_variable.object = object;
- }
- if (current_graph != null) {
- table.predicate_variable.return_graph = true;
- }
- table.sql_query_tablename = current_predicate + (++counter).to_string ();
- triple_context.tables.append (table);
-
- // add to variable list
- var binding = new VariableBinding ();
- binding.data_type = PropertyType.RESOURCE;
- binding.variable = context.get_variable (current_predicate);
- binding.table = table;
- binding.sql_db_column_name = "predicate";
-
- add_variable_binding (sql, binding, VariableState.BOUND);
- }
-
- if (newtable) {
- if (current_subject_is_var) {
- var binding = new VariableBinding ();
- binding.data_type = PropertyType.RESOURCE;
- binding.variable = context.get_variable (current_subject);
- binding.table = table;
- binding.type = subject_type;
- if (is_fts_match) {
- binding.sql_db_column_name = "rowid";
- } else {
- binding.sql_db_column_name = "ID";
- }
-
- add_variable_binding (sql, binding, VariableState.BOUND);
- } else {
- var binding = new LiteralBinding ();
- binding.data_type = PropertyType.RESOURCE;
- binding.literal = current_subject;
- // binding.data_type = triple.subject.type;
- binding.table = table;
- binding.sql_db_column_name = "ID";
- triple_context.bindings.append (binding);
- }
- }
-
- if (!rdftype) {
- if (object_is_var) {
- var binding = new VariableBinding ();
- binding.variable = context.get_variable (object);
- binding.table = table;
- if (prop != null) {
-
- binding.type = prop.range;
-
- binding.data_type = prop.data_type;
- binding.sql_db_column_name = prop.name;
- if (!prop.multiple_values) {
- // for single value properties, row may have NULL
- // in any column except the ID column
- binding.maybe_null = true;
- binding.in_simple_optional = in_simple_optional;
- }
- } else {
- // variable as predicate
- binding.data_type = PropertyType.STRING;
- binding.sql_db_column_name = "object";
- binding.maybe_null = true;
- }
-
- VariableState state;
- if (in_simple_optional) {
- state = VariableState.OPTIONAL;
- } else {
- state = VariableState.BOUND;
- }
-
- add_variable_binding (sql, binding, state);
- } else if (is_fts_match) {
- var binding = new LiteralBinding ();
- binding.is_fts_match = true;
- binding.literal = object;
- // binding.data_type = triple.object.type;
- binding.table = table;
- binding.sql_db_column_name = "fts5";
- triple_context.bindings.append (binding);
-
- sql.append_printf ("\"%s\".\"rowid\" AS \"ID\", ",
- binding.table.sql_query_tablename);
- sql.append_printf ("\"%s\".\"rank\" AS \"%s_u_rank\", ",
- binding.table.sql_query_tablename,
- context.get_variable (current_subject).name);
- } else {
- var binding = new LiteralBinding ();
- binding.literal = object;
- // binding.data_type = triple.object.type;
- binding.table = table;
- if (prop != null) {
- binding.data_type = prop.data_type;
- binding.sql_db_column_name = prop.name;
- } else {
- // variable as predicate
- binding.sql_db_column_name = "object";
- }
- triple_context.bindings.append (binding);
- }
-
- if (current_graph != null) {
- if (current_graph_is_var) {
- var binding = new VariableBinding ();
- binding.variable = context.get_variable (current_graph);
- binding.table = table;
- binding.data_type = PropertyType.RESOURCE;
-
- if (prop != null) {
- binding.sql_db_column_name = prop.name + ":graph";
- } else {
- // variable as predicate
- binding.sql_db_column_name = "graph";
- }
-
- binding.maybe_null = true;
- binding.in_simple_optional = in_simple_optional;
-
- VariableState state;
- if (in_simple_optional) {
- state = VariableState.OPTIONAL;
- } else {
- state = VariableState.BOUND;
- }
-
- add_variable_binding (sql, binding, state);
- } else {
- var binding = new LiteralBinding ();
- binding.literal = current_graph;
- binding.table = table;
- binding.data_type = PropertyType.RESOURCE;
-
- if (prop != null) {
- binding.sql_db_column_name = prop.name + ":graph";
- } else {
- // variable as predicate
- binding.sql_db_column_name = "graph";
- }
-
- triple_context.bindings.append (binding);
- }
- }
- }
-
- if (sql.len == begin_sql_len) {
- // no SELECT expression was added, add dummy expression
- // this is required in cases where no values need to be retrieved
- sql.append ("1, ");
- }
- }
-
- private DataTable get_table (string subject, string db_table, bool share_table, out bool newtable) {
- string tablestring = "%s.%s".printf (subject, db_table);
- DataTable table = null;
- newtable = false;
- if (share_table) {
- table = triple_context.table_map.lookup (tablestring);
- }
- if (table == null) {
- newtable = true;
- table = new DataTable ();
- table.sql_db_tablename = db_table;
- table.sql_query_tablename = db_table + (++counter).to_string ();
- triple_context.tables.append (table);
- triple_context.table_map.insert (tablestring, table);
- }
- return table;
- }
-}
diff --git a/src/libtracker-data/tracker-sparql-query.vala b/src/libtracker-data/tracker-sparql-query.vala
deleted file mode 100644
index 54ce57003..000000000
--- a/src/libtracker-data/tracker-sparql-query.vala
+++ /dev/null
@@ -1,1102 +0,0 @@
-/*
- * Copyright (C) 2008-2010, Nokia
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-namespace Tracker.Sparql {
- enum VariableState {
- NONE,
- BOUND,
- OPTIONAL
- }
-
- enum UpdateType {
- DELETE,
- INSERT,
- UPDATE
- }
-
- // Represents a SQL table
- class DataTable : Object {
- public string sql_db_tablename; // as in db schema
- public string sql_query_tablename; // temp. name, generated
- public PredicateVariable predicate_variable;
- }
-
- abstract class DataBinding : Object {
- public PropertyType data_type;
- public DataTable table;
- public string sql_db_column_name;
- public string sql_expression {
- get {
- if (this._sql_expression == null && table != null) {
- this._sql_expression = "\"%s\".\"%s\"".printf (table.sql_query_tablename, sql_db_column_name);
- }
- return this._sql_expression;
- }
- set {
- this._sql_expression = value;
- }
- }
- string? _sql_expression;
- public string get_extra_sql_expression (string suffix) {
- return "\"%s\".\"%s:%s\"".printf (table.sql_query_tablename, sql_db_column_name, suffix);
- }
- }
-
- // Represents a mapping of a SPARQL literal to a SQL table and column
- class LiteralBinding : DataBinding {
- public bool is_fts_match;
- public string literal;
- }
-
- // Represents a mapping of a SPARQL variable to a SQL table and column
- class VariableBinding : DataBinding {
- public weak Variable variable;
- // Specified whether SQL column may contain NULL entries
- public bool maybe_null;
- public bool in_simple_optional;
- public Class? type;
- }
-
- class VariableBindingList : Object {
- public List<VariableBinding> list;
- }
-
- class Variable : Object {
- public string name { get; private set; }
- public int index { get; private set; }
- public string sql_expression { get; private set; }
- public VariableBinding binding;
- string sql_identifier;
-
- public Variable (string name, int index) {
- this.name = name;
- this.index = index;
- this.sql_identifier = "%d_u".printf (index);
- this.sql_expression = "\"%s\"".printf (sql_identifier);
- }
-
- public string get_extra_sql_expression (string suffix) {
- return "\"%s:%s\"".printf (sql_identifier, suffix);
- }
-
- public static bool equal (Variable a, Variable b) {
- return a.index == b.index;
- }
-
- public static uint hash (Variable variable) {
- return (uint) variable.index;
- }
- }
-
- class Context {
- public weak Query query;
-
- public Context? parent_context;
- // All SPARQL variables within a subgraph pattern (used by UNION)
- // value is VariableState
- public HashTable<Variable,int> var_set;
-
- public HashTable<string,Variable> var_map;
- // All selected SPARQL variables (used by compositional subqueries)
- public HashTable<Variable,int> select_var_set;
-
- // Variables used as predicates
- public HashTable<Variable,PredicateVariable> predicate_variable_map;
-
- public bool scalar_subquery;
- public bool need_binding_expression;
-
- public Context (Query query, Context? parent_context = null) {
- this.query = query;
- this.parent_context = parent_context;
- this.var_set = new HashTable<Variable,int>.full (Variable.hash, Variable.equal, g_object_unref, null);
-
- if (parent_context == null) {
- select_var_set = new HashTable<Variable,int>.full (Variable.hash, Variable.equal, g_object_unref, null);
- var_map = new HashTable<string,Variable>.full (str_hash, str_equal, g_free, g_object_unref);
- predicate_variable_map = new HashTable<Variable,PredicateVariable>.full (Variable.hash, Variable.equal, g_object_unref, g_object_unref);
- } else {
- select_var_set = parent_context.select_var_set;
- var_map = parent_context.var_map;
- predicate_variable_map = parent_context.predicate_variable_map;
- }
- }
-
- public Context.subquery (Query query, Context parent_context) {
- this.query = query;
- this.parent_context = parent_context;
- this.var_set = new HashTable<Variable,int>.full (Variable.hash, Variable.equal, g_object_unref, null);
-
- select_var_set = new HashTable<Variable,int>.full (Variable.hash, Variable.equal, g_object_unref, null);
- var_map = parent_context.var_map;
- predicate_variable_map = new HashTable<Variable,PredicateVariable>.full (Variable.hash, Variable.equal, g_object_unref, g_object_unref);
- scalar_subquery = true;
- }
-
- internal unowned Variable get_variable (string name) {
- unowned Variable result = this.var_map.lookup (name);
- if (result == null) {
- var variable = new Variable (name, ++query.last_var_index);
- this.var_map.insert (name, variable);
-
- result = variable;
- }
- return result;
- }
- }
-
- class SelectContext : Context {
- public PropertyType type;
- public PropertyType[] types = {};
- public string[] variable_names = {};
-
- public SelectContext (Query query, Context? parent_context = null) {
- base (query, parent_context);
- }
-
- public SelectContext.subquery (Query query, Context parent_context) {
- base.subquery (query, parent_context);
- }
- }
-
- class Solution {
- public HashTable<string,int?> hash;
- public GenericArray<string> values;
- public int solution_index;
-
- public Solution () {
- this.hash = new HashTable<string,int?> (str_hash, str_equal);
- this.values = new GenericArray<string> ();
- }
-
- public string? lookup (string variable_name) {
- int? variable_index = hash.get (variable_name);
- if (variable_index == null) {
- return null;
- }
- return values[solution_index * hash.size () + variable_index];
- }
- }
-}
-
-public class Tracker.Sparql.Query : Object {
- SparqlScanner scanner;
-
- // token buffer
- TokenInfo[] tokens;
- // index of current token in buffer
- int index;
- // number of tokens in buffer
- int size;
-
- const int BUFFER_SIZE = 32;
-
- struct TokenInfo {
- public SparqlTokenType type;
- public SourceLocation begin;
- public SourceLocation end;
- }
-
- const string FN_NS = "http://www.w3.org/2005/xpath-functions#";
-
- string query_string;
- bool update_extensions;
-
- internal Expression expression;
- internal Pattern pattern;
-
- string current_graph;
- string current_subject;
- bool current_subject_is_var;
- string current_predicate;
- bool current_predicate_is_var;
-
- // SILENT => ignore (non-syntax) errors
- bool silent;
-
- HashTable<string,string> prefix_map;
-
- // All SPARQL literals
- internal List<LiteralBinding> bindings;
-
- internal Context context;
-
- int bnodeid = 0;
- // base UUID used for blank nodes
- uchar[] base_uuid;
- HashTable<string,string> blank_nodes;
-
- public Data.Manager manager;
-
- // Keep track of used SQL identifiers for SPARQL variables
- public int last_var_index;
-
- public bool no_cache { get; set; }
-
- public Query (Data.Manager manager, string query) {
- no_cache = false; /* Start with false, expression sets it */
- tokens = new TokenInfo[BUFFER_SIZE];
- prefix_map = new HashTable<string,string>.full (str_hash, str_equal, g_free, g_free);
-
- base_uuid = new uchar[16];
- uuid_generate (base_uuid);
-
- this.query_string = query;
- this.manager = manager;
-
- expression = new Expression (this);
- pattern = new Pattern (this);
- }
-
- public Query.update (Data.Manager manager, string query) {
- this (manager, query);
- this.update_extensions = true;
- }
-
- string get_uuid_for_name (uchar[] base_uuid, string name) {
- var checksum = new Checksum (ChecksumType.SHA1);
- // base UUID, unique per file
- checksum.update (base_uuid, 16);
-
- // node ID
- checksum.update ((uchar[]) name, -1);
-
- string sha1 = checksum.get_string ();
-
- // generate name based uuid
- return "urn:uuid:%.8s-%.4s-%.4s-%.4s-%.12s".printf (
- sha1, sha1.substring (8), sha1.substring (12), sha1.substring (16), sha1.substring (20));
- }
-
- internal string generate_bnodeid (string? user_bnodeid) {
- // user_bnodeid is NULL for anonymous nodes
- if (user_bnodeid == null) {
- return ":%d".printf (++bnodeid);
- } else {
- string uri = null;
-
- if (blank_nodes != null) {
- uri = blank_nodes.lookup (user_bnodeid);
- if (uri != null) {
- return uri;
- }
- }
-
- uri = get_uuid_for_name (base_uuid, user_bnodeid);
-
- if (blank_nodes != null) {
- var iface = manager.get_db_interface ();
- while (Data.query_resource_id (manager, iface, uri) > 0) {
- // uri collision, generate new UUID
- uchar[] new_base_uuid = new uchar[16];
- uuid_generate (new_base_uuid);
- uri = get_uuid_for_name (new_base_uuid, user_bnodeid);
- }
-
- blank_nodes.insert (user_bnodeid, uri);
- }
-
- return uri;
- }
- }
-
- internal bool next () throws Sparql.Error {
- index = (index + 1) % BUFFER_SIZE;
- size--;
- if (size <= 0) {
- SourceLocation begin, end;
- SparqlTokenType type = scanner.read_token (out begin, out end);
- tokens[index].type = type;
- tokens[index].begin = begin;
- tokens[index].end = end;
- size = 1;
- }
- return (tokens[index].type != SparqlTokenType.EOF);
- }
-
- internal SparqlTokenType current () {
- return tokens[index].type;
- }
-
- internal SparqlTokenType last () {
- int last_index = (index + BUFFER_SIZE - 1) % BUFFER_SIZE;
- return tokens[last_index].type;
- }
-
- internal bool accept (SparqlTokenType type) throws Sparql.Error {
- if (current () == type) {
- next ();
- return true;
- }
- return false;
- }
-
- internal void optional (SparqlTokenType type) throws Sparql.Error {
- if (current () == type)
- next ();
- }
-
- internal Sparql.Error get_error (string msg) {
- return new Sparql.Error.PARSE ("%d.%d: syntax error, %s".printf (tokens[index].begin.line, tokens[index].begin.column, msg));
- }
-
- internal Sparql.Error get_internal_error (string msg) {
- return new Sparql.Error.INTERNAL ("%d.%d: %s".printf (tokens[index].begin.line, tokens[index].begin.column, msg));
- }
-
- internal bool expect (SparqlTokenType type) throws Sparql.Error {
- if (accept (type)) {
- return true;
- }
-
- throw get_error ("expected %s".printf (type.to_string ()));
- }
-
- internal SourceLocation get_location () {
- return tokens[index].begin;
- }
-
- internal void set_location (SourceLocation location) {
- scanner.seek (location);
- size = 0;
- index = 0;
- try {
- next ();
- } catch (Sparql.Error e) {
- // this should never happen as this is the second time we scan this token
- critical ("internal error: next in set_location failed");
- }
- }
-
- internal string get_last_string (int strip = 0) {
- int last_index = (index + BUFFER_SIZE - 1) % BUFFER_SIZE;
- return ((string) (tokens[last_index].begin.pos + strip)).substring (0, (int) (tokens[last_index].end.pos - tokens[last_index].begin.pos - 2 * strip));
- }
-
- private void parse_prologue () throws Sparql.Error {
- if (accept (SparqlTokenType.BASE)) {
- expect (SparqlTokenType.IRI_REF);
- }
- while (accept (SparqlTokenType.PREFIX)) {
- string ns = "";
- if (accept (SparqlTokenType.PN_PREFIX)) {
- ns = get_last_string ();
- }
- expect (SparqlTokenType.COLON);
- expect (SparqlTokenType.IRI_REF);
- string uri = get_last_string (1);
- prefix_map.insert (ns, uri);
- }
- }
-
- private void prepare_execute () throws DBInterfaceError, Sparql.Error, DateError {
- assert (!update_extensions);
-
- scanner = new SparqlScanner ((char*) query_string, (long) query_string.length);
- next ();
-
- // declare fn prefix for XPath functions
- prefix_map.insert ("fn", FN_NS);
- var ontologies = manager.get_ontologies ();
-
- foreach (Namespace ns in ontologies.get_namespaces ()) {
- if (ns.prefix == null) {
- critical ("Namespace does not specify a prefix: %s", ns.uri);
- continue;
- }
- prefix_map.insert (ns.prefix, ns.uri);
- }
-
- parse_prologue ();
- }
-
-
- public DBCursor? execute_cursor () throws DBInterfaceError, Sparql.Error, DateError {
-
- prepare_execute ();
-
- switch (current ()) {
- case SparqlTokenType.SELECT:
- return execute_select_cursor ();
- case SparqlTokenType.CONSTRUCT:
- throw get_internal_error ("CONSTRUCT is not supported");
- case SparqlTokenType.DESCRIBE:
- throw get_internal_error ("DESCRIBE is not supported");
- case SparqlTokenType.ASK:
- return execute_ask_cursor ();
- case SparqlTokenType.INSERT:
- case SparqlTokenType.DELETE:
- case SparqlTokenType.DROP:
- throw get_error ("INSERT and DELETE are not supported in query mode");
- default:
- throw get_error ("expected SELECT or ASK");
- }
- }
-
- public Variant? execute_update (bool blank) throws GLib.Error {
- Variant result = null;
- assert (update_extensions);
-
- scanner = new SparqlScanner ((char*) query_string, (long) query_string.length);
- next ();
-
- // declare fn prefix for XPath functions
- prefix_map.insert ("fn", FN_NS);
- var ontologies = manager.get_ontologies ();
-
- foreach (Namespace ns in ontologies.get_namespaces ()) {
- if (ns.prefix == null) {
- critical ("Namespace does not specify a prefix: %s", ns.uri);
- continue;
- }
- prefix_map.insert (ns.prefix, ns.uri);
- }
-
- parse_prologue ();
-
- // SPARQL update supports multiple operations in a single query
- VariantBuilder? ublank_nodes = null;
-
- if (blank) {
- ublank_nodes = new VariantBuilder ((VariantType) "aaa{ss}");
- }
-
- while (current () != SparqlTokenType.EOF) {
- switch (current ()) {
- case SparqlTokenType.WITH:
- case SparqlTokenType.INSERT:
- case SparqlTokenType.DELETE:
- if (blank) {
- ublank_nodes.open ((VariantType) "aa{ss}");
- execute_insert_delete (ublank_nodes);
- ublank_nodes.close ();
- } else {
- execute_insert_delete (null);
- }
- break;
- case SparqlTokenType.DROP:
- throw get_internal_error ("DROP GRAPH is not supported");
- case SparqlTokenType.SELECT:
- case SparqlTokenType.CONSTRUCT:
- case SparqlTokenType.DESCRIBE:
- case SparqlTokenType.ASK:
- throw get_error ("SELECT, CONSTRUCT, DESCRIBE, and ASK are not supported in update mode");
- default:
- throw get_error ("expected INSERT or DELETE");
- }
-
- // semicolon is used to separate multiple operations in the current SPARQL Update draft
- // keep it optional for now to reatin backward compatibility
- optional (SparqlTokenType.SEMICOLON);
- }
-
- if (blank) {
- result = ublank_nodes.end ();
- }
-
- return result;
- }
-
- private DBStatement prepare_for_exec (DBInterface iface, string sql) throws DBInterfaceError, Sparql.Error, DateError {
- var stmt = iface.create_statement (no_cache ? DBStatementCacheType.NONE : DBStatementCacheType.SELECT, "%s", sql);
-
- // set literals specified in query
- int i = 0;
- foreach (LiteralBinding binding in bindings) {
- if (binding.data_type == PropertyType.BOOLEAN) {
- if (binding.literal == "true" || binding.literal == "1") {
- stmt.bind_int (i, 1);
- } else if (binding.literal == "false" || binding.literal == "0") {
- stmt.bind_int (i, 0);
- } else {
- throw new Sparql.Error.TYPE ("`%s' is not a valid boolean".printf (binding.literal));
- }
- } else if (binding.data_type == PropertyType.DATE) {
- stmt.bind_int (i, (int) string_to_date (binding.literal + "T00:00:00Z", null));
- } else if (binding.data_type == PropertyType.DATETIME) {
- stmt.bind_double (i, string_to_date (binding.literal, null));
- } else if (binding.data_type == PropertyType.INTEGER) {
- stmt.bind_int (i, int.parse (binding.literal));
- } else {
- stmt.bind_text (i, binding.literal);
- }
- i++;
- }
-
- return stmt;
- }
-
- private DBCursor? exec_sql_cursor (DBInterface iface, string sql, PropertyType[]? types, string[]? variable_names) throws DBInterfaceError, Sparql.Error, DateError {
- var stmt = prepare_for_exec (iface, sql);
-
- return stmt.start_sparql_cursor (types, variable_names);
- }
-
- private string get_select_query (out SelectContext context) throws DBInterfaceError, Sparql.Error, DateError {
- // SELECT query
-
- // build SQL
- var sql = new StringBuilder ();
- context = pattern.translate_select (sql);
-
- expect (SparqlTokenType.EOF);
-
- return sql.str;
- }
-
- private DBCursor? execute_select_cursor () throws DBInterfaceError, Sparql.Error, DateError {
- SelectContext context;
- string sql = get_select_query (out context);
- var iface = manager.get_db_interface ();
-
- return exec_sql_cursor (iface, sql, context.types, context.variable_names);
- }
-
- private string get_ask_query () throws DBInterfaceError, Sparql.Error, DateError {
- // ASK query
-
- var pattern_sql = new StringBuilder ();
-
- // build SQL
- var sql = new StringBuilder ();
- sql.append ("SELECT CASE EXISTS ( ");
-
- expect (SparqlTokenType.ASK);
-
- optional (SparqlTokenType.WHERE);
-
- context = pattern.translate_group_graph_pattern (pattern_sql);
-
- // select from results of WHERE clause
- sql.append (pattern_sql.str);
- sql.append (" ) WHEN 1 THEN 'true' WHEN 0 THEN 'false' ELSE NULL END");
-
- if (accept (SparqlTokenType.GROUP) || accept (SparqlTokenType.ORDER) ||
- accept (SparqlTokenType.OFFSET) || accept (SparqlTokenType.LIMIT)) {
- throw get_error ("invalid use of %s in ASK".printf (last().to_string()));
- }
-
- expect (SparqlTokenType.EOF);
-
- context = context.parent_context;
-
- return sql.str;
- }
-
- private DBCursor? execute_ask_cursor () throws DBInterfaceError, Sparql.Error, DateError {
- var iface = manager.get_db_interface ();
- return exec_sql_cursor (iface, get_ask_query (), new PropertyType[] { PropertyType.BOOLEAN }, new string[] { "result" });
- }
-
- private void parse_from_or_into_param () throws Sparql.Error {
- if (accept (SparqlTokenType.IRI_REF)) {
- current_graph = get_last_string (1);
- } else if (accept (SparqlTokenType.PN_PREFIX)) {
- string ns = get_last_string ();
- expect (SparqlTokenType.COLON);
- current_graph = resolve_prefixed_name (ns, get_last_string ().substring (1));
- } else {
- expect (SparqlTokenType.COLON);
- current_graph = resolve_prefixed_name ("", get_last_string ().substring (1));
- }
- }
-
- private void execute_insert_delete (VariantBuilder? update_blank_nodes) throws GLib.Error {
- bool blank = true;
-
- // DELETE and/or INSERT
-
- if (accept (SparqlTokenType.WITH)) {
- parse_from_or_into_param ();
- } else {
- current_graph = null;
- }
-
- SourceLocation? delete_location = null;
- SourceLocation? insert_location = null;
- bool insert_is_update = false;
- bool delete_where = false;
- bool data = false;
-
- var data_update = manager.get_data ();
-
- // Sparql 1.1 defines deletes/inserts as a single
- // operation with the syntax:
- // [DELETE {...}] [INSERT {...}] WHERE {...}
-
- if (accept (SparqlTokenType.DELETE)) {
- blank = false;
-
- // SILENT => ignore (non-syntax) errors
- silent = accept (SparqlTokenType.SILENT);
-
- if (current_graph == null && accept (SparqlTokenType.FROM)) {
- parse_from_or_into_param ();
- }
-
- if (current_graph == null && accept (SparqlTokenType.DATA)) {
- // INSERT/DELETE DATA are simpler variants
- // that don't support variables
- data = true;
- } else if (accept (SparqlTokenType.WHERE)) {
- // DELETE WHERE is a short form where the pattern
- // is also used as the template for deletion
- delete_where = true;
- }
-
- delete_location = get_location ();
-
- if (!data && !delete_where) {
- skip_braces ();
- }
- }
-
- if (!data && !delete_where && accept (SparqlTokenType.INSERT)) {
- if (accept (SparqlTokenType.OR)) {
- expect (SparqlTokenType.REPLACE);
- insert_is_update = true;
- }
-
- if (!insert_is_update) {
- // SILENT => ignore (non-syntax) errors
- silent = accept (SparqlTokenType.SILENT);
- }
-
- if (current_graph == null && accept (SparqlTokenType.INTO)) {
- parse_from_or_into_param ();
- }
-
- if (current_graph == null && accept (SparqlTokenType.DATA)) {
- // INSERT/DELETE DATA are simpler variants
- // that don't support variables
- data = true;
- }
-
- if (current () != SparqlTokenType.OPEN_BRACE) {
- throw get_error ("Expected '{' beginning a quad data/pattern block");
- }
-
- insert_location = get_location ();
-
- if (!data) {
- skip_braces ();
- }
- }
-
- var pattern_sql = new StringBuilder ();
-
- var sql = new StringBuilder ();
-
- if (!data) {
- if (delete_where || accept (SparqlTokenType.WHERE)) {
- pattern.current_graph = current_graph;
- context = pattern.translate_group_graph_pattern (pattern_sql);
- pattern.current_graph = null;
- } else {
- context = new Context (this);
-
- pattern_sql.append ("SELECT 1");
- }
- } else {
- // WHERE pattern not supported for INSERT/DELETE DATA,
- // nor unbound values in the quad data.
- if (quad_data_unbound_var_count () > 0) {
- throw get_error ("INSERT/DELETE DATA do not allow unbound values");
- }
-
- context = new Context (this);
-
- pattern_sql.append ("SELECT 1");
- }
-
- var after_where = get_location ();
-
- var solution = new Solution ();
-
- // build SQL
- sql.append ("SELECT ");
- int var_idx = 0;
- foreach (var variable in context.var_set.get_keys ()) {
- if (var_idx > 0) {
- sql.append (", ");
- }
-
- if (variable.binding == null) {
- throw get_error ("use of undefined variable `%s'".printf (variable.name));
- }
- Expression.append_expression_as_string (sql, variable.sql_expression, variable.binding.data_type);
-
- solution.hash.insert (variable.name, var_idx++);
- }
-
- if (var_idx == 0) {
- sql.append ("1");
- }
-
- // select from results of WHERE clause
- sql.append (" FROM (");
- sql.append (pattern_sql.str);
- sql.append (")");
-
- var iface = manager.get_writable_db_interface ();
- var cursor = exec_sql_cursor (iface, sql.str, null, null);
-
- int n_solutions = 0;
- while (cursor.next ()) {
- // get values of all variables to be bound
- for (var_idx = 0; var_idx < solution.hash.size (); var_idx++) {
- solution.values.add (cursor.get_string (var_idx));
- }
- n_solutions++;
- }
-
- cursor = null;
-
- // Iterate over all solutions twice
- // First handle deletes
- if (delete_location != null) {
- for (int i = 0; i < n_solutions; i++) {
- solution.solution_index = i;
- set_location (delete_location);
- parse_construct_triples_block (solution, UpdateType.DELETE);
- data_update.update_buffer_might_flush ();
- }
-
- // Force flush on delete/insert operations,
- // so the elements are already removed at
- // the time of insertion.
- if (insert_location != null)
- data_update.update_buffer_flush ();
- }
-
- // Then handle inserts/updates
- if (insert_location != null) {
- for (int i = 0; i < n_solutions; i++) {
- uuid_generate (base_uuid);
- blank_nodes = new HashTable<string,string>.full (str_hash, str_equal, g_free, g_free);
- solution.solution_index = i;
-
- set_location (insert_location);
- parse_construct_triples_block (solution,
- insert_is_update ?
- UpdateType.UPDATE :
- UpdateType.INSERT);
-
- if (blank && update_blank_nodes != null) {
- update_blank_nodes.add_value (blank_nodes);
- }
-
- data_update.update_buffer_might_flush ();
- }
- }
-
- solution = null;
-
- if (!data) {
- // reset location to the end of the update
- set_location (after_where);
- }
-
- // ensure possible WHERE clause in next part gets the correct results
- data_update.update_buffer_flush ();
- bindings = null;
-
- context = context.parent_context;
- }
-
- internal string resolve_prefixed_name (string prefix, string local_name) throws Sparql.Error {
- string ns = prefix_map.lookup (prefix);
- if (ns == null) {
- throw get_error ("use of undefined prefix `%s'".printf (prefix));
- }
- return ns + local_name;
- }
-
- private int quad_data_unbound_var_count () throws Sparql.Error {
- SourceLocation current_pos = get_location ();
- int n_braces = 1;
- int n_unbound = 0;
-
- expect (SparqlTokenType.OPEN_BRACE);
- while (n_braces > 0) {
- if (accept (SparqlTokenType.OPEN_BRACE)) {
- n_braces++;
- } else if (accept (SparqlTokenType.CLOSE_BRACE)) {
- n_braces--;
- } else if (current () == SparqlTokenType.EOF) {
- throw get_error ("unexpected end of query, expected }");
- } else {
- if (current () == SparqlTokenType.VAR)
- n_unbound++;
- // ignore everything else
- next ();
- }
- }
-
- set_location (current_pos);
- return n_unbound;
- }
-
- private void skip_braces () throws Sparql.Error {
- expect (SparqlTokenType.OPEN_BRACE);
- int n_braces = 1;
- while (n_braces > 0) {
- if (accept (SparqlTokenType.OPEN_BRACE)) {
- n_braces++;
- } else if (accept (SparqlTokenType.CLOSE_BRACE)) {
- n_braces--;
- } else if (current () == SparqlTokenType.EOF) {
- throw get_error ("unexpected end of query, expected }");
- } else {
- // ignore everything else
- next ();
- }
- }
- }
-
- private void parse_construct_triples_block (Solution var_value_map, UpdateType type) throws Sparql.Error, DateError {
- expect (SparqlTokenType.OPEN_BRACE);
-
- while (current () != SparqlTokenType.CLOSE_BRACE) {
- bool is_null = false;
-
- if (accept (SparqlTokenType.GRAPH)) {
- var old_graph = current_graph;
- current_graph = parse_construct_var_or_term (var_value_map, type, out is_null);
-
- if (is_null) {
- throw get_error ("'null' not supported for graph");
- }
-
- expect (SparqlTokenType.OPEN_BRACE);
-
- while (current () != SparqlTokenType.CLOSE_BRACE) {
- current_subject = parse_construct_var_or_term (var_value_map, type, out is_null);
-
- if (is_null) {
- throw get_error ("'null' not supported for subject");
- }
-
- parse_construct_property_list_not_empty (var_value_map, type);
- if (!accept (SparqlTokenType.DOT)) {
- // no triples following
- break;
- }
- }
-
- expect (SparqlTokenType.CLOSE_BRACE);
-
- current_graph = old_graph;
-
- optional (SparqlTokenType.DOT);
- } else {
- current_subject = parse_construct_var_or_term (var_value_map, type, out is_null);
-
- if (is_null) {
- throw get_error ("'null' not supported for subject");
- }
-
- parse_construct_property_list_not_empty (var_value_map, type);
- if (!accept (SparqlTokenType.DOT) && current () != SparqlTokenType.GRAPH) {
- // neither GRAPH nor triples following
- break;
- }
- }
- }
-
- expect (SparqlTokenType.CLOSE_BRACE);
- }
-
- bool anon_blank_node_open = false;
-
- private string? parse_construct_var_or_term (Solution var_value_map, UpdateType type, out bool is_null) throws Sparql.Error, DateError {
- string result = "";
- is_null = false;
- if (current () == SparqlTokenType.VAR) {
- next ();
- result = var_value_map.lookup (get_last_string ().substring (1));
- } else if (current () == SparqlTokenType.IRI_REF) {
- next ();
- result = get_last_string (1);
- } else if (current () == SparqlTokenType.PN_PREFIX) {
- // prefixed name with namespace foo:bar
- next ();
- string ns = get_last_string ();
- expect (SparqlTokenType.COLON);
- result = resolve_prefixed_name (ns, get_last_string ().substring (1));
- } else if (current () == SparqlTokenType.COLON) {
- // prefixed name without namespace :bar
- next ();
- result = resolve_prefixed_name ("", get_last_string ().substring (1));
- } else if (accept (SparqlTokenType.BLANK_NODE)) {
- // _:foo
- expect (SparqlTokenType.COLON);
- result = generate_bnodeid (get_last_string ().substring (1));
- } else if (current () == SparqlTokenType.MINUS) {
- next ();
- if (current () == SparqlTokenType.INTEGER ||
- current () == SparqlTokenType.DECIMAL ||
- current () == SparqlTokenType.DOUBLE) {
- next ();
- result = "-" + get_last_string ();
- } else {
- throw get_error ("expected variable or term");
- }
- } else if (current () == SparqlTokenType.INTEGER) {
- next ();
- result = get_last_string ();
- } else if (current () == SparqlTokenType.NULL) {
- next ();
- result = "null";
- is_null = true;
- } else if (current () == SparqlTokenType.DECIMAL) {
- next ();
- result = get_last_string ();
- } else if (current () == SparqlTokenType.DOUBLE) {
- next ();
- result = get_last_string ();
- } else if (current () == SparqlTokenType.TRUE) {
- next ();
- result = "true";
- } else if (current () == SparqlTokenType.FALSE) {
- next ();
- result = "false";
- } else if (current () == SparqlTokenType.STRING_LITERAL1) {
- result = expression.parse_string_literal ();
- } else if (current () == SparqlTokenType.STRING_LITERAL2) {
- result = expression.parse_string_literal ();
- } else if (current () == SparqlTokenType.STRING_LITERAL_LONG1) {
- result = expression.parse_string_literal ();
- } else if (current () == SparqlTokenType.STRING_LITERAL_LONG2) {
- result = expression.parse_string_literal ();
- } else if (current () == SparqlTokenType.OPEN_BRACKET) {
-
- if (anon_blank_node_open) {
- throw get_error ("no support for nested anonymous blank nodes");
- }
-
- anon_blank_node_open = true;
- next ();
-
- result = generate_bnodeid (null);
-
- string old_subject = current_subject;
- bool old_subject_is_var = current_subject_is_var;
-
- current_subject = result;
- parse_construct_property_list_not_empty (var_value_map, type);
- expect (SparqlTokenType.CLOSE_BRACKET);
- anon_blank_node_open = false;
-
- current_subject = old_subject;
- current_subject_is_var = old_subject_is_var;
- } else {
- throw get_error ("expected variable or term");
- }
- return result;
- }
-
- private void parse_construct_property_list_not_empty (Solution var_value_map, UpdateType type) throws Sparql.Error, DateError {
- while (true) {
- var old_predicate = current_predicate;
-
- current_predicate = null;
- if (current () == SparqlTokenType.VAR) {
- current_predicate_is_var = true;
- next ();
- current_predicate = var_value_map.lookup (get_last_string ().substring (1));
- } else if (current () == SparqlTokenType.IRI_REF) {
- next ();
- current_predicate = get_last_string (1);
- } else if (current () == SparqlTokenType.PN_PREFIX) {
- next ();
- string ns = get_last_string ();
- expect (SparqlTokenType.COLON);
- current_predicate = resolve_prefixed_name (ns, get_last_string ().substring (1));
- } else if (current () == SparqlTokenType.COLON) {
- next ();
- current_predicate = resolve_prefixed_name ("", get_last_string ().substring (1));
- } else if (current () == SparqlTokenType.A) {
- next ();
- current_predicate = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";
- } else {
- throw get_error ("expected non-empty property list");
- }
- parse_construct_object_list (var_value_map, type);
-
- current_predicate = old_predicate;
-
- if (accept (SparqlTokenType.SEMICOLON)) {
- continue;
- }
- break;
- }
- }
-
- private void parse_construct_object_list (Solution var_value_map, UpdateType type) throws Sparql.Error, DateError {
- while (true) {
- parse_construct_object (var_value_map, type);
- if (accept (SparqlTokenType.COMMA)) {
- continue;
- }
- break;
- }
- }
-
- private void parse_construct_object (Solution var_value_map, UpdateType type) throws Sparql.Error, DateError {
- bool is_null = false;
- string object = parse_construct_var_or_term (var_value_map, type, out is_null);
- var data = manager.get_data ();
- if (current_subject == null || current_predicate == null || object == null) {
- // the SPARQL specification says that triples containing unbound variables
- // should be excluded from the output RDF graph of CONSTRUCT
- return;
- }
- try {
- if (type == UpdateType.UPDATE) {
- // update triple in database
- data.update_statement (current_graph, current_subject, current_predicate, is_null ? null : object);
- } else if (type == UpdateType.DELETE) {
- // delete triple from database
- if (is_null) {
- throw get_error ("'null' not supported in this mode");
- }
- data.delete_statement (current_graph, current_subject, current_predicate, object);
- } else if (type == UpdateType.INSERT) {
- // insert triple into database
- if (is_null) {
- throw get_error ("'null' not supported in this mode");
- }
- data.insert_statement (current_graph, current_subject, current_predicate, object);
- }
- } catch (Sparql.Error e) {
- if (!silent) {
- throw e;
- }
- } catch (DateError e) {
- if (!silent) {
- throw new Sparql.Error.TYPE (e.message);
- }
- }
- }
-
- [CCode (cname = "uuid_generate")]
- public extern static void uuid_generate ([CCode (array_length = false)] uchar[] uuid);
-}
-