From b71657f05077e4cf10c88d60280e0b086d8f71cc Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Fri, 30 Dec 2022 13:21:22 +0100 Subject: core: Drop "rowid" column from tracker_triples virtual table The rowid is already handled in its own virtual table vfunc, this is unused and redundant. --- src/libtracker-sparql/core/tracker-vtab-triples.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/libtracker-sparql/core/tracker-vtab-triples.c b/src/libtracker-sparql/core/tracker-vtab-triples.c index 4d18f84f6..2be4ba467 100644 --- a/src/libtracker-sparql/core/tracker-vtab-triples.c +++ b/src/libtracker-sparql/core/tracker-vtab-triples.c @@ -38,7 +38,6 @@ #endif enum { - COL_ROWID, COL_GRAPH, COL_SUBJECT, COL_PREDICATE, @@ -153,7 +152,6 @@ triples_connect (sqlite3 *db, rc = sqlite3_declare_vtab (module->db, "CREATE TABLE x(" - " ID INTEGER," " graph INTEGER," " subject INTEGER, " " predicate INTEGER, " @@ -203,11 +201,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 +211,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; @@ -557,12 +550,8 @@ triples_column (sqlite3_vtab_cursor *vtab_cursor, TrackerTriplesCursor *cursor = (TrackerTriplesCursor *) vtab_cursor; sqlite3_value *value; - if (n_col == COL_ROWID) { - sqlite3_result_int64 (context, cursor->rowid); - } else { - value = sqlite3_column_value (cursor->stmt, n_col - 1); - sqlite3_result_value (context, value); - } + value = sqlite3_column_value (cursor->stmt, n_col); + sqlite3_result_value (context, value); return SQLITE_OK; } -- cgit v1.2.1 From 82573ab40979b1950ee7e3d4e0c84665a63654b7 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sun, 25 Dec 2022 13:10:35 +0100 Subject: core: Cluster queries in tracker_triples virtual table Currently, the virtual table does query all properties individually, even single-valued ones that live together with other single-valued properties in the class table. Since we no longer have to deal with value-to-string conversions for the upper layers, it is now easier to query these single-valued properties altogether in a single query to the whole class table. --- src/libtracker-sparql/core/tracker-vtab-triples.c | 280 ++++++++++++++++----- .../serialize/describe-graph-trig.out | 8 +- .../serialize/describe-graph-ttl.out | 2 - .../serialize/describe-single-trig.out | 2 +- .../serialize/describe-single-ttl.out | 2 +- .../serialize/describe-var-trig.out | 2 +- .../serialize/describe-var-ttl.out | 2 +- 7 files changed, 229 insertions(+), 69 deletions(-) diff --git a/src/libtracker-sparql/core/tracker-vtab-triples.c b/src/libtracker-sparql/core/tracker-vtab-triples.c index 2be4ba467..f7a80a35f 100644 --- a/src/libtracker-sparql/core/tracker-vtab-triples.c +++ b/src/libtracker-sparql/core/tracker-vtab-triples.c @@ -37,6 +37,9 @@ #define SQLITE_INDEX_CONSTRAINT_ISNULL 71 #endif +/* Properties are additional columns after graph and rowid */ +#define FIRST_PROPERTY_COLUMN 2 + enum { COL_GRAPH, COL_SUBJECT, @@ -76,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; @@ -87,7 +92,9 @@ typedef struct { GHashTable *query_graphs; GList *properties; + GList *classes; GList *graphs; + gint column; guint64 rowid; guint finished : 1; @@ -119,6 +126,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; @@ -132,6 +140,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); } @@ -274,26 +283,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); + } + } } } @@ -382,31 +413,78 @@ 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->graphs) { + if (cursor->classes) { + cursor->classes = g_list_remove (cursor->classes, + cursor->classes->data); + } else if (cursor->properties) { + cursor->properties = g_list_remove (cursor->properties, + cursor->properties->data); + } + + if (!cursor->classes && !cursor->properties) + return FALSE; + + if (cursor->classes || cursor->properties) + cursor->graphs = g_hash_table_get_keys (cursor->query_graphs); } - if (!cursor->properties) + if (!cursor->graphs) return FALSE; - *property = cursor->properties->data; - id = cursor->graphs->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->classes) + *class = cursor->classes->data; + else if (cursor->properties) + *property = cursor->properties->data; + + if (cursor->graphs) { + cursor->graphs = g_list_remove (cursor->graphs, + cursor->graphs->data); + } return TRUE; } @@ -415,41 +493,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"); @@ -457,12 +566,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 @@ -497,8 +623,9 @@ triples_filter (sqlite3_vtab_cursor *vtab_cursor, if ((rc = collect_graphs (cursor)) != SQLITE_DONE) return rc; - collect_properties (cursor); + collect_tables (cursor); rc = init_stmt (cursor); + cursor->column = FIRST_PROPERTY_COLUMN; if (rc == SQLITE_DONE) cursor->finished = TRUE; @@ -515,19 +642,34 @@ triples_next (sqlite3_vtab_cursor *vtab_cursor) TrackerTriplesCursor *cursor = (TrackerTriplesCursor *) vtab_cursor; int rc; + cursor->column++; + cursor->rowid++; + + 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 (cursor->column < sqlite3_column_count (cursor->stmt)) + 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; @@ -549,9 +691,35 @@ triples_column (sqlite3_vtab_cursor *vtab_cursor, { TrackerTriplesCursor *cursor = (TrackerTriplesCursor *) vtab_cursor; sqlite3_value *value; + TrackerProperty *property; + + 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; + } - value = sqlite3_column_value (cursor->stmt, n_col); - sqlite3_result_value (context, value); + 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/tests/libtracker-sparql/serialize/describe-graph-trig.out b/tests/libtracker-sparql/serialize/describe-graph-trig.out index 323a1e0d5..06193a5d1 100644 --- a/tests/libtracker-sparql/serialize/describe-graph-trig.out +++ b/tests/libtracker-sparql/serialize/describe-graph-trig.out @@ -22,19 +22,13 @@ GRAPH { GRAPH { nie:title "Aaa" ; - dc:title "Aaa" ; + dc:title "Aaa" . } GRAPH { } -GRAPH { -} - -GRAPH { -} - GRAPH { 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..b1f66a101 100644 --- a/tests/libtracker-sparql/serialize/describe-graph-ttl.out +++ b/tests/libtracker-sparql/serialize/describe-graph-ttl.out @@ -22,8 +22,6 @@ dc:title "Aaa" ; - - rdf:type rdfs:Resource, nie:InformationElement, nfo:Media, nmm:MusicPiece, rdfs:Resource, nie:InformationElement, nfo:Media, nmm:MusicPiece . 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: . 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: . 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 . -- cgit v1.2.1 From f86f409d6c6810ffb9a7e4c24a170609abbe5ffb Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sun, 25 Dec 2022 19:36:55 +0100 Subject: core: Group tracker_triples results by graph Graphs are currently the last component in our iteration through tables. Move it to be on top of the loop, while it does not affect performance querying things in one or other order, this provides better natural clustering of data for the serialization formats that handle graphs (e.g. Trig). --- src/libtracker-sparql/core/tracker-vtab-triples.c | 52 ++++++++++++---------- .../serialize/describe-graph-trig.out | 17 +++---- .../serialize/describe-graph-ttl.out | 9 ++-- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/libtracker-sparql/core/tracker-vtab-triples.c b/src/libtracker-sparql/core/tracker-vtab-triples.c index f7a80a35f..bb0f7f9c5 100644 --- a/src/libtracker-sparql/core/tracker-vtab-triples.c +++ b/src/libtracker-sparql/core/tracker-vtab-triples.c @@ -94,6 +94,10 @@ typedef struct { GList *properties; GList *classes; GList *graphs; + + const GList *cur_property; + const GList *cur_class; + const GList *cur_graph; gint column; guint64 rowid; @@ -453,38 +457,35 @@ iterate_next_stmt (TrackerTriplesCursor *cursor, *class = NULL; *property = NULL; - if (!cursor->graphs) { - if (cursor->classes) { - cursor->classes = g_list_remove (cursor->classes, - cursor->classes->data); - } else if (cursor->properties) { - cursor->properties = g_list_remove (cursor->properties, - cursor->properties->data); - } + if (cursor->finished) + return FALSE; - if (!cursor->classes && !cursor->properties) - return FALSE; + 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; - if (cursor->classes || cursor->properties) - cursor->graphs = g_hash_table_get_keys (cursor->query_graphs); + cursor->cur_class = cursor->classes; + cursor->cur_property = cursor->properties; } - if (!cursor->graphs) + 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); - if (cursor->classes) - *class = cursor->classes->data; - else if (cursor->properties) - *property = cursor->properties->data; - - if (cursor->graphs) { - 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; } @@ -624,6 +625,11 @@ triples_filter (sqlite3_vtab_cursor *vtab_cursor, return rc; collect_tables (cursor); + + cursor->cur_graph = NULL; + cursor->cur_class = NULL; + cursor->cur_property = NULL; + rc = init_stmt (cursor); cursor->column = FIRST_PROPERTY_COLUMN; diff --git a/tests/libtracker-sparql/serialize/describe-graph-trig.out b/tests/libtracker-sparql/serialize/describe-graph-trig.out index 06193a5d1..0c03de073 100644 --- a/tests/libtracker-sparql/serialize/describe-graph-trig.out +++ b/tests/libtracker-sparql/serialize/describe-graph-trig.out @@ -14,26 +14,19 @@ @prefix xsd: . @prefix mfo: . -GRAPH { - nmm:trackNumber 1 . - - nmm:beatsPerMinute 120 . -} - GRAPH { nie:title "Aaa" ; - dc:title "Aaa" . + dc:title "Aaa" ; + rdf:type rdfs:Resource, nie:InformationElement, nfo:Media, nmm:MusicPiece . } GRAPH { + nmm:trackNumber 1 . + + nmm:beatsPerMinute 120 . -} -GRAPH { - rdf:type rdfs:Resource, nie:InformationElement, nfo:Media, nmm:MusicPiece . -} -GRAPH { rdf:type rdfs:Resource, nie:InformationElement, nfo:Media, nmm:MusicPiece . 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 b1f66a101..6204d080d 100644 --- a/tests/libtracker-sparql/serialize/describe-graph-ttl.out +++ b/tests/libtracker-sparql/serialize/describe-graph-ttl.out @@ -14,14 +14,15 @@ @prefix xsd: . @prefix mfo: . - nmm:trackNumber 1 . + nie:title "Aaa" ; + dc:title "Aaa" ; + rdf:type rdfs:Resource, nie:InformationElement, nfo:Media, nmm:MusicPiece ; + nmm:trackNumber 1 . nmm:beatsPerMinute 120 . - nie:title "Aaa" ; - dc:title "Aaa" ; - rdf:type rdfs:Resource, nie:InformationElement, nfo:Media, nmm:MusicPiece, rdfs:Resource, nie:InformationElement, nfo:Media, nmm:MusicPiece . + rdf:type rdfs:Resource, nie:InformationElement, nfo:Media, nmm:MusicPiece . rdf:type rdfs:Resource, nie:InformationElement, nfo:Media, nmm:MusicPiece . -- cgit v1.2.1 From 9dc2f1067c69f38ad7e28329234df725e8fc5db8 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sun, 25 Dec 2022 19:57:59 +0100 Subject: core: Skip empty columns in tracker_triples table We used to propagate empty values up, just to let the upper layers filter these out. Simply avoid rows with an empty "object" column in the virtual table implementation. --- src/libtracker-sparql/core/tracker-vtab-triples.c | 29 ++++++++++++++--------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/libtracker-sparql/core/tracker-vtab-triples.c b/src/libtracker-sparql/core/tracker-vtab-triples.c index bb0f7f9c5..f3fe7aea1 100644 --- a/src/libtracker-sparql/core/tracker-vtab-triples.c +++ b/src/libtracker-sparql/core/tracker-vtab-triples.c @@ -646,24 +646,31 @@ static int triples_next (sqlite3_vtab_cursor *vtab_cursor) { TrackerTriplesCursor *cursor = (TrackerTriplesCursor *) vtab_cursor; - int rc; + int rc, column_count; - cursor->column++; cursor->rowid++; + column_count = sqlite3_column_count (cursor->stmt); - if ((cursor->match.idxFlags & IDX_MATCH_PREDICATE_NEG) != 0) { - TrackerProperty *property; + while (cursor->column < column_count) { + cursor->column++; - /* Single valued properties skip the "predicate != ..." here */ - property = get_column_property (cursor, 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 (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 < sqlite3_column_count (cursor->stmt)) + if (cursor->column < column_count) return SQLITE_OK; rc = sqlite3_step (cursor->stmt); -- cgit v1.2.1 From c2006110204f9798c96b5238b1325970aa7239b8 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 31 Dec 2022 14:19:46 +0100 Subject: core: Use direct strftime() on SparqlPrintValue The allocation, string composition and freeing of GDateTime may turn into a hottish path if the resultset has a high amount of strings. Resort to strftime() on a fixed buffer in order to reduce malloc/free overhead. --- .../core/tracker-db-interface-sqlite.c | 30 +++++++++++----------- 1 file changed, 15 insertions(+), 15 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..eb99e226f 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 *) ×tamp, &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]); -- cgit v1.2.1 From 92696167b908b54aaf4b10da61df701018acea9a Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 31 Dec 2022 14:22:14 +0100 Subject: core: Optimize getting TrackerSparqlValueType for columns Getting the value types as propagated from the queries themselves potentially involves locking/unlocking twice the DB interface mutex. Make everything happen within a single mutually exclusive area. --- .../core/tracker-db-interface-sqlite.c | 49 ++++++++++------------ 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/src/libtracker-sparql/core/tracker-db-interface-sqlite.c b/src/libtracker-sparql/core/tracker-db-interface-sqlite.c index eb99e226f..4696319ec 100644 --- a/src/libtracker-sparql/core/tracker-db-interface-sqlite.c +++ b/src/libtracker-sparql/core/tracker-db-interface-sqlite.c @@ -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* -- cgit v1.2.1 From ff9c769f3e15170ab8f3fd790d5b7b89e66bf430 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 31 Dec 2022 14:24:35 +0100 Subject: libtracker-sparql: Optimize tracker_namespace_manager_compress_uri() Keep an array of expanded IRIs with their length, so that we can minimize string comparisons (by checking the first/last chars match) and avoid GHashTableIter. --- src/libtracker-sparql/tracker-namespace-manager.c | 43 ++++++++++++++++++----- 1 file changed, 35 insertions(+), 8 deletions(-) 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 @@ -30,9 +30,16 @@ #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); } } -- cgit v1.2.1 From 771724af16eeacf672194eaf866cbe201342104c Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 31 Dec 2022 14:27:31 +0100 Subject: libtracker-sparql: Avoid g_string_append_printf() in TTL serializer Prefer concatenations, as that performs better. --- src/libtracker-sparql/tracker-serializer-turtle.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/libtracker-sparql/tracker-serializer-turtle.c b/src/libtracker-sparql/tracker-serializer-turtle.c index fc60f2325..98e490b51 100644 --- a/src/libtracker-sparql/tracker-serializer-turtle.c +++ b/src/libtracker-sparql/tracker-serializer-turtle.c @@ -139,9 +139,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 +153,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 +163,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,9 +185,9 @@ 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; @@ -201,7 +205,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; } -- cgit v1.2.1 From 56bc19e8e8c778b7a4493cf6737a5a747af6a9ad Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 31 Dec 2022 14:33:50 +0100 Subject: libtracker-sparql: Avoid frequent malloc/free in TTL serializer The TrackerTriple struct we preserve to compare current/previous value and abbreviate the resulting turtle is created for each row. Store this in the serializer, so we avoid a malloc/free for each row. --- src/libtracker-sparql/tracker-serializer-turtle.c | 46 ++++++++++------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/src/libtracker-sparql/tracker-serializer-turtle.c b/src/libtracker-sparql/tracker-serializer-turtle.c index 98e490b51..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) @@ -192,7 +184,7 @@ serialize_up_to_size (TrackerSerializerTurtle *serializer_ttl, TrackerSparqlCursor *cursor; TrackerNamespaceManager *namespaces; GError *inner_error = NULL; - TrackerTriple *cur; + TrackerTriple cur; if (!serializer_ttl->data) serializer_ttl->data = g_string_new (NULL); @@ -227,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, @@ -237,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) { @@ -251,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); } @@ -260,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 */ @@ -314,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); -- cgit v1.2.1 From e5c81ca041caed717f982f45a59e43c408bde04a Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 31 Dec 2022 17:28:16 +0100 Subject: libtracker-sparql: Avoid g_string_append_printf() in Trig serializer Prefer concatenations, as that performs better. --- src/libtracker-sparql/tracker-serializer-trig.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/libtracker-sparql/tracker-serializer-trig.c b/src/libtracker-sparql/tracker-serializer-trig.c index 307bfb78c..0ed05a98a 100644 --- a/src/libtracker-sparql/tracker-serializer-trig.c +++ b/src/libtracker-sparql/tracker-serializer-trig.c @@ -148,9 +148,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 +162,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 +172,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,9 +194,9 @@ 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; @@ -210,7 +214,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; } -- cgit v1.2.1 From 857d8a4711f436fa207ffad638c83c4fcc31d90e Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 31 Dec 2022 17:33:00 +0100 Subject: libtracker-sparql: Avoid frequent malloc/free in Trig serializer The TrackerTriple struct we preserve to compare current/previous value and abbreviate the resulting turtle is created for each row. Store this in the serializer, so we avoid a malloc/free for each row. --- src/libtracker-sparql/tracker-serializer-trig.c | 54 +++++++++++-------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/src/libtracker-sparql/tracker-serializer-trig.c b/src/libtracker-sparql/tracker-serializer-trig.c index 0ed05a98a..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) @@ -201,7 +195,7 @@ serialize_up_to_size (TrackerSerializerTrig *serializer_trig, TrackerSparqlCursor *cursor; TrackerNamespaceManager *namespaces; GError *inner_error = NULL; - TrackerQuad *cur; + TrackerQuad cur; if (!serializer_trig->data) serializer_trig->data = g_string_new (NULL); @@ -236,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, @@ -246,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, ' '); } @@ -265,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) { @@ -274,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); } @@ -283,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 */ @@ -337,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); -- cgit v1.2.1