summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Garnacho <carlosg@gnome.org>2023-01-10 15:19:01 +0000
committerCarlos Garnacho <carlosg@gnome.org>2023-01-10 15:19:01 +0000
commit84a01b7f0481e71cd9cd0b930c51b8a12a5c531c (patch)
tree037c609ce40c95a86e913f4e20c7b405d20707c5
parent8dbf74386f28871385f784d9873c5aeaecf5222e (diff)
parent857d8a4711f436fa207ffad638c83c4fcc31d90e (diff)
downloadtracker-84a01b7f0481e71cd9cd0b930c51b8a12a5c531c.tar.gz
Merge branch 'wip/carlosg/triples-table-perf' into 'master'
Performance improvements to tracker_triples virtual table See merge request https://gitlab.gnome.org/GNOME/tracker/-/merge_requests/562
-rw-r--r--src/libtracker-sparql/core/tracker-db-interface-sqlite.c79
-rw-r--r--src/libtracker-sparql/core/tracker-vtab-triples.c308
-rw-r--r--src/libtracker-sparql/tracker-namespace-manager.c43
-rw-r--r--src/libtracker-sparql/tracker-serializer-trig.c77
-rw-r--r--src/libtracker-sparql/tracker-serializer-turtle.c69
-rw-r--r--tests/libtracker-sparql/serialize/describe-graph-trig.out19
-rw-r--r--tests/libtracker-sparql/serialize/describe-graph-ttl.out9
-rw-r--r--tests/libtracker-sparql/serialize/describe-single-trig.out2
-rw-r--r--tests/libtracker-sparql/serialize/describe-single-ttl.out2
-rw-r--r--tests/libtracker-sparql/serialize/describe-var-trig.out2
-rw-r--r--tests/libtracker-sparql/serialize/describe-var-ttl.out2
11 files changed, 393 insertions, 219 deletions
diff --git a/src/libtracker-sparql/core/tracker-db-interface-sqlite.c b/src/libtracker-sparql/core/tracker-db-interface-sqlite.c
index c8e221c0c..4696319ec 100644
--- a/src/libtracker-sparql/core/tracker-db-interface-sqlite.c
+++ b/src/libtracker-sparql/core/tracker-db-interface-sqlite.c
@@ -1890,27 +1890,27 @@ function_sparql_print_value (sqlite3_context *context,
case TRACKER_PROPERTY_TYPE_DATE:
case TRACKER_PROPERTY_TYPE_DATETIME:
if (sqlite3_value_numeric_type (argv[0]) == SQLITE_INTEGER) {
- GDateTime *datetime;
+ struct tm tm;
gint64 timestamp;
+ gchar buf[100];
+ int retval;
timestamp = sqlite3_value_int64 (argv[0]);
- datetime = g_date_time_new_from_unix_utc (timestamp);
- if (datetime) {
- gchar *str;
+ if (gmtime_r ((time_t *) &timestamp, &tm) == NULL)
+ result_context_function_error (context, fn, "Invalid unix timestamp");
- if (prop_type == TRACKER_PROPERTY_TYPE_DATETIME)
- str = tracker_date_format_iso8601 (datetime);
- else if (prop_type == TRACKER_PROPERTY_TYPE_DATE)
- str = g_date_time_format (datetime, "%Y-%m-%d");
- else
- g_assert_not_reached ();
+ if (prop_type == TRACKER_PROPERTY_TYPE_DATETIME)
+ retval = strftime ((gchar *) &buf, sizeof (buf), "%2C%y-%m-%dT%TZ", &tm);
+ else if (prop_type == TRACKER_PROPERTY_TYPE_DATE)
+ retval = strftime ((gchar *) &buf, sizeof (buf), "%2C%y-%m-%d", &tm);
+ else
+ g_assert_not_reached ();
- sqlite3_result_text (context, str, -1, g_free);
- g_date_time_unref (datetime);
- } else {
- sqlite3_result_null (context);
- }
+ if (retval != 0)
+ sqlite3_result_text (context, g_strdup (buf), -1, g_free);
+ else
+ result_context_function_error (context, fn, "Invalid unix timestamp");
} else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT) {
if (prop_type == TRACKER_PROPERTY_TYPE_DATETIME) {
sqlite3_result_value (context, argv[0]);
@@ -3489,26 +3489,20 @@ tracker_db_cursor_get_boolean (TrackerSparqlCursor *sparql_cursor,
static gboolean
tracker_db_cursor_get_annotated_value_type (TrackerDBCursor *cursor,
guint column,
+ int column_type,
TrackerSparqlValueType *value_type)
{
- TrackerDBInterface *iface;
TrackerPropertyType property_type;
gboolean is_null;
if (cursor->n_columns == 0)
return FALSE;
- iface = cursor->ref_stmt->db_interface;
-
- tracker_db_interface_lock (iface);
-
/* The value type may be annotated in extra columns, one per
* user-visible column.
*/
property_type = sqlite3_column_int64 (cursor->stmt, column + cursor->n_columns);
- is_null = sqlite3_column_type (cursor->stmt, column) == SQLITE_NULL;
-
- tracker_db_interface_unlock (iface);
+ is_null = column_type == SQLITE_NULL;
if (is_null) {
*value_type = TRACKER_SPARQL_VALUE_TYPE_UNBOUND;
@@ -3536,7 +3530,7 @@ tracker_db_cursor_get_annotated_value_type (TrackerDBCursor *cursor,
*value_type = TRACKER_SPARQL_VALUE_TYPE_DATETIME;
return TRUE;
case TRACKER_PROPERTY_TYPE_RESOURCE:
- if (g_str_has_prefix (tracker_db_cursor_get_string (cursor, column, NULL),
+ if (g_str_has_prefix ((const gchar *) sqlite3_column_text (cursor->stmt, column),
"urn:bnode:"))
*value_type = TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE;
else
@@ -3554,34 +3548,35 @@ tracker_db_cursor_get_value_type (TrackerDBCursor *cursor,
{
TrackerDBInterface *iface;
gint column_type;
- guint n_columns = tracker_db_cursor_get_n_columns (cursor);
TrackerSparqlValueType value_type;
- g_return_val_if_fail (column < n_columns, TRACKER_SPARQL_VALUE_TYPE_UNBOUND);
-
- if (tracker_db_cursor_get_annotated_value_type (cursor, column, &value_type))
- return value_type;
-
iface = cursor->ref_stmt->db_interface;
tracker_db_interface_lock (iface);
column_type = sqlite3_column_type (cursor->stmt, column);
- tracker_db_interface_unlock (iface);
-
- if (column_type == SQLITE_NULL) {
- return TRACKER_SPARQL_VALUE_TYPE_UNBOUND;
- }
+ if (!tracker_db_cursor_get_annotated_value_type (cursor, column, column_type, &value_type)) {
+ if (column_type == SQLITE_NULL) {
+ value_type = TRACKER_SPARQL_VALUE_TYPE_UNBOUND;
+ }
- switch (column_type) {
- case SQLITE_INTEGER:
- return TRACKER_SPARQL_VALUE_TYPE_INTEGER;
- case SQLITE_FLOAT:
- return TRACKER_SPARQL_VALUE_TYPE_DOUBLE;
- default:
- return TRACKER_SPARQL_VALUE_TYPE_STRING;
+ switch (column_type) {
+ case SQLITE_INTEGER:
+ value_type = TRACKER_SPARQL_VALUE_TYPE_INTEGER;
+ break;
+ case SQLITE_FLOAT:
+ value_type = TRACKER_SPARQL_VALUE_TYPE_DOUBLE;
+ break;
+ default:
+ value_type = TRACKER_SPARQL_VALUE_TYPE_STRING;
+ break;
+ }
}
+
+ tracker_db_interface_unlock (iface);
+
+ return value_type;
}
const gchar*
diff --git a/src/libtracker-sparql/core/tracker-vtab-triples.c b/src/libtracker-sparql/core/tracker-vtab-triples.c
index 4d18f84f6..f3fe7aea1 100644
--- a/src/libtracker-sparql/core/tracker-vtab-triples.c
+++ b/src/libtracker-sparql/core/tracker-vtab-triples.c
@@ -37,8 +37,10 @@
#define SQLITE_INDEX_CONSTRAINT_ISNULL 71
#endif
+/* Properties are additional columns after graph and rowid */
+#define FIRST_PROPERTY_COLUMN 2
+
enum {
- COL_ROWID,
COL_GRAPH,
COL_SUBJECT,
COL_PREDICATE,
@@ -77,6 +79,8 @@ typedef struct {
struct sqlite3_vtab_cursor parent;
TrackerTriplesVTab *vtab;
struct sqlite3_stmt *stmt;
+ TrackerProperty **column_properties;
+ guint n_alloc_column_properties;
struct {
sqlite3_value *graph;
@@ -88,8 +92,14 @@ typedef struct {
GHashTable *query_graphs;
GList *properties;
+ GList *classes;
GList *graphs;
+ const GList *cur_property;
+ const GList *cur_class;
+ const GList *cur_graph;
+ gint column;
+
guint64 rowid;
guint finished : 1;
} TrackerTriplesCursor;
@@ -120,6 +130,7 @@ tracker_triples_cursor_reset (TrackerTriplesCursor *cursor)
g_clear_pointer (&cursor->match.subject, sqlite3_value_free);
g_clear_pointer (&cursor->match.predicate, sqlite3_value_free);
g_clear_pointer (&cursor->properties, g_list_free);
+ g_clear_pointer (&cursor->classes, g_list_free);
g_clear_pointer (&cursor->graphs, g_list_free);
g_clear_pointer (&cursor->query_graphs, g_hash_table_unref);
cursor->match.idxFlags = 0;
@@ -133,6 +144,7 @@ tracker_triples_cursor_free (gpointer data)
TrackerTriplesCursor *cursor = data;
tracker_triples_cursor_reset (cursor);
+ g_clear_pointer (&cursor->column_properties, g_free);
g_free (cursor);
}
@@ -153,7 +165,6 @@ triples_connect (sqlite3 *db,
rc = sqlite3_declare_vtab (module->db,
"CREATE TABLE x("
- " ID INTEGER,"
" graph INTEGER,"
" subject INTEGER, "
" predicate INTEGER, "
@@ -203,11 +214,6 @@ triples_best_index (sqlite3_vtab *vtab,
info->aConstraint[i].iColumn == COL_OBJECT_TYPE)
continue;
- if (info->aConstraint[i].iColumn == COL_ROWID) {
- sqlite3_free (idx_str);
- return SQLITE_ERROR;
- }
-
/* We can only check for (in)equality */
if (info->aConstraint[i].op != SQLITE_INDEX_CONSTRAINT_EQ &&
info->aConstraint[i].op != SQLITE_INDEX_CONSTRAINT_NE &&
@@ -218,11 +224,11 @@ triples_best_index (sqlite3_vtab *vtab,
}
/* idxNum encodes the used columns and their operators */
- idx |= masks[info->aConstraint[i].iColumn - 1].mask;
+ idx |= masks[info->aConstraint[i].iColumn].mask;
if (info->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_NE ||
info->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_ISNOTNULL)
- idx |= masks[info->aConstraint[i].iColumn - 1].negated_mask;
+ idx |= masks[info->aConstraint[i].iColumn].negated_mask;
/* idxStr stores the mapping between columns and filter arguments */
idx_str[info->aConstraint[i].iColumn] = argv_idx - 1;
@@ -281,26 +287,48 @@ triples_close (sqlite3_vtab_cursor *vtab_cursor)
}
static void
-collect_properties (TrackerTriplesCursor *cursor)
+collect_tables (TrackerTriplesCursor *cursor)
{
- TrackerProperty **properties;
- guint n_properties, i;
-
- properties = tracker_ontologies_get_properties (cursor->vtab->module->ontologies,
- &n_properties);
- for (i = 0; i < n_properties; i++) {
- if (cursor->match.predicate) {
- gboolean negated = !!(cursor->match.idxFlags & IDX_MATCH_PREDICATE_NEG);
- gboolean equals =
- (sqlite3_value_int64 (cursor->match.predicate) ==
- tracker_property_get_id (properties[i]));
+ TrackerProperty *property = NULL;
+ const gchar *uri = NULL;
+ gboolean pred_negated;
- if (equals == negated)
- continue;
- }
+ pred_negated = !!(cursor->match.idxFlags & IDX_MATCH_PREDICATE_NEG);
- cursor->properties = g_list_prepend (cursor->properties,
- properties[i]);
+ if (cursor->match.predicate) {
+ uri = tracker_ontologies_get_uri_by_id (cursor->vtab->module->ontologies,
+ sqlite3_value_int64 (cursor->match.predicate));
+ }
+
+ if (uri) {
+ property = tracker_ontologies_get_property_by_uri (cursor->vtab->module->ontologies,
+ uri);
+ }
+
+ if (property && !pred_negated) {
+ cursor->properties = g_list_prepend (cursor->properties, property);
+ } else {
+ TrackerProperty **properties;
+ guint n_properties, i;
+
+ properties = tracker_ontologies_get_properties (cursor->vtab->module->ontologies,
+ &n_properties);
+ for (i = 0; i < n_properties; i++) {
+ if (tracker_property_get_multiple_values (properties[i])) {
+ if (pred_negated && property == properties[i])
+ continue;
+
+ cursor->properties = g_list_prepend (cursor->properties,
+ properties[i]);
+ } else {
+ TrackerClass *class;
+
+ class = tracker_property_get_domain (properties[i]);
+
+ if (!g_list_find (cursor->classes, class))
+ cursor->classes = g_list_prepend (cursor->classes, class);
+ }
+ }
}
}
@@ -389,31 +417,75 @@ bind_arg (sqlite3_stmt *stmt,
sqlite3_bind_value (stmt, idx, value);
}
+static TrackerProperty *
+get_column_property (TrackerTriplesCursor *cursor,
+ int n_col)
+{
+ TrackerProperty *property;
+ const gchar *col_name;
+ int n_cols;
+
+ n_cols = sqlite3_column_count (cursor->stmt);
+ g_assert ((guint) n_cols <= cursor->n_alloc_column_properties);
+
+ if (n_col < 0 || n_col >= n_cols)
+ return NULL;
+
+ property = cursor->column_properties[n_col];
+
+ if (!property) {
+ col_name = sqlite3_column_name (cursor->stmt, n_col);
+ property = tracker_ontologies_get_property_by_uri (cursor->vtab->module->ontologies,
+ col_name);
+ cursor->column_properties[n_col] = property;
+ }
+
+ return property;
+}
+
static gboolean
iterate_next_stmt (TrackerTriplesCursor *cursor,
const gchar **graph,
TrackerRowid *graph_id,
+ TrackerClass **class,
TrackerProperty **property)
{
TrackerRowid *id;
- while (cursor->properties && !cursor->graphs) {
- /* Iterate to next property, and redo graph list */
- cursor->properties = g_list_remove (cursor->properties,
- cursor->properties->data);
- cursor->graphs = g_hash_table_get_keys (cursor->query_graphs);
- }
+ *graph_id = 0;
+ *graph = NULL;
+ *class = NULL;
+ *property = NULL;
- if (!cursor->properties)
+ if (cursor->finished)
return FALSE;
- *property = cursor->properties->data;
+ if (cursor->cur_class)
+ cursor->cur_class = cursor->cur_class->next;
+ else if (cursor->cur_property)
+ cursor->cur_property = cursor->cur_property->next;
+
+ if (!cursor->cur_class && !cursor->cur_property) {
+ if (cursor->cur_graph)
+ cursor->cur_graph = cursor->cur_graph->next;
+ else
+ cursor->cur_graph = cursor->graphs;
+
+ cursor->cur_class = cursor->classes;
+ cursor->cur_property = cursor->properties;
+ }
+
+ if (!cursor->cur_graph)
+ return FALSE;
- id = cursor->graphs->data;
+ id = cursor->cur_graph->data;
*graph_id = *id;
*graph = g_hash_table_lookup (cursor->query_graphs, id);
- cursor->graphs = g_list_remove (cursor->graphs, cursor->graphs->data);
+ if (cursor->cur_class)
+ *class = cursor->cur_class->data;
+ else if (cursor->cur_property)
+ *property = cursor->cur_property->data;
return TRUE;
}
@@ -422,41 +494,72 @@ static int
init_stmt (TrackerTriplesCursor *cursor)
{
TrackerProperty *property;
+ TrackerClass *class;
const gchar *graph;
TrackerRowid graph_id;
- GString *sql;
- int rc;
+ int rc = SQLITE_DONE;
+
+ while (iterate_next_stmt (cursor, &graph, &graph_id, &class, &property)) {
+ GString *sql;
- while (iterate_next_stmt (cursor, &graph, &graph_id, &property)) {
sql = g_string_new (NULL);
- g_string_append_printf (sql,
- "SELECT %" G_GINT64_FORMAT ", t.ID, "
- " (SELECT ID From Resource WHERE Uri = \"%s\"), "
- " t.\"%s\", "
- " %d "
- "FROM \"%s\".\"%s\" AS t "
- "WHERE 1 ",
- graph_id,
- tracker_property_get_uri (property),
- tracker_property_get_name (property),
- tracker_property_get_data_type (property),
- graph,
- tracker_property_get_table_name (property));
+
+ if (class) {
+ TrackerProperty **properties;
+ guint n_properties, i;
+
+ g_string_append_printf (sql,
+ "SELECT %" G_GINT64_FORMAT ", ROWID ",
+ graph_id);
+
+
+ properties = tracker_ontologies_get_properties (cursor->vtab->module->ontologies,
+ &n_properties);
+ for (i = 0; i < n_properties; i++) {
+ if (!tracker_property_get_multiple_values (properties[i]) &&
+ tracker_property_get_domain (properties[i]) == class) {
+ g_string_append_printf (sql, ", \"%s\" ",
+ tracker_property_get_name (properties[i]));
+ }
+ }
+
+ g_string_append_printf (sql,
+ "FROM \"%s\".\"%s\" AS t ",
+ graph,
+ tracker_class_get_name (class));
+ } else if (property) {
+ sql = g_string_new (NULL);
+
+ if (tracker_property_get_multiple_values (property)) {
+ g_string_append_printf (sql,
+ "SELECT %" G_GINT64_FORMAT ", * "
+ "FROM \"%s\".\"%s\" AS t ",
+ graph_id,
+ graph,
+ tracker_property_get_table_name (property));
+ } else {
+ g_string_append_printf (sql,
+ "SELECT %" G_GINT64_FORMAT ", ROWID, \"%s\" "
+ "FROM \"%s\".\"%s\" AS t ",
+ graph_id,
+ tracker_property_get_name (property),
+ graph,
+ tracker_property_get_table_name (property));
+ }
+ }
if (cursor->match.subject) {
- g_string_append (sql, "AND t.ID ");
+ g_string_append (sql, "WHERE t.ID ");
add_arg_check (sql, cursor->match.subject,
!!(cursor->match.idxFlags & IDX_MATCH_SUBJECT_NEG),
"@s");
}
rc = sqlite3_prepare_v2 (cursor->vtab->module->db,
- sql->str, -1, &cursor->stmt, 0);
+ sql->str, -1, &cursor->stmt, 0);
g_string_free (sql, TRUE);
if (rc == SQLITE_OK) {
- if (cursor->match.graph)
- bind_arg (cursor->stmt, cursor->match.graph, "@g");
if (cursor->match.subject)
bind_arg (cursor->stmt, cursor->match.subject, "@s");
@@ -464,12 +567,29 @@ init_stmt (TrackerTriplesCursor *cursor)
}
if (rc != SQLITE_DONE)
- return rc;
+ break;
g_clear_pointer (&cursor->stmt, sqlite3_finalize);
}
- return SQLITE_DONE;
+ if (rc == SQLITE_ROW) {
+ int columns;
+
+ columns = sqlite3_column_count (cursor->stmt);
+
+ if ((guint) columns > cursor->n_alloc_column_properties) {
+ g_free (cursor->column_properties);
+ cursor->column_properties =
+ g_new0 (TrackerProperty*, columns);
+ cursor->n_alloc_column_properties = columns;
+ } else {
+ bzero (cursor->column_properties,
+ sizeof (TrackerProperty*) *
+ cursor->n_alloc_column_properties);
+ }
+ }
+
+ return rc;
}
static int
@@ -504,8 +624,14 @@ triples_filter (sqlite3_vtab_cursor *vtab_cursor,
if ((rc = collect_graphs (cursor)) != SQLITE_DONE)
return rc;
- collect_properties (cursor);
+ collect_tables (cursor);
+
+ cursor->cur_graph = NULL;
+ cursor->cur_class = NULL;
+ cursor->cur_property = NULL;
+
rc = init_stmt (cursor);
+ cursor->column = FIRST_PROPERTY_COLUMN;
if (rc == SQLITE_DONE)
cursor->finished = TRUE;
@@ -520,21 +646,43 @@ static int
triples_next (sqlite3_vtab_cursor *vtab_cursor)
{
TrackerTriplesCursor *cursor = (TrackerTriplesCursor *) vtab_cursor;
- int rc;
+ int rc, column_count;
+
+ cursor->rowid++;
+ column_count = sqlite3_column_count (cursor->stmt);
+
+ while (cursor->column < column_count) {
+ cursor->column++;
+
+ if ((cursor->match.idxFlags & IDX_MATCH_PREDICATE_NEG) != 0) {
+ TrackerProperty *property;
+
+ /* Single valued properties skip the "predicate != ..." here */
+ property = get_column_property (cursor, cursor->column);
+
+ if (property &&
+ sqlite3_value_int64 (cursor->match.predicate) ==
+ tracker_property_get_id (property))
+ cursor->column++;
+ }
+
+ if (sqlite3_column_type (cursor->stmt, cursor->column) != SQLITE_NULL)
+ break;
+ }
+
+ if (cursor->column < column_count)
+ return SQLITE_OK;
rc = sqlite3_step (cursor->stmt);
+ cursor->column = FIRST_PROPERTY_COLUMN;
if (rc == SQLITE_DONE) {
g_clear_pointer (&cursor->stmt, sqlite3_finalize);
rc = init_stmt (cursor);
}
- if (rc == SQLITE_ROW) {
- cursor->rowid++;
- } else {
+ if (rc != SQLITE_ROW)
cursor->finished = TRUE;
- }
-
if (rc != SQLITE_ROW && rc != SQLITE_DONE)
return rc;
@@ -556,12 +704,34 @@ triples_column (sqlite3_vtab_cursor *vtab_cursor,
{
TrackerTriplesCursor *cursor = (TrackerTriplesCursor *) vtab_cursor;
sqlite3_value *value;
+ TrackerProperty *property;
- if (n_col == COL_ROWID) {
- sqlite3_result_int64 (context, cursor->rowid);
- } else {
- value = sqlite3_column_value (cursor->stmt, n_col - 1);
+ switch (n_col) {
+ case COL_GRAPH:
+ value = sqlite3_column_value (cursor->stmt, 0);
+ sqlite3_result_value (context, value);
+ break;
+ case COL_SUBJECT:
+ value = sqlite3_column_value (cursor->stmt, 1);
sqlite3_result_value (context, value);
+ break;
+ case COL_OBJECT:
+ value = sqlite3_column_value (cursor->stmt, cursor->column);
+ sqlite3_result_value (context, value);
+ break;
+ case COL_PREDICATE:
+ case COL_OBJECT_TYPE:
+ property = get_column_property (cursor, cursor->column);
+ if (!property) {
+ sqlite3_result_error_code (context, SQLITE_CORRUPT);
+ break;
+ }
+
+ if (n_col == COL_PREDICATE)
+ sqlite3_result_int64 (context, tracker_property_get_id (property));
+ else
+ sqlite3_result_int64 (context, tracker_property_get_data_type (property));
+ break;
}
return SQLITE_OK;
diff --git a/src/libtracker-sparql/tracker-namespace-manager.c b/src/libtracker-sparql/tracker-namespace-manager.c
index 377feab81..f2eabdf00 100644
--- a/src/libtracker-sparql/tracker-namespace-manager.c
+++ b/src/libtracker-sparql/tracker-namespace-manager.c
@@ -31,8 +31,15 @@
#define MAX_PREFIX_LENGTH 100
typedef struct {
+ const gchar *prefix;
+ const gchar *uri;
+ int uri_len;
+} PrefixMap;
+
+typedef struct {
GHashTable *prefix_to_namespace;
GHashTable *namespace_to_prefix;
+ GArray *prefix_map;
gboolean sealed;
} TrackerNamespaceManagerPrivate;
@@ -74,6 +81,7 @@ tracker_namespace_manager_init (TrackerNamespaceManager *self)
priv->prefix_to_namespace = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
priv->namespace_to_prefix = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->prefix_map = g_array_new (FALSE, FALSE, sizeof (PrefixMap));
}
static void
@@ -85,6 +93,7 @@ finalize (GObject *object)
g_hash_table_unref (priv->prefix_to_namespace);
g_hash_table_unref (priv->namespace_to_prefix);
+ g_array_unref (priv->prefix_map);
(G_OBJECT_CLASS (tracker_namespace_manager_parent_class)->finalize)(object);
}
@@ -220,6 +229,8 @@ tracker_namespace_manager_add_prefix (TrackerNamespaceManager *self,
{
TrackerNamespaceManagerPrivate *priv;
const char *str;
+ gchar *prefix_copy, *ns_copy;
+ PrefixMap map;
g_return_if_fail (TRACKER_IS_NAMESPACE_MANAGER (self));
g_return_if_fail (prefix != NULL);
@@ -245,8 +256,16 @@ tracker_namespace_manager_add_prefix (TrackerNamespaceManager *self,
return;
}
- g_hash_table_insert (priv->prefix_to_namespace, g_strdup (prefix), g_strdup (ns));
+ prefix_copy = g_strdup (prefix);
+ ns_copy = g_strdup (ns);
+
+ g_hash_table_insert (priv->prefix_to_namespace, prefix_copy, ns_copy);
g_hash_table_insert (priv->namespace_to_prefix, g_strdup (ns), g_strdup (prefix));
+
+ map.prefix = prefix_copy;
+ map.uri = ns_copy;
+ map.uri_len = strlen (map.uri);
+ g_array_append_val (priv->prefix_map, map);
}
/**
@@ -311,20 +330,28 @@ tracker_namespace_manager_compress_uri (TrackerNamespaceManager *self,
const char *uri)
{
TrackerNamespaceManagerPrivate *priv;
- GHashTableIter iter;
- const char *prefix, *namespace, *suffix;
+ guint i;
+ int len;
g_return_val_if_fail (TRACKER_IS_NAMESPACE_MANAGER (self), NULL);
g_return_val_if_fail (uri != NULL, NULL);
priv = GET_PRIVATE (self);
- g_hash_table_iter_init (&iter, priv->prefix_to_namespace);
+ len = strlen (uri);
+
+ for (i = 0; i < priv->prefix_map->len; i++) {
+ PrefixMap *map;
+ const char *suffix;
+
+ map = &g_array_index (priv->prefix_map, PrefixMap, i);
- while (g_hash_table_iter_next (&iter, (gpointer *) &prefix, (gpointer *) &namespace)) {
- if (g_str_has_prefix (uri, namespace)) {
- suffix = &uri[strlen(namespace)];
- return g_strdup_printf ("%s:%s", prefix, suffix);
+ if (map->uri_len <= len &&
+ map->uri[0] == uri[0] &&
+ map->uri[map->uri_len - 1] == uri[map->uri_len - 1] &&
+ strncmp (uri, map->uri, map->uri_len) == 0) {
+ suffix = &uri[map->uri_len];
+ return g_strconcat (map->prefix, ":", suffix, NULL);
}
}
diff --git a/src/libtracker-sparql/tracker-serializer-trig.c b/src/libtracker-sparql/tracker-serializer-trig.c
index 307bfb78c..2af6d557f 100644
--- a/src/libtracker-sparql/tracker-serializer-trig.c
+++ b/src/libtracker-sparql/tracker-serializer-trig.c
@@ -42,7 +42,7 @@ struct _TrackerQuad
struct _TrackerSerializerTrig
{
TrackerSerializer parent_instance;
- TrackerQuad *last_quad;
+ TrackerQuad last_quad;
GString *data;
guint stream_closed : 1;
guint cursor_started : 1;
@@ -63,15 +63,10 @@ typedef enum
TRACKER_QUAD_BREAK_OBJECT,
} TrackerQuadBreak;
-static TrackerQuad *
-tracker_quad_new_from_cursor (TrackerSparqlCursor *cursor)
+static void
+tracker_quad_init_from_cursor (TrackerQuad *quad,
+ TrackerSparqlCursor *cursor)
{
- TrackerQuad *quad;
-
- if (tracker_sparql_cursor_get_n_columns (cursor) < 3)
- return NULL;
-
- quad = g_new0 (TrackerQuad, 1);
quad->subject_type = tracker_sparql_cursor_get_value_type (cursor, 0);
quad->object_type = tracker_sparql_cursor_get_value_type (cursor, 2);
quad->subject = g_strdup (tracker_sparql_cursor_get_string (cursor, 0, NULL));
@@ -80,6 +75,8 @@ tracker_quad_new_from_cursor (TrackerSparqlCursor *cursor)
if (tracker_sparql_cursor_get_n_columns (cursor) >= 4)
quad->graph = g_strdup (tracker_sparql_cursor_get_string (cursor, 3, NULL));
+ else
+ quad->graph = NULL;
if (quad->subject_type == TRACKER_SPARQL_VALUE_TYPE_STRING) {
if (g_str_has_prefix (quad->subject, "urn:bnode:")) {
@@ -94,25 +91,22 @@ tracker_quad_new_from_cursor (TrackerSparqlCursor *cursor)
quad->object_type = TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE;
}
}
-
- return quad;
}
static void
-tracker_quad_free (TrackerQuad *quad)
+tracker_quad_clear (TrackerQuad *quad)
{
- g_free (quad->subject);
- g_free (quad->predicate);
- g_free (quad->object);
- g_free (quad->graph);
- g_free (quad);
+ g_clear_pointer (&quad->subject, g_free);
+ g_clear_pointer (&quad->predicate, g_free);
+ g_clear_pointer (&quad->object, g_free);
+ g_clear_pointer (&quad->graph, g_free);
}
static TrackerQuadBreak
tracker_quad_get_break (TrackerQuad *last,
TrackerQuad *cur)
{
- if (!last)
+ if (!last->subject)
return TRACKER_QUAD_BREAK_NONE;
if (g_strcmp0 (last->graph, cur->graph) != 0)
@@ -148,9 +142,11 @@ print_value (GString *str,
shortname = tracker_namespace_manager_compress_uri (namespaces, value);
if (shortname) {
- g_string_append_printf (str, "%s", shortname);
+ g_string_append (str, shortname);
} else {
- g_string_append_printf (str, "<%s>", value);
+ g_string_append_c (str, '<');
+ g_string_append (str, value);
+ g_string_append_c (str, '>');
}
g_free (shortname);
@@ -160,7 +156,8 @@ print_value (GString *str,
gchar *bnode_label;
bnode_label = g_strdelimit (g_strdup (value), ":", '_');
- g_string_append_printf (str, "_:%s", bnode_label);
+ g_string_append (str, "_:");
+ g_string_append (str, bnode_label);
g_free (bnode_label);
break;
}
@@ -169,8 +166,9 @@ print_value (GString *str,
gchar *escaped;
escaped = tracker_sparql_escape_string (value);
- g_string_append_printf (str, "\"%s\"",
- escaped);
+ g_string_append_c (str, '"');
+ g_string_append (str, escaped);
+ g_string_append_c (str, '"');
g_free (escaped);
break;
}
@@ -190,14 +188,14 @@ print_value (GString *str,
static gboolean
serialize_up_to_size (TrackerSerializerTrig *serializer_trig,
- gsize size,
- GCancellable *cancellable,
- GError **error)
+ gsize size,
+ GCancellable *cancellable,
+ GError **error)
{
TrackerSparqlCursor *cursor;
TrackerNamespaceManager *namespaces;
GError *inner_error = NULL;
- TrackerQuad *cur;
+ TrackerQuad cur;
if (!serializer_trig->data)
serializer_trig->data = g_string_new (NULL);
@@ -210,7 +208,8 @@ serialize_up_to_size (TrackerSerializerTrig *serializer_trig,
str = tracker_namespace_manager_print_turtle (namespaces);
- g_string_append_printf (serializer_trig->data, "%s\n", str);
+ g_string_append (serializer_trig->data, str);
+ g_string_append_c (serializer_trig->data, '\n');
g_free (str);
serializer_trig->head_printed = TRUE;
}
@@ -231,9 +230,9 @@ serialize_up_to_size (TrackerSerializerTrig *serializer_trig,
serializer_trig->cursor_started = TRUE;
}
- cur = tracker_quad_new_from_cursor (cursor);
+ tracker_quad_init_from_cursor (&cur, cursor);
- if (!cur) {
+ if (!cur.subject && cur.predicate && cur.object) {
g_set_error (error,
TRACKER_SPARQL_ERROR,
TRACKER_SPARQL_ERROR_INTERNAL,
@@ -241,15 +240,15 @@ serialize_up_to_size (TrackerSerializerTrig *serializer_trig,
return FALSE;
}
- br = tracker_quad_get_break (serializer_trig->last_quad, cur);
+ br = tracker_quad_get_break (&serializer_trig->last_quad, &cur);
if (br <= TRACKER_QUAD_BREAK_GRAPH) {
if (br == TRACKER_QUAD_BREAK_GRAPH)
g_string_append (serializer_trig->data, " .\n}\n\n");
- if (cur->graph) {
+ if (cur.graph) {
g_string_append (serializer_trig->data, "GRAPH ");
- print_value (serializer_trig->data, cur->graph,
+ print_value (serializer_trig->data, cur.graph,
TRACKER_SPARQL_VALUE_TYPE_URI, namespaces);
g_string_append_c (serializer_trig->data, ' ');
}
@@ -260,7 +259,7 @@ serialize_up_to_size (TrackerSerializerTrig *serializer_trig,
if (br <= TRACKER_QUAD_BREAK_SUBJECT) {
if (br == TRACKER_QUAD_BREAK_SUBJECT)
g_string_append (serializer_trig->data, " .\n\n ");
- print_value (serializer_trig->data, cur->subject, cur->subject_type, namespaces);
+ print_value (serializer_trig->data, cur.subject, cur.subject_type, namespaces);
}
if (br <= TRACKER_QUAD_BREAK_PREDICATE) {
@@ -269,7 +268,7 @@ serialize_up_to_size (TrackerSerializerTrig *serializer_trig,
else
g_string_append_c (serializer_trig->data, ' ');
- print_value (serializer_trig->data, cur->predicate,
+ print_value (serializer_trig->data, cur.predicate,
TRACKER_SPARQL_VALUE_TYPE_URI, namespaces);
}
@@ -278,12 +277,12 @@ serialize_up_to_size (TrackerSerializerTrig *serializer_trig,
g_string_append (serializer_trig->data, ",");
g_string_append_c (serializer_trig->data, ' ');
- print_value (serializer_trig->data, cur->object, cur->object_type, namespaces);
+ print_value (serializer_trig->data, cur.object, cur.object_type, namespaces);
}
serializer_trig->has_quads = TRUE;
- g_clear_pointer (&serializer_trig->last_quad, tracker_quad_free);
- serializer_trig->last_quad = cur;
+ tracker_quad_clear (&serializer_trig->last_quad);
+ memcpy (&serializer_trig->last_quad, &cur, sizeof (TrackerQuad));
}
/* Close the last quad */
@@ -332,7 +331,7 @@ tracker_serializer_trig_close (GInputStream *istream,
{
TrackerSerializerTrig *serializer_trig = TRACKER_SERIALIZER_TRIG (istream);
- g_clear_pointer (&serializer_trig->last_quad, tracker_quad_free);
+ tracker_quad_clear (&serializer_trig->last_quad);
if (serializer_trig->data) {
g_string_free (serializer_trig->data, TRUE);
diff --git a/src/libtracker-sparql/tracker-serializer-turtle.c b/src/libtracker-sparql/tracker-serializer-turtle.c
index fc60f2325..2202c9c1b 100644
--- a/src/libtracker-sparql/tracker-serializer-turtle.c
+++ b/src/libtracker-sparql/tracker-serializer-turtle.c
@@ -41,7 +41,7 @@ struct _TrackerTriple
struct _TrackerSerializerTurtle
{
TrackerSerializer parent_instance;
- TrackerTriple *last_triple;
+ TrackerTriple last_triple;
GString *data;
guint stream_closed : 1;
guint cursor_started : 1;
@@ -61,15 +61,10 @@ typedef enum
TRACKER_TRIPLE_BREAK_OBJECT,
} TrackerTripleBreak;
-static TrackerTriple *
-tracker_triple_new_from_cursor (TrackerSparqlCursor *cursor)
+static void
+tracker_triple_init_from_cursor (TrackerTriple *triple,
+ TrackerSparqlCursor *cursor)
{
- TrackerTriple *triple;
-
- if (tracker_sparql_cursor_get_n_columns (cursor) < 3)
- return NULL;
-
- triple = g_new0 (TrackerTriple, 1);
triple->subject_type = tracker_sparql_cursor_get_value_type (cursor, 0);
triple->object_type = tracker_sparql_cursor_get_value_type (cursor, 2);
triple->subject = g_strdup (tracker_sparql_cursor_get_string (cursor, 0, NULL));
@@ -89,24 +84,21 @@ tracker_triple_new_from_cursor (TrackerSparqlCursor *cursor)
triple->object_type = TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE;
}
}
-
- return triple;
}
static void
-tracker_triple_free (TrackerTriple *triple)
+tracker_triple_clear (TrackerTriple *triple)
{
- g_free (triple->subject);
- g_free (triple->predicate);
- g_free (triple->object);
- g_free (triple);
+ g_clear_pointer (&triple->subject, g_free);
+ g_clear_pointer (&triple->predicate, g_free);
+ g_clear_pointer (&triple->object, g_free);
}
static TrackerTripleBreak
tracker_triple_get_break (TrackerTriple *last,
TrackerTriple *cur)
{
- if (!last)
+ if (!last->subject)
return TRACKER_TRIPLE_BREAK_NONE;
if (g_strcmp0 (last->subject, cur->subject) != 0)
@@ -139,9 +131,11 @@ print_value (GString *str,
shortname = tracker_namespace_manager_compress_uri (namespaces, value);
if (shortname) {
- g_string_append_printf (str, "%s", shortname);
+ g_string_append (str, shortname);
} else {
- g_string_append_printf (str, "<%s>", value);
+ g_string_append_c (str, '<');
+ g_string_append (str, value);
+ g_string_append_c (str, '>');
}
g_free (shortname);
@@ -151,7 +145,8 @@ print_value (GString *str,
gchar *bnode_label;
bnode_label = g_strdelimit (g_strdup (value), ":", '_');
- g_string_append_printf (str, "_:%s", bnode_label);
+ g_string_append (str, "_:");
+ g_string_append (str, bnode_label);
g_free (bnode_label);
break;
}
@@ -160,8 +155,9 @@ print_value (GString *str,
gchar *escaped;
escaped = tracker_sparql_escape_string (value);
- g_string_append_printf (str, "\"%s\"",
- escaped);
+ g_string_append_c (str, '"');
+ g_string_append (str, escaped);
+ g_string_append_c (str, '"');
g_free (escaped);
break;
}
@@ -181,14 +177,14 @@ print_value (GString *str,
static gboolean
serialize_up_to_size (TrackerSerializerTurtle *serializer_ttl,
- gsize size,
- GCancellable *cancellable,
- GError **error)
+ gsize size,
+ GCancellable *cancellable,
+ GError **error)
{
TrackerSparqlCursor *cursor;
TrackerNamespaceManager *namespaces;
GError *inner_error = NULL;
- TrackerTriple *cur;
+ TrackerTriple cur;
if (!serializer_ttl->data)
serializer_ttl->data = g_string_new (NULL);
@@ -201,7 +197,8 @@ serialize_up_to_size (TrackerSerializerTurtle *serializer_ttl,
str = tracker_namespace_manager_print_turtle (namespaces);
- g_string_append_printf (serializer_ttl->data, "%s\n", str);
+ g_string_append (serializer_ttl->data, str);
+ g_string_append_c (serializer_ttl->data, '\n');
g_free (str);
serializer_ttl->head_printed = TRUE;
}
@@ -222,9 +219,9 @@ serialize_up_to_size (TrackerSerializerTurtle *serializer_ttl,
serializer_ttl->cursor_started = TRUE;
}
- cur = tracker_triple_new_from_cursor (cursor);
+ tracker_triple_init_from_cursor (&cur, cursor);
- if (!cur) {
+ if (!cur.subject || !cur.predicate || !cur.object) {
g_set_error (error,
TRACKER_SPARQL_ERROR,
TRACKER_SPARQL_ERROR_INTERNAL,
@@ -232,12 +229,12 @@ serialize_up_to_size (TrackerSerializerTurtle *serializer_ttl,
return FALSE;
}
- br = tracker_triple_get_break (serializer_ttl->last_triple, cur);
+ br = tracker_triple_get_break (&serializer_ttl->last_triple, &cur);
if (br <= TRACKER_TRIPLE_BREAK_SUBJECT) {
if (br == TRACKER_TRIPLE_BREAK_SUBJECT)
g_string_append (serializer_ttl->data, " .\n\n");
- print_value (serializer_ttl->data, cur->subject, cur->subject_type, namespaces);
+ print_value (serializer_ttl->data, cur.subject, cur.subject_type, namespaces);
}
if (br <= TRACKER_TRIPLE_BREAK_PREDICATE) {
@@ -246,7 +243,7 @@ serialize_up_to_size (TrackerSerializerTurtle *serializer_ttl,
else
g_string_append_c (serializer_ttl->data, ' ');
- print_value (serializer_ttl->data, cur->predicate,
+ print_value (serializer_ttl->data, cur.predicate,
TRACKER_SPARQL_VALUE_TYPE_URI, namespaces);
}
@@ -255,12 +252,12 @@ serialize_up_to_size (TrackerSerializerTurtle *serializer_ttl,
g_string_append (serializer_ttl->data, ",");
g_string_append_c (serializer_ttl->data, ' ');
- print_value (serializer_ttl->data, cur->object, cur->object_type, namespaces);
+ print_value (serializer_ttl->data, cur.object, cur.object_type, namespaces);
}
serializer_ttl->has_triples = TRUE;
- g_clear_pointer (&serializer_ttl->last_triple, tracker_triple_free);
- serializer_ttl->last_triple = cur;
+ tracker_triple_clear (&serializer_ttl->last_triple);
+ memcpy (&serializer_ttl->last_triple, &cur, sizeof (TrackerTriple));
}
/* Print dot for the last triple */
@@ -309,7 +306,7 @@ tracker_serializer_turtle_close (GInputStream *istream,
{
TrackerSerializerTurtle *serializer_ttl = TRACKER_SERIALIZER_TURTLE (istream);
- g_clear_pointer (&serializer_ttl->last_triple, tracker_triple_free);
+ tracker_triple_clear (&serializer_ttl->last_triple);
if (serializer_ttl->data) {
g_string_free (serializer_ttl->data, TRUE);
diff --git a/tests/libtracker-sparql/serialize/describe-graph-trig.out b/tests/libtracker-sparql/serialize/describe-graph-trig.out
index 323a1e0d5..0c03de073 100644
--- a/tests/libtracker-sparql/serialize/describe-graph-trig.out
+++ b/tests/libtracker-sparql/serialize/describe-graph-trig.out
@@ -14,32 +14,19 @@
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix mfo: <http://tracker.api.gnome.org/ontology/v3/mfo#> .
-GRAPH <http://example/B> {
- <http://example/a> nmm:trackNumber 1 .
-
- <http://example/b> nmm:beatsPerMinute 120 .
-}
-
GRAPH <http://example/A> {
<http://example/a> nie:title "Aaa" ;
dc:title "Aaa" ;
+ rdf:type rdfs:Resource, nie:InformationElement, nfo:Media, nmm:MusicPiece .
}
GRAPH <http://example/B> {
+ <http://example/a> nmm:trackNumber 1 .
-}
-
-GRAPH <http://example/A> {
-}
+ <http://example/b> nmm:beatsPerMinute 120 .
-GRAPH <http://example/B> {
-}
-GRAPH <http://example/A> {
- <http://example/a> rdf:type rdfs:Resource, nie:InformationElement, nfo:Media, nmm:MusicPiece .
-}
-GRAPH <http://example/B> {
<http://example/a> rdf:type rdfs:Resource, nie:InformationElement, nfo:Media, nmm:MusicPiece .
<http://example/b> rdf:type rdfs:Resource, nie:InformationElement, nfo:Media, nmm:MusicPiece .
diff --git a/tests/libtracker-sparql/serialize/describe-graph-ttl.out b/tests/libtracker-sparql/serialize/describe-graph-ttl.out
index 196f83d8e..6204d080d 100644
--- a/tests/libtracker-sparql/serialize/describe-graph-ttl.out
+++ b/tests/libtracker-sparql/serialize/describe-graph-ttl.out
@@ -14,16 +14,15 @@
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix mfo: <http://tracker.api.gnome.org/ontology/v3/mfo#> .
-<http://example/a> nmm:trackNumber 1 .
-
-<http://example/b> nmm:beatsPerMinute 120 .
-
<http://example/a> nie:title "Aaa" ;
dc:title "Aaa" ;
+ rdf:type rdfs:Resource, nie:InformationElement, nfo:Media, nmm:MusicPiece ;
+ nmm:trackNumber 1 .
+<http://example/b> nmm:beatsPerMinute 120 .
-<http://example/a> rdf:type rdfs:Resource, nie:InformationElement, nfo:Media, nmm:MusicPiece, rdfs:Resource, nie:InformationElement, nfo:Media, nmm:MusicPiece .
+<http://example/a> rdf:type rdfs:Resource, nie:InformationElement, nfo:Media, nmm:MusicPiece .
<http://example/b> rdf:type rdfs:Resource, nie:InformationElement, nfo:Media, nmm:MusicPiece .
diff --git a/tests/libtracker-sparql/serialize/describe-single-trig.out b/tests/libtracker-sparql/serialize/describe-single-trig.out
index ec5667d31..77649d1cf 100644
--- a/tests/libtracker-sparql/serialize/describe-single-trig.out
+++ b/tests/libtracker-sparql/serialize/describe-single-trig.out
@@ -16,7 +16,7 @@
{
rdfs:Resource nrl:classSpecification "https://www.w3.org/TR/rdf-schema/#ch_resource" ;
- rdfs:label "All Resources" ;
rdfs:comment "All resources" ;
+ rdfs:label "All Resources" ;
rdf:type rdfs:Resource, rdfs:Class .
}
diff --git a/tests/libtracker-sparql/serialize/describe-single-ttl.out b/tests/libtracker-sparql/serialize/describe-single-ttl.out
index 78b8ce55a..3b76470f6 100644
--- a/tests/libtracker-sparql/serialize/describe-single-ttl.out
+++ b/tests/libtracker-sparql/serialize/describe-single-ttl.out
@@ -15,6 +15,6 @@
@prefix mfo: <http://tracker.api.gnome.org/ontology/v3/mfo#> .
rdfs:Resource nrl:classSpecification "https://www.w3.org/TR/rdf-schema/#ch_resource" ;
- rdfs:label "All Resources" ;
rdfs:comment "All resources" ;
+ rdfs:label "All Resources" ;
rdf:type rdfs:Resource, rdfs:Class .
diff --git a/tests/libtracker-sparql/serialize/describe-var-trig.out b/tests/libtracker-sparql/serialize/describe-var-trig.out
index eb1ceda36..ddcbc81d4 100644
--- a/tests/libtracker-sparql/serialize/describe-var-trig.out
+++ b/tests/libtracker-sparql/serialize/describe-var-trig.out
@@ -16,8 +16,8 @@
{
rdfs:Class nrl:classSpecification "https://www.w3.org/TR/rdf-schema/#ch_class" ;
- rdfs:label "Class" ;
rdfs:comment "The class of classes" ;
+ rdfs:label "Class" ;
rdfs:subClassOf rdfs:Resource ;
rdf:type rdfs:Resource, rdfs:Class .
}
diff --git a/tests/libtracker-sparql/serialize/describe-var-ttl.out b/tests/libtracker-sparql/serialize/describe-var-ttl.out
index 8cd80c5d4..26a905ba8 100644
--- a/tests/libtracker-sparql/serialize/describe-var-ttl.out
+++ b/tests/libtracker-sparql/serialize/describe-var-ttl.out
@@ -15,7 +15,7 @@
@prefix mfo: <http://tracker.api.gnome.org/ontology/v3/mfo#> .
rdfs:Class nrl:classSpecification "https://www.w3.org/TR/rdf-schema/#ch_class" ;
- rdfs:label "Class" ;
rdfs:comment "The class of classes" ;
+ rdfs:label "Class" ;
rdfs:subClassOf rdfs:Resource ;
rdf:type rdfs:Resource, rdfs:Class .