summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml29
-rw-r--r--HACKING.md62
-rw-r--r--NEWS21
-rw-r--r--README.md54
-rw-r--r--data/meson.build5
-rw-r--r--docs/reference/libtracker-sparql/libtracker-sparql-docs.xml1
-rw-r--r--docs/reference/libtracker-sparql/migrating-2to3.xml71
-rw-r--r--docs/tools/ttl_loader.c4
-rw-r--r--meson.build33
-rw-r--r--meson_options.txt4
-rw-r--r--po/sk.po1061
-rw-r--r--src/libtracker-common/tracker-date-time.c27
-rw-r--r--src/libtracker-common/tracker-date-time.h3
-rw-r--r--src/libtracker-common/tracker-log.c20
-rw-r--r--src/libtracker-common/tracker-utils.c116
-rw-r--r--src/libtracker-common/tracker-utils.h2
-rw-r--r--src/libtracker-control/meson.build14
-rw-r--r--src/libtracker-control/tracker-control.pc.in11
-rw-r--r--src/libtracker-control/tracker-miner-manager.c9
-rw-r--r--src/libtracker-data/libtracker-data.vapi11
-rw-r--r--src/libtracker-data/meson.build2
-rw-r--r--src/libtracker-data/tracker-class.c27
-rw-r--r--src/libtracker-data/tracker-class.h3
-rw-r--r--src/libtracker-data/tracker-data-backup.c454
-rw-r--r--src/libtracker-data/tracker-data-manager.c1910
-rw-r--r--src/libtracker-data/tracker-data-manager.h35
-rw-r--r--src/libtracker-data/tracker-data-query.c8
-rw-r--r--src/libtracker-data/tracker-data-query.h1
-rw-r--r--src/libtracker-data/tracker-data-update.c1827
-rw-r--r--src/libtracker-data/tracker-data-update.h24
-rw-r--r--src/libtracker-data/tracker-data.h1
-rw-r--r--src/libtracker-data/tracker-db-interface-sqlite.c575
-rw-r--r--src/libtracker-data/tracker-db-interface-sqlite.h19
-rw-r--r--src/libtracker-data/tracker-db-interface.h3
-rw-r--r--src/libtracker-data/tracker-db-journal.c2165
-rw-r--r--src/libtracker-data/tracker-db-journal.h169
-rw-r--r--src/libtracker-data/tracker-db-manager.c502
-rw-r--r--src/libtracker-data/tracker-db-manager.h18
-rw-r--r--src/libtracker-data/tracker-ontologies.c21
-rw-r--r--src/libtracker-data/tracker-property.c36
-rw-r--r--src/libtracker-data/tracker-property.h4
-rw-r--r--src/libtracker-data/tracker-sparql-grammar.h4
-rw-r--r--src/libtracker-data/tracker-sparql-parser.c5
-rw-r--r--src/libtracker-data/tracker-sparql-types.c67
-rw-r--r--src/libtracker-data/tracker-sparql-types.h26
-rw-r--r--src/libtracker-data/tracker-sparql.c2556
-rw-r--r--src/libtracker-data/tracker-turtle-reader.vala24
-rw-r--r--src/libtracker-data/tracker-uuid.c20
-rw-r--r--src/libtracker-data/tracker-uuid.h2
-rw-r--r--src/libtracker-data/tracker-vtab-service.c372
-rw-r--r--src/libtracker-data/tracker-vtab-service.h30
-rw-r--r--src/libtracker-data/tracker-vtab-triples.c21
-rw-r--r--src/libtracker-direct/tracker-direct.c14
-rw-r--r--src/libtracker-direct/tracker-direct.vapi2
-rw-r--r--src/libtracker-fts/tracker-fts.c85
-rw-r--r--src/libtracker-fts/tracker-fts.h24
-rw-r--r--src/libtracker-miner/meson.build14
-rw-r--r--src/libtracker-miner/tracker-file-notifier.c22
-rw-r--r--src/libtracker-miner/tracker-miner-fs.c134
-rw-r--r--src/libtracker-miner/tracker-miner-object.c14
-rw-r--r--src/libtracker-miner/tracker-miner.pc.in11
-rw-r--r--src/libtracker-miner/tracker-sparql-buffer.c2
-rw-r--r--src/libtracker-remote/meson.build4
-rw-r--r--src/libtracker-remote/tracker-remote.vala7
-rw-r--r--src/libtracker-sparql-backend/meson.build19
-rw-r--r--src/libtracker-sparql-backend/tracker-backend.vala4
-rw-r--r--src/libtracker-sparql-backend/tracker-sparql-2.map2
-rw-r--r--src/libtracker-sparql/libtracker-sparql-intermediate-c.vapi10
-rw-r--r--src/libtracker-sparql/meson.build13
-rw-r--r--src/libtracker-sparql/tracker-connection.vala6
-rw-r--r--src/libtracker-sparql/tracker-endpoint-dbus.c610
-rw-r--r--src/libtracker-sparql/tracker-endpoint-dbus.h41
-rw-r--r--src/libtracker-sparql/tracker-endpoint.c118
-rw-r--r--src/libtracker-sparql/tracker-endpoint.h41
-rw-r--r--src/libtracker-sparql/tracker-resource.c53
-rw-r--r--src/libtracker-sparql/tracker-sparql.h2
-rw-r--r--src/libtracker-sparql/tracker-sparql.pc.in18
-rw-r--r--src/ontologies/11-rdf.ontology43
-rw-r--r--src/ontologies/nepomuk/30-nie.ontology3
-rw-r--r--src/ontologies/nepomuk/90-tracker.ontology1
-rw-r--r--src/tracker-store/tracker-main.vala21
-rw-r--r--src/tracker-store/tracker-resources.vala5
-rw-r--r--src/tracker-store/tracker-statistics.vala38
-rw-r--r--src/tracker/tracker-daemon.c7
-rw-r--r--src/tracker/tracker-main.c7
-rw-r--r--src/tracker/tracker-reset.c30
-rw-r--r--src/tracker/tracker-sql.c4
-rw-r--r--[-rwxr-xr-x]tests/functional-tests/01-insertion.py38
-rw-r--r--[-rwxr-xr-x]tests/functional-tests/02-sparql-bugs.py7
-rw-r--r--[-rwxr-xr-x]tests/functional-tests/03-fts-functions.py4
-rw-r--r--[-rwxr-xr-x]tests/functional-tests/04-group-concat.py4
-rw-r--r--[-rwxr-xr-x]tests/functional-tests/05-coalesce.py4
-rw-r--r--[-rwxr-xr-x]tests/functional-tests/06-distance.py4
-rw-r--r--[-rwxr-xr-x]tests/functional-tests/07-graph.py45
-rw-r--r--[-rwxr-xr-x]tests/functional-tests/08-unique-insertions.py9
-rw-r--r--[-rwxr-xr-x]tests/functional-tests/09-concurrent-query.py4
-rw-r--r--[-rwxr-xr-x]tests/functional-tests/14-signals.py7
-rw-r--r--[-rwxr-xr-x]tests/functional-tests/15-statistics.py4
-rw-r--r--[-rwxr-xr-x]tests/functional-tests/16-collation.py3
-rw-r--r--[-rwxr-xr-x]tests/functional-tests/17-ontology-changes.py176
-rw-r--r--tests/functional-tests/configuration.json.in9
-rw-r--r--tests/functional-tests/configuration.py41
-rw-r--r--tests/functional-tests/expectedFailure.py12
-rw-r--r--tests/functional-tests/ipc/meson.build26
-rw-r--r--tests/functional-tests/ipc/test-bus-query-cancellation.c2
-rw-r--r--tests/functional-tests/ipc/test-insert-or-replace.vala12
-rw-r--r--tests/functional-tests/meson.build54
-rw-r--r--tests/functional-tests/storetest.py27
-rwxr-xr-xtests/functional-tests/test-runner.sh.in20
-rw-r--r--tests/gvdb/meson.build1
-rw-r--r--tests/libtracker-common/meson.build6
-rw-r--r--tests/libtracker-common/tracker-date-time-test.c39
-rw-r--r--tests/libtracker-data/aggregates/aggregate-group-2.out2
-rw-r--r--tests/libtracker-data/aggregates/aggregate-group-2.rq9
-rw-r--r--tests/libtracker-data/aggregates/aggregate-group-as-1.out2
-rw-r--r--tests/libtracker-data/aggregates/aggregate-group-as-1.rq9
-rw-r--r--tests/libtracker-data/basic/base-1.out1
-rw-r--r--tests/libtracker-data/basic/base-1.rq3
-rw-r--r--tests/libtracker-data/construct/construct-pattern.out12
-rw-r--r--tests/libtracker-data/construct/construct-pattern.rq3
-rw-r--r--tests/libtracker-data/construct/construct-where.out8
-rw-r--r--tests/libtracker-data/construct/construct-where.rq1
-rw-r--r--tests/libtracker-data/construct/construct-with-modifiers.out4
-rw-r--r--tests/libtracker-data/construct/construct-with-modifiers.rq3
-rw-r--r--tests/libtracker-data/construct/data.ttl31
-rw-r--r--tests/libtracker-data/construct/test.ontology34
-rw-r--r--tests/libtracker-data/datetime/functions-timezone-3.out1
-rw-r--r--tests/libtracker-data/datetime/functions-timezone-3.rq6
-rw-r--r--tests/libtracker-data/datetime/functions-tz-1.out1
-rw-r--r--tests/libtracker-data/datetime/functions-tz-1.rq6
-rw-r--r--tests/libtracker-data/describe/data.ttl31
-rw-r--r--tests/libtracker-data/describe/describe-limit.out11
-rw-r--r--tests/libtracker-data/describe/describe-limit.rq1
-rw-r--r--tests/libtracker-data/describe/describe-multiple.out22
-rw-r--r--tests/libtracker-data/describe/describe-multiple.rq1
-rw-r--r--tests/libtracker-data/describe/describe-non-existent.out0
-rw-r--r--tests/libtracker-data/describe/describe-non-existent.rq1
-rw-r--r--tests/libtracker-data/describe/describe-pattern.out6
-rw-r--r--tests/libtracker-data/describe/describe-pattern.rq1
-rw-r--r--tests/libtracker-data/describe/describe-single.out5
-rw-r--r--tests/libtracker-data/describe/describe-single.rq1
-rw-r--r--tests/libtracker-data/describe/test.ontology34
-rwxr-xr-xtests/libtracker-data/expr-ops/query-ge-1.rq1
-rw-r--r--tests/libtracker-data/functions/functions-datatypes-1.out27
-rw-r--r--tests/libtracker-data/functions/functions-datatypes-1.rq3
-rw-r--r--tests/libtracker-data/functions/functions-datatypes-2.out10
-rw-r--r--tests/libtracker-data/functions/functions-datatypes-2.rq3
-rw-r--r--tests/libtracker-data/functions/functions-datatypes-3.out20
-rw-r--r--tests/libtracker-data/functions/functions-datatypes-3.rq3
-rw-r--r--tests/libtracker-data/functions/functions-datatypes-4.out52
-rw-r--r--tests/libtracker-data/functions/functions-datatypes-4.rq3
-rw-r--r--tests/libtracker-data/functions/functions-property-1.rq2
-rw-r--r--tests/libtracker-data/functions/functions-tracker-loc-1.rq5
-rw-r--r--tests/libtracker-data/graph/add-from-default.out2
-rw-r--r--tests/libtracker-data/graph/add-from-default.rq6
-rw-r--r--tests/libtracker-data/graph/add-from-non-existent.out1
-rw-r--r--tests/libtracker-data/graph/add-from-non-existent.rq5
-rw-r--r--tests/libtracker-data/graph/add-into-self.out1
-rw-r--r--tests/libtracker-data/graph/add-into-self.rq5
-rw-r--r--tests/libtracker-data/graph/add-to-default.out2
-rw-r--r--tests/libtracker-data/graph/add-to-default.rq3
-rw-r--r--tests/libtracker-data/graph/add-to-existent.out4
-rw-r--r--tests/libtracker-data/graph/add-to-existent.rq6
-rw-r--r--tests/libtracker-data/graph/add-to-non-existent.out2
-rw-r--r--tests/libtracker-data/graph/add-to-non-existent.rq5
-rw-r--r--tests/libtracker-data/graph/add.out3
-rw-r--r--tests/libtracker-data/graph/add.rq6
-rw-r--r--tests/libtracker-data/graph/clear-all.out0
-rw-r--r--tests/libtracker-data/graph/clear-all.rq4
-rw-r--r--tests/libtracker-data/graph/clear-default.out2
-rw-r--r--tests/libtracker-data/graph/clear-default.rq4
-rw-r--r--tests/libtracker-data/graph/clear-named.out1
-rw-r--r--tests/libtracker-data/graph/clear-named.rq4
-rw-r--r--tests/libtracker-data/graph/clear-non-existent.out0
-rw-r--r--tests/libtracker-data/graph/clear-non-existent.rq5
-rw-r--r--tests/libtracker-data/graph/clear.out1
-rw-r--r--tests/libtracker-data/graph/clear.rq5
-rw-r--r--tests/libtracker-data/graph/copy-from-default.out1
-rw-r--r--tests/libtracker-data/graph/copy-from-default.rq5
-rw-r--r--tests/libtracker-data/graph/copy-from-non-existent.out1
-rw-r--r--tests/libtracker-data/graph/copy-from-non-existent.rq5
-rw-r--r--tests/libtracker-data/graph/copy-into-self.out1
-rw-r--r--tests/libtracker-data/graph/copy-into-self.rq5
-rw-r--r--tests/libtracker-data/graph/copy-to-default.out1
-rw-r--r--tests/libtracker-data/graph/copy-to-default.rq3
-rw-r--r--tests/libtracker-data/graph/copy-to-existent.out2
-rw-r--r--tests/libtracker-data/graph/copy-to-existent.rq6
-rw-r--r--tests/libtracker-data/graph/copy-to-non-existent.out2
-rw-r--r--tests/libtracker-data/graph/copy-to-non-existent.rq5
-rw-r--r--tests/libtracker-data/graph/copy.out2
-rw-r--r--tests/libtracker-data/graph/copy.rq5
-rw-r--r--tests/libtracker-data/graph/data-2.rq4
-rw-r--r--tests/libtracker-data/graph/data-5.rq10
-rw-r--r--tests/libtracker-data/graph/data-add-from-default.rq10
-rw-r--r--tests/libtracker-data/graph/data-add-from-non-existent.rq8
-rw-r--r--tests/libtracker-data/graph/data-add-into-self.rq8
-rw-r--r--tests/libtracker-data/graph/data-add-to-default.rq10
-rw-r--r--tests/libtracker-data/graph/data-add-to-existent.rq14
-rw-r--r--tests/libtracker-data/graph/data-add-to-non-existent.rq8
-rw-r--r--tests/libtracker-data/graph/data-add.rq12
-rw-r--r--tests/libtracker-data/graph/data-clear-all.rq12
-rw-r--r--tests/libtracker-data/graph/data-clear-default.rq12
-rw-r--r--tests/libtracker-data/graph/data-clear-named.rq12
-rw-r--r--tests/libtracker-data/graph/data-clear-non-existent.rq1
-rw-r--r--tests/libtracker-data/graph/data-clear.rq12
-rw-r--r--tests/libtracker-data/graph/data-copy-from-default.rq10
-rw-r--r--tests/libtracker-data/graph/data-copy-from-non-existent.rq8
-rw-r--r--tests/libtracker-data/graph/data-copy-into-self.rq8
-rw-r--r--tests/libtracker-data/graph/data-copy-to-default.rq10
-rw-r--r--tests/libtracker-data/graph/data-copy-to-existent.rq14
-rw-r--r--tests/libtracker-data/graph/data-copy-to-non-existent.rq8
-rw-r--r--tests/libtracker-data/graph/data-copy.rq10
-rw-r--r--tests/libtracker-data/graph/data-drop-all.rq12
-rw-r--r--tests/libtracker-data/graph/data-drop-default.rq12
-rw-r--r--tests/libtracker-data/graph/data-drop-named.rq12
-rw-r--r--tests/libtracker-data/graph/data-drop-non-existent.rq1
-rw-r--r--tests/libtracker-data/graph/data-drop-silent.rq1
-rw-r--r--tests/libtracker-data/graph/data-drop.rq12
-rw-r--r--tests/libtracker-data/graph/data-move-from-default.rq10
-rw-r--r--tests/libtracker-data/graph/data-move-from-non-existent.rq8
-rw-r--r--tests/libtracker-data/graph/data-move-into-self.rq8
-rw-r--r--tests/libtracker-data/graph/data-move-to-default.rq10
-rw-r--r--tests/libtracker-data/graph/data-move-to-existent.rq14
-rw-r--r--tests/libtracker-data/graph/data-move.rq8
-rw-r--r--tests/libtracker-data/graph/drop-all.out0
-rw-r--r--tests/libtracker-data/graph/drop-all.rq4
-rw-r--r--tests/libtracker-data/graph/drop-default.out2
-rw-r--r--tests/libtracker-data/graph/drop-default.rq4
-rw-r--r--tests/libtracker-data/graph/drop-named.out1
-rw-r--r--tests/libtracker-data/graph/drop-named.rq4
-rw-r--r--tests/libtracker-data/graph/drop-non-existent.out0
-rw-r--r--tests/libtracker-data/graph/drop-non-existent.rq5
-rw-r--r--tests/libtracker-data/graph/drop-silent.out0
-rw-r--r--tests/libtracker-data/graph/drop-silent.rq5
-rw-r--r--tests/libtracker-data/graph/drop.out1
-rw-r--r--tests/libtracker-data/graph/drop.rq5
-rw-r--r--tests/libtracker-data/graph/graph-1.out1
-rw-r--r--tests/libtracker-data/graph/graph-4.rq3
-rw-r--r--tests/libtracker-data/graph/graph-5.rq3
-rw-r--r--tests/libtracker-data/graph/graph-6.out2
-rw-r--r--tests/libtracker-data/graph/graph-6.rq6
-rw-r--r--tests/libtracker-data/graph/move-from-default.out1
-rw-r--r--tests/libtracker-data/graph/move-from-default.rq5
-rw-r--r--tests/libtracker-data/graph/move-from-non-existent.out1
-rw-r--r--tests/libtracker-data/graph/move-from-non-existent.rq5
-rw-r--r--tests/libtracker-data/graph/move-into-self.out1
-rw-r--r--tests/libtracker-data/graph/move-into-self.rq5
-rw-r--r--tests/libtracker-data/graph/move-to-default.out1
-rw-r--r--tests/libtracker-data/graph/move-to-default.rq3
-rw-r--r--tests/libtracker-data/graph/move-to-existent.out1
-rw-r--r--tests/libtracker-data/graph/move-to-existent.rq6
-rw-r--r--tests/libtracker-data/graph/move.out1
-rw-r--r--tests/libtracker-data/graph/move.rq5
-rw-r--r--tests/libtracker-data/graph/non-existent-1.out0
-rw-r--r--tests/libtracker-data/graph/non-existent-1.rq5
-rw-r--r--tests/libtracker-data/langstring/data.rq16
-rw-r--r--tests/libtracker-data/langstring/langmatches.out2
-rw-r--r--tests/libtracker-data/langstring/langmatches.rq1
-rw-r--r--tests/libtracker-data/langstring/match-non-langstring.out1
-rw-r--r--tests/libtracker-data/langstring/match-non-langstring.rq1
-rw-r--r--tests/libtracker-data/langstring/match-with-langstring.out1
-rw-r--r--tests/libtracker-data/langstring/match-with-langstring.rq1
-rw-r--r--tests/libtracker-data/langstring/match-with-non-langstring.out1
-rw-r--r--tests/libtracker-data/langstring/match-with-non-langstring.rq1
-rw-r--r--tests/libtracker-data/langstring/strlang.out1
-rw-r--r--tests/libtracker-data/langstring/strlang.rq1
-rw-r--r--tests/libtracker-data/langstring/test.ontology19
-rw-r--r--tests/libtracker-data/lists/data-list-in-object.rq8
-rw-r--r--tests/libtracker-data/lists/data-list-in-select.rq8
-rw-r--r--tests/libtracker-data/lists/data-list-in-subject.rq6
-rw-r--r--tests/libtracker-data/lists/data-list-nested.rq17
-rw-r--r--tests/libtracker-data/lists/list-in-object.out4
-rw-r--r--tests/libtracker-data/lists/list-in-object.rq2
-rw-r--r--tests/libtracker-data/lists/list-in-select.out1
-rw-r--r--tests/libtracker-data/lists/list-in-select.rq4
-rw-r--r--tests/libtracker-data/lists/list-in-subject.out4
-rw-r--r--tests/libtracker-data/lists/list-in-subject.rq2
-rw-r--r--tests/libtracker-data/lists/list-nested.out10
-rw-r--r--tests/libtracker-data/lists/list-nested.rq1
-rw-r--r--tests/libtracker-data/lists/test.ontology19
-rw-r--r--tests/libtracker-data/meson.build9
-rw-r--r--tests/libtracker-data/optional/q-opt-complex-1.rq3
-rw-r--r--tests/libtracker-data/property-paths/data.ttl9
-rw-r--r--tests/libtracker-data/tracker-backup-test.c26
-rw-r--r--tests/libtracker-data/tracker-db-journal-test.c405
-rw-r--r--tests/libtracker-data/tracker-ontology-change-test.c4
-rw-r--r--tests/libtracker-data/tracker-ontology-test.c14
-rw-r--r--tests/libtracker-data/tracker-sparql-blank-test.c4
-rw-r--r--tests/libtracker-data/tracker-sparql-test.c92
-rw-r--r--tests/libtracker-fts/meson.build1
-rw-r--r--tests/libtracker-miner/meson.build10
-rw-r--r--tests/libtracker-miner/tracker-miner-fs-test.c13
-rw-r--r--tests/libtracker-miner/tracker-monitor-test.c2
-rw-r--r--tests/libtracker-sparql/meson.build6
-rw-r--r--tests/libtracker-sparql/tracker-sparql-test.c11
-rw-r--r--tests/meson.build13
-rw-r--r--tests/services/meson.build2
-rw-r--r--tests/test-bus.conf.in8
-rw-r--r--tests/tracker-steroids/meson.build1
-rwxr-xr-xutils/sandbox/tracker-sandbox.py601
-rw-r--r--utils/trackertestutils/__main__.py479
-rw-r--r--utils/trackertestutils/dbusdaemon.py182
-rw-r--r--utils/trackertestutils/dconf.py69
-rw-r--r--utils/trackertestutils/helpers.py290
-rw-r--r--utils/trackertestutils/meson.build36
-rw-r--r--utils/trackertestutils/psutil_mini.py98
-rwxr-xr-xutils/trackertestutils/tracker-sandbox.in5
307 files changed, 9674 insertions, 8222 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 87e32d5ee..0616811d8 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,3 +1,15 @@
+# The container images used in this pipeline are built from this
+# GitLab project: https://gitlab.gnome.org/GNOME/tracker-oci-images
+
+variables:
+ # These can be used to see verbose log output from the functional-tests.
+ # See HACKING.md for more information.
+ TRACKER_VERBOSITY: "1"
+ TRACKER_TESTS_VERBOSE: "no"
+
+ # This can be used when debugging test failures that only occur within GitLab CI.
+ MESON_TEST_EXTRA_ARGS: ""
+
stages:
- test
@@ -7,7 +19,7 @@ test-fedora-latest:
script:
- su tracker -c 'mkdir build'
- - su tracker -c 'cd build; meson .. -Db_lto=true'
+ - su tracker -c 'cd build; meson .. -Db_lto=true -Db_coverage=true'
- su tracker -c 'cd build; ninja'
- |
# Remove the many "CI_" variables from the environment. Meson dumps the
@@ -15,7 +27,20 @@ test-fedora-latest:
# screenful of junk each time unless we strip these.
unset $(env|grep -o '^CI_[^=]*')
- su tracker -c 'cd build; meson test --print-errorlogs'
+ # We limit `meson test` to 4 parallel test processes. The default is to
+ # have one test process per CPU, but our tests are mostly IO bound
+ # and we get timeouts and failures if we try to run 32 of them at once.
+ su tracker -c 'cd build; meson test --num-processes=4 --print-errorlogs ${MESON_TEST_EXTRA_ARGS}'
+
+ after_script:
+ - |
+ echo "Test suite settings:"
+ echo
+ echo " TRACKER_VERBOSITY: ${TRACKER_VERBOSITY}"
+ echo " TRACKER_TESTS_VERBOSE: ${TRACKER_TESTS_VERBOSE}"
+ echo " MESON_TEST_EXTRA_ARGS: ${MESON_TEST_EXTRA_ARGS}"
+ echo
+ echo "These values can be set at https://gitlab.gnome.org/GNOME/tracker/pipelines/new"
artifacts:
when: always
diff --git a/HACKING.md b/HACKING.md
new file mode 100644
index 000000000..dea503369
--- /dev/null
+++ b/HACKING.md
@@ -0,0 +1,62 @@
+# Logging
+
+The following environment variables control logging from Tracker daemons:
+
+ * `TRACKER_VERBOSITY`: takes a value of 1, 2 or 3 and causes increasing
+ amounts of log output from Tracker code to be written to stdout.
+ * `G_MESSAGES_DEBUG`: controls log output from GLib-based libraries that
+ are used in the Tracker process. Use `G_MESSAGES_DEBUG=all` for maximal
+ log output.
+
+Internally, Tracker will set `G_MESSAGES_DEBUG=Tracker` if `TRACKER_VERBOSITY`
+is set and `G_MESSAGES_DEBUG` is not set, to enable printing of its own log
+messages to stdout.
+
+You can set these variables when using `tracker-sandbox`, and when running the
+Tracker test suite. Note that Meson will not print log output from tests by
+default, use `meson test --verbose` or `meson test --print-errorlogs` to
+enable.
+
+The functional tests understand an additional variable, `TRACKER_TESTS_VERBOSE`
+with can be set to `1` or `yes` to see detailed logging from the test harness
+itself, and full log output from the internal D-Bus daemon. By default, these
+tests filter output from the D-Bus daemon to only show log messages from
+Tracker processes. Anything written directly to stdout, for example by
+`g_print()` or by the dbus-daemon itself, will not be displayed unless
+`TRACKER_TESTS_VERBOSE` is set.
+
+When working with GitLab CI, you can use the
+[Run Pipeline dialog](https://gitlab.gnome.org/GNOME/tracker/pipelines/new)
+to set the values of these variables and increase the verbosity of the tests in
+CI.
+
+# Attaching a debugger to Tracker daemons
+
+Tracker daemons are not started directly. Instead they are started by the D-Bus
+daemon by request. When using the run-uninstalled script or the
+functional-tests, it's difficult to start the daemon manually under `gdb`.
+
+Instead, we recommend adding a 10 second timeout at the top of the daemon's
+main() function. In Vala code, try this:
+
+ print("Pausing to attach debugger. Run: gdb attach %i\n", Posix.getpid());
+ Posix.usleep(10 * 1000 * 1000);
+ print("Waking up again\n");
+
+Run the test, using the `meson build --timeout-multiplier=10000`
+option to avoid your process being killed by the test runner. When you see
+the 'Pausing' message, run the `gdb attach``command in another terminal within
+10 seconds.
+
+# Running Tracker daemons under Valgrind
+
+The Tracker daemons are launched using D-Bus autolaunch. When running them from
+the source tree using the run-uninstalled script or the functional-tests, the
+commandline is controlled by the D-Bus .service.in files stored in
+`./tests/services`. Just change the `Exec=` line to add Valgrind, like this:
+
+ Exec=/usr/bin/valgrind @abs_top_builddir@/src/tracker-store/tracker-store
+
+By default the run-uninstalled script and the functional-tests will only show
+output from Tracker code. For the functional-tests, set TRACKER_TESTS_VERBOSE=1
+to see output from Valgrind. For tracker-sandbox use the `--debug-dbus` option.
diff --git a/NEWS b/NEWS
index d54849ed5..147a474b9 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,24 @@
+NEW in 2.3.0 - 2019-09-09
+=========================
+
+ Highlighted changes since 2.2.x:
+ --------------------------------
+ * Support for storing external references for other services
+ (eg. musicbrainz)
+
+NEW in 2.2.99.1 - 2019-09-05
+============================
+
+ * Handle circular references in TrackerResource
+ * Removed stress tests
+ * Handle application/x-zero-size
+ * Refactors and cleanups in functional testing infrastructure
+ * Documentation improvements
+ * Make tracker_sparql_escape_string() escape single quotes
+ * Don't make tracker:referenceSource a subproperty of nie:identifier
+
+Translations: eu
+
NEW in 2.2.99.0 - 2019-08-07
============================
diff --git a/README.md b/README.md
index 48c374e6f..83e0b9199 100644
--- a/README.md
+++ b/README.md
@@ -84,48 +84,50 @@ At this point you can run the Tracker test suite from the `build` directory:
meson test --print-errorlogs
-## Developing with tracker-sandbox
+## Using the run-uninstalled script
Tracker normally runs automatically, indexing content in the background so that
search results are available quickly when needed.
When developing and testing Tracker you will normally want it to run in the
-foreground instead. The `tracker-sandbox` tool exists to help with this.
+foreground instead. You also probably want to run it from a build tree, rather
+than installing it somewhere everytime you make a change, and you certainly
+should isolates your development version from the real Tracker database in your
+home directory.
-You can run the tool directly from the tracker.git source tree. Ensure you are
-in the top of the tracker source tree and type this to see the --help output:
+There is a tool to help with this, which is part of the 'trackertestutils'
+Python module. You can run the tool using a helper script generated in the
+tracker-miners.git build process named 'run-uninstalled'.
- ./utils/sandbox/tracker-sandbox.py --help
+Check the helper script is set up correctly by running this from your
+tracker-miners.git build tree:
-You should always pass the `--prefix` option, which should be the same as the
---prefix argument you passed to Meson. You also need to use `--index` which
-controls where internal state files like the database are kept. You may also
-want to pass `--debug` to see detailed log output.
+ ./run-uninstalled --help
-Now you can index some files using `--update` mode. Here's how to index files
-in `~/Documents` for example:
+If run with no arguments, the script will start an interactive shell. Any
+arguments after a `--` sentinel are treated as a command to run in a non-interactive
+shell.
- ./utils/sandbox/tracker-sandbox.py --prefix ~/opt/tracker --index ~/tracker-content \
- --update --content ~/Documents
+Now check that it runs the correct version of the Tracker CLI:
-You can then list the files that have been indexed...
+ ./run-uninstalled -- tracker --version
- ./utils/sandbox/tracker-sandbox.py --prefix ~/opt/tracker --index ~/tracker-content \
- --list-files
+Let's try and index some content. (Subtitute ~/Music for any other location
+where you have interesting data). We need to explicitly tell the script to wait
+for the miners to finish, or it will exit too soon. (This is a workaround for
+[issue #122](https://gitlab.gnome.org/GNOME/tracker/issues/122).)
-... run a full-text search ...
+ ./run-uninstalled --wait-for-miner=Files --wait-for-miner=Extract -- tracker index --file ~/Music
- ./utils/sandbox/tracker-sandbox.py --prefix ~/opt/tracker --index ~/tracker-content \
- --search "bananas"
+Let's see what files were found!
-... or run a SPARQL query on the content:
+ ./run-uninstalled -- tracker sparql -q 'SELECT ?url { ?u nie:url ?url } }'
- ./utils/sandbox/tracker-sandbox.py --prefix ~/opt/tracker --index ~/tracker-content \
- --sparql "SELECT ?url { ?resource a nfo:FileDataObject ; nie:url ?url . }"
+Or, you can try a full-text search ...
-You can also open a shell inside the sandbox environment. From here you can run
-the `tracker` commandline tool, and you can run the Tracker daemons manually
-under a debugger such as GDB.
+ ./run-uninstalled -- tracker search "bananas"
+
+There are many more things you can do with the script.
For more information about developing Tracker, look at
-https://wiki.gnome.org/Projects/Tracker.
+https://wiki.gnome.org/Projects/Tracker and HACKING.md.
diff --git a/data/meson.build b/data/meson.build
index 16e70d339..877c9fbec 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -6,7 +6,7 @@ tracker_gsettings_schemas = files([
])
foreach schema : tracker_gsettings_schemas
- schemas = configure_file(
+ configure_file(
input: schema,
output: '@PLAINNAME@',
copy: true,
@@ -35,6 +35,7 @@ custom_target('compile-schemas',
output: 'gschemas.compiled',
command: [glib_compile_schemas, meson.current_build_dir()],
build_by_default: true,
- depends: tracker_store_settings_enums)
+ depends: tracker_store_settings_enums,
+ depend_files: tracker_gsettings_schemas)
tracker_uninstalled_gsettings_schema_dir = meson.current_build_dir()
diff --git a/docs/reference/libtracker-sparql/libtracker-sparql-docs.xml b/docs/reference/libtracker-sparql/libtracker-sparql-docs.xml
index 5f5611e18..e599974cd 100644
--- a/docs/reference/libtracker-sparql/libtracker-sparql-docs.xml
+++ b/docs/reference/libtracker-sparql/libtracker-sparql-docs.xml
@@ -53,6 +53,7 @@
<xi:include href="private-store.xml"/>
<xi:include href="examples.xml"/>
<xi:include href="migrating-1to2.xml"/>
+ <xi:include href="migrating-2to3.xml"/>
<index id="api-index-full">
<title>Index</title>
diff --git a/docs/reference/libtracker-sparql/migrating-2to3.xml b/docs/reference/libtracker-sparql/migrating-2to3.xml
new file mode 100644
index 000000000..9c4201f60
--- /dev/null
+++ b/docs/reference/libtracker-sparql/migrating-2to3.xml
@@ -0,0 +1,71 @@
+<?xml version='1.0'?>
+
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+ "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
+<!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
+]>
+<chapter id="tracker-migrating-2-to-3">
+ <title>Migrating from libtracker-sparql 2.x to 3.0</title>
+
+ <para>
+ Tracker 3.0 is a new major version, containing some large
+ syntax and conceptual changes.
+ </para>
+
+ <section>
+ <title>Graph semantics</title>
+ <para>
+ One of the big features in 3.0 is full SPARQL 1.1 syntax
+ support. Besides the missing syntax additions, one conceptually
+ big change is the handling of graphs in the database.
+ </para>
+ <para>
+ In 2.x, there was a minimum concept of graphs, but it didn't
+ represent what is defined in the standard. You could attribute
+ a graph to a data triple, but a given triple could only reside
+ in one graph at a time. In other words, this yields the wrong
+ result:
+ </para>
+ <programlisting>
+INSERT { GRAPH &lt;A&gt; { &lt;foo&gt; nie:title 'Hello' } }
+INSERT { GRAPH &lt;B&gt; { &lt;foo&gt; nie:title 'Hola' } }
+
+# We expect 2 rows, 2.x returns 1.
+SELECT ?g ?t { GRAPH ?g { &lt;foo&gt; nie:title ?t } }
+ </programlisting>
+ <para>
+ 3.0 implements the graph semantics as defined in the SPARQL 1.1
+ documents. So the SELECT query would return both graphs.
+ </para>
+ <para>
+ 3.0 also honors properly the concept of «Unnamed graph». This
+ graph is always used whenever no graph is specified, and always
+ skipped if a GRAPH is requested or defined, e.g.:
+ </para>
+ <programlisting>
+# Inserts element into the unnamed graph
+INSERT { &lt;foo&gt; a nfo:FileDataObject }
+
+# Inserts element into named graph A
+INSERT { GRAPH &lt;A&gt; { &lt;bar&gt; a nfo:FileDataObject } }
+
+# Queries from all named graphs, A in this case
+SELECT ?g ?s { GRAPH ?g { ?s a nfo:FileDataObject } }
+
+# Queries the default graph, which includes the unnamed graph
+SELECT ?s { ?s a nfo:FileDataObject }
+ </programlisting>
+ <para>
+ 3.0 defines the default graph to be the union of the unnamed
+ graph plus all known named graphs. So the last query in the
+ example above would return results from both the unnamed graph
+ and graph A. This behavior can be influenced with FROM/FROM NAMED
+ syntax (also newly handled in 3.0)
+ </para>
+ <para>
+ In contrast, 2.x does not distinguish between named and unnamed
+ graphs. The first SELECT query would return a row for the unnamed
+ graph, with ?g being NULL.
+ </para>
+ </section>
+</chapter>
diff --git a/docs/tools/ttl_loader.c b/docs/tools/ttl_loader.c
index 4f882d077..c9e8b6980 100644
--- a/docs/tools/ttl_loader.c
+++ b/docs/tools/ttl_loader.c
@@ -26,6 +26,7 @@
/* Ontology classes */
#define RDFS_CLASS "http://www.w3.org/2000/01/rdf-schema#Class"
#define RDF_PROPERTY "http://www.w3.org/1999/02/22-rdf-syntax-ns#Property"
+#define RDF_LIST "http://www.w3.org/1999/02/22-rdf-syntax-ns#List"
#define RDFS_SUBCLASSOF "http://www.w3.org/2000/01/rdf-schema#subClassOf"
#define RDFS_TYPE "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
#define RDFS_RANGE "http://www.w3.org/2000/01/rdf-schema#range"
@@ -83,7 +84,8 @@ load_in_memory (Ontology *ontology,
if (!g_strcmp0 (turtle_predicate, RDFS_TYPE)) {
/* It is a definition of class or property */
- if (!g_strcmp0 (turtle_object, RDFS_CLASS)) {
+ if (!g_strcmp0 (turtle_object, RDFS_CLASS) ||
+ !g_strcmp0 (turtle_object, RDF_LIST)) {
g_hash_table_insert (ontology->classes,
g_strdup (turtle_subject),
ttl_model_class_new (turtle_subject));
diff --git a/meson.build b/meson.build
index 24582c820..b7049890f 100644
--- a/meson.build
+++ b/meson.build
@@ -1,9 +1,10 @@
project('tracker', 'c', 'vala',
- version: '2.2.99.0',
+ version: '2.99.0',
meson_version: '>=0.47')
gnome = import('gnome')
i18n = import('i18n')
+pkg = import('pkgconfig')
cc = meson.get_compiler('c')
# This is the X.Y used in -llibtracker-FOO-X.Y
@@ -241,8 +242,6 @@ endif
conf = configuration_data()
# Config that goes in config.h
-conf.set('DISABLE_JOURNAL', get_option('journal') == false)
-
conf.set10('HAVE_TRACKER_FTS', enable_fts)
conf.set('HAVE_BUILTIN_FTS', sqlite3_has_builtin_fts5)
@@ -269,34 +268,9 @@ conf.set('TRACKER_MICRO_VERSION', tracker_micro_version)
conf.set('TRACKER_INTERFACE_AGE', 0)
conf.set('TRACKER_BINARY_AGE', 100 * tracker_minor_version + tracker_micro_version)
-# Config that goes in some other generated files (.desktop, .pc, etc)
+# Config that goes in some other generated files (.desktop, .service, etc)
conf.set('abs_top_builddir', meson.current_build_dir())
-conf.set('exec_prefix', get_option('prefix'))
-conf.set('bindir', join_paths(get_option('prefix'), get_option('bindir')))
-conf.set('datadir', datadir)
-conf.set('datarootdir', join_paths(get_option('prefix'), get_option('datadir')))
-conf.set('includedir', join_paths(get_option('prefix'), get_option('includedir')))
-conf.set('libdir', libdir)
conf.set('libexecdir', join_paths(get_option('prefix'), get_option('libexecdir')))
-conf.set('prefix', get_option('prefix'))
-conf.set('TRACKER_API_VERSION', tracker_api_version)
-conf.set('VERSION', meson.project_version())
-conf.set('tracker_store', join_paths ('${libexecdir}', 'tracker-store'))
-conf.set('ontologies_dir', join_paths ('${datadir}', 'tracker', 'ontologies'))
-conf.set('domain_ontologies_dir', join_paths('${datadir}', 'tracker', 'domain-ontologies'))
-
-# Configure functional tests to run completely from source tree.
-conf.set('FUNCTIONAL_TESTS_ONTOLOGIES_DIR', join_paths(meson.current_source_dir(), 'tests', 'functional-tests', 'test-ontologies'))
-conf.set('FUNCTIONAL_TESTS_TRACKER_STORE_PATH', join_paths(meson.current_build_dir(), 'src', 'tracker-store', 'tracker-store'))
-
-# This is set in an awkward way for compatibility with Autoconf. Switch it
-# to a normal boolean once we get rid of the Autotools build system. It's
-# only used in tests/functional-tests/common/utils/configuration.py.in.
-if get_option('journal')
- conf.set('DISABLE_JOURNAL_TRUE', 'true')
-else
- conf.set('DISABLE_JOURNAL_TRUE', '')
-endif
configure_file(input: 'config.h.meson.in',
output: 'config.h',
@@ -352,6 +326,7 @@ test_c_args = tracker_c_args + [
'-DTOP_SRCDIR="@0@"'.format(source_root),
]
+tracker_uninstalled_cli_dir = join_paths(meson.current_build_dir(), 'src', 'tracker')
tracker_uninstalled_domain_rule = join_paths(meson.current_source_dir(), 'src', 'tracker-store', 'default.rule')
tracker_uninstalled_nepomuk_ontologies_dir = join_paths(meson.current_source_dir(), 'src', 'ontologies', 'nepomuk')
tracker_uninstalled_stop_words_dir = join_paths(meson.current_source_dir(), 'src', 'libtracker-common', 'stop-words')
diff --git a/meson_options.txt b/meson_options.txt
index fa1ce51dc..509b3479f 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -22,3 +22,7 @@ option('dbus_services', type: 'string', value: '',
description: 'Directory to install D-Bus .service files (leave blank to use the value from dbus-1.pc)')
option('systemd_user_services', type: 'string', value: 'yes',
description: 'Directory to install systemd user .service files (or "yes" for default directory, "no" to disable installation)')
+option('test_utils', type: 'boolean', value: true,
+ description: 'Whether to install the trackertestutils Python package')
+option('test_utils_dir', type: 'string', value: '',
+ description: 'Directory to install trackertestutils Python package (or empty to use the default)')
diff --git a/po/sk.po b/po/sk.po
index d2eb738ea..e23b1dd0b 100644
--- a/po/sk.po
+++ b/po/sk.po
@@ -8,10 +8,9 @@
msgid ""
msgstr ""
"Project-Id-Version: tracker HEAD\n"
-"Report-Msgid-Bugs-To: https://bugzilla.gnome.org/enter_bug.cgi?"
-"product=tracker&keywords=I18N+L10N&component=General\n"
-"POT-Creation-Date: 2017-08-31 17:34+0000\n"
-"PO-Revision-Date: 2017-09-02 10:18+0200\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/tracker/issues\n"
+"POT-Creation-Date: 2019-09-10 06:32+0000\n"
+"PO-Revision-Date: 2019-09-26 09:33+0200\n"
"Last-Translator: Dušan Kazik <prescott66@gmail.com>\n"
"Language-Team: Slovak <sk-i18n@lists.linux.sk>\n"
"Language: sk\n"
@@ -19,110 +18,51 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n"
-"X-Generator: Poedit 2.0.3\n"
+"X-Generator: Poedit 2.2.1\n"
-# tab
-#: ../examples/rss-reader/rss_viewer.ui.h:1
-msgid "All posts"
-msgstr "Všetky príspevky"
-
-# tab
-#: ../examples/rss-reader/rss_viewer.ui.h:2
-msgid "By usage"
-msgstr "Podľa použitia"
-
-#: ../src/libtracker-common/tracker-utils.c:90
-msgid "unknown time"
-msgstr "neznámy čas"
-
-#: ../src/libtracker-common/tracker-utils.c:104
-#: ../src/libtracker-common/tracker-utils.c:155
-msgid "less than one second"
-msgstr "menej ako jedna sekunda"
-
-# PM: treba aby to bolo čo najrkatšie
-#. Translators: this is %d days
-#: ../src/libtracker-common/tracker-utils.c:119
-#, c-format
-msgid " %dd"
-msgstr " %dd"
-
-#. Translators: this is %2.2d hours
-#: ../src/libtracker-common/tracker-utils.c:123
-#, c-format
-msgid " %2.2dh"
-msgstr " %2.2dh"
-
-#. Translators: this is %2.2d minutes
-#: ../src/libtracker-common/tracker-utils.c:127
-#, c-format
-msgid " %2.2dm"
-msgstr " %2.2dm"
-
-#. Translators: this is %2.2d seconds
-#: ../src/libtracker-common/tracker-utils.c:131
-#, c-format
-msgid " %2.2ds"
-msgstr " %2.2ds"
-
-#: ../src/libtracker-common/tracker-utils.c:135
-#, c-format
-msgid " %d day"
-msgid_plural " %d days"
-msgstr[0] " %d dní"
-msgstr[1] " %d deň"
-msgstr[2] " %d dni"
+#: data/org.freedesktop.Tracker.DB.gschema.xml:24
+msgid "Maximum size of journal"
+msgstr "Maximálna veľkosť žurnálu"
-#: ../src/libtracker-common/tracker-utils.c:139
-#, c-format
-msgid " %2.2d hour"
-msgid_plural " %2.2d hours"
-msgstr[0] " %2.2d hodín"
-msgstr[1] " %2.2d hodina"
-msgstr[2] " %2.2d hodiny"
+#: data/org.freedesktop.Tracker.DB.gschema.xml:25
+msgid "Size of the journal at rotation in MB. Use -1 to disable rotating."
+msgstr ""
+"Veľkosť žurnálu pri rotácii v MB. Použite -1, ak chcete rotáciu vypnúť."
-#: ../src/libtracker-common/tracker-utils.c:143
-#, c-format
-msgid " %2.2d minute"
-msgid_plural " %2.2d minutes"
-msgstr[0] " %2.2d minút"
-msgstr[1] " %2.2d minúta"
-msgstr[2] " %2.2d minúty"
+#: data/org.freedesktop.Tracker.DB.gschema.xml:29
+msgid "Location of journal pieces"
+msgstr "Umiestnenie častí žurnálu"
-#: ../src/libtracker-common/tracker-utils.c:147
-#, c-format
-msgid " %2.2d second"
-msgid_plural " %2.2d seconds"
-msgstr[0] " %2.2d sekúnd"
-msgstr[1] " %2.2d sekunda"
-msgstr[2] " %2.2d sekundy"
+#: data/org.freedesktop.Tracker.DB.gschema.xml:30
+msgid "Where to store a journal chunk when it hits the max size."
+msgstr "Kam sa majú ukladať časti žurnálu, keď dosiahnu maximálnu veľkosť."
-#: ../src/libtracker-fts/org.freedesktop.Tracker.FTS.gschema.xml.in.h:1
+#: data/org.freedesktop.Tracker.FTS.gschema.xml:26
msgid "Maximum length of a word to be indexed"
msgstr "Maximálna dĺžka indexovaného slova"
-#: ../src/libtracker-fts/org.freedesktop.Tracker.FTS.gschema.xml.in.h:2
+#: data/org.freedesktop.Tracker.FTS.gschema.xml:27
msgid ""
"Words with more characters than this length will be ignored by the indexer."
msgstr ""
"Slová s väčším počtom znakov ako je táto dĺžka budú ignorované indexovacím "
"nástrojom."
-#: ../src/libtracker-fts/org.freedesktop.Tracker.FTS.gschema.xml.in.h:3
+#: data/org.freedesktop.Tracker.FTS.gschema.xml:32
msgid "Maximum number of words to index in a document"
msgstr "Maximálny počet indexovaných slov v dokumente"
-#: ../src/libtracker-fts/org.freedesktop.Tracker.FTS.gschema.xml.in.h:4
+#: data/org.freedesktop.Tracker.FTS.gschema.xml:33
msgid ""
"Indexer will read only this maximum number of words from a single document."
msgstr ""
"Indexovací nástroj načíta z jedného dokumentu maximálne takýto počet slov."
-#: ../src/libtracker-fts/org.freedesktop.Tracker.FTS.gschema.xml.in.h:5
+#: data/org.freedesktop.Tracker.FTS.gschema.xml:37
msgid "Enable stemmer"
msgstr "Povoliť skrátenie"
-#: ../src/libtracker-fts/org.freedesktop.Tracker.FTS.gschema.xml.in.h:6
+#: data/org.freedesktop.Tracker.FTS.gschema.xml:38
msgid ""
"Simplify the words to their root to provide more results. E.g. “shelves” and "
"“shelf” to “shel”"
@@ -130,11 +70,11 @@ msgstr ""
"Zjednoduší slová na koreň slova aby poskytol viacero výsledkov. Napr. "
"„shelves“ a „shelf“ skráti na „shel“"
-#: ../src/libtracker-fts/org.freedesktop.Tracker.FTS.gschema.xml.in.h:7
+#: data/org.freedesktop.Tracker.FTS.gschema.xml:43
msgid "Enable unaccent"
msgstr "Povoliť zrušenie prízvukov"
-#: ../src/libtracker-fts/org.freedesktop.Tracker.FTS.gschema.xml.in.h:8
+#: data/org.freedesktop.Tracker.FTS.gschema.xml:44
msgid ""
"Translate accented characters to the equivalent unaccented. E.g. “Idéa” to "
"“Idea” for improved matching."
@@ -142,19 +82,19 @@ msgstr ""
"Preloží znaky s prízvukom na ich ekvivalenty bez prízvuku. Napr. „Idéa“ "
"zmení na „Idea“, čím vylepší porovnávanie."
-#: ../src/libtracker-fts/org.freedesktop.Tracker.FTS.gschema.xml.in.h:9
+#: data/org.freedesktop.Tracker.FTS.gschema.xml:49
msgid "Ignore numbers"
msgstr "Ignorovať čísla"
-#: ../src/libtracker-fts/org.freedesktop.Tracker.FTS.gschema.xml.in.h:10
+#: data/org.freedesktop.Tracker.FTS.gschema.xml:50
msgid "If enabled, numbers will not be indexed."
msgstr "Ak je povolené, čísla nebudú indexované."
-#: ../src/libtracker-fts/org.freedesktop.Tracker.FTS.gschema.xml.in.h:11
+#: data/org.freedesktop.Tracker.FTS.gschema.xml:55
msgid "Ignore stop words"
msgstr "Ignorovať stopslová"
-#: ../src/libtracker-fts/org.freedesktop.Tracker.FTS.gschema.xml.in.h:12
+#: data/org.freedesktop.Tracker.FTS.gschema.xml:56
msgid ""
"If enabled, the words listed in the stop-words list are ignored. E.g. common "
"words like “the”, “yes”, “no”, etc."
@@ -162,22 +102,91 @@ msgstr ""
"Ak je povolené, slová uvedené v zozname stopslov budú ignorované. Ide o "
"slová bez významovej informácie ako sú spojky a predložky."
-#: ../src/libtracker-data/org.freedesktop.Tracker.DB.gschema.xml.in.h:1
-msgid "Maximum size of journal"
-msgstr "Maximálna veľkosť žurnálu"
+#: data/org.freedesktop.Tracker.Store.gschema.xml:24
+msgid "Log verbosity"
+msgstr "Podrobnosť záznamu"
-#: ../src/libtracker-data/org.freedesktop.Tracker.DB.gschema.xml.in.h:2
-msgid "Size of the journal at rotation in MB. Use -1 to disable rotating."
+#: data/org.freedesktop.Tracker.Store.gschema.xml:25
+msgid "Log verbosity."
+msgstr "Podrobnosť záznamu."
+
+#: data/org.freedesktop.Tracker.Store.gschema.xml:29
+msgid "GraphUpdated delay"
+msgstr "Oneskorenie signálu GraphUpdated"
+
+#: data/org.freedesktop.Tracker.Store.gschema.xml:30
+msgid ""
+"Period in milliseconds between GraphUpdated signals being emitted when "
+"indexed data has changed inside the database."
msgstr ""
-"Veľkosť žurnálu pri rotácii v MB. Použite -1, ak chcete rotáciu vypnúť."
+"Doba v milisekundách medzi vyslaniami signálov GraphUpdated, keď sú "
+"indexované údaje zmenené v databáze."
-#: ../src/libtracker-data/org.freedesktop.Tracker.DB.gschema.xml.in.h:3
-msgid "Location of journal pieces"
-msgstr "Umiestnenie častí žurnálu"
+#: src/libtracker-common/tracker-utils.c:90
+msgid "unknown time"
+msgstr "neznámy čas"
-#: ../src/libtracker-data/org.freedesktop.Tracker.DB.gschema.xml.in.h:4
-msgid "Where to store a journal chunk when it hits the max size."
-msgstr "Kam sa majú ukladať časti žurnálu, keď dosiahnu maximálnu veľkosť."
+#: src/libtracker-common/tracker-utils.c:104
+#: src/libtracker-common/tracker-utils.c:155
+msgid "less than one second"
+msgstr "menej ako jedna sekunda"
+
+# PM: treba aby to bolo čo najrkatšie
+#. Translators: this is %d days
+#: src/libtracker-common/tracker-utils.c:119
+#, c-format
+msgid " %dd"
+msgstr " %dd"
+
+#. Translators: this is %2.2d hours
+#: src/libtracker-common/tracker-utils.c:123
+#, c-format
+msgid " %2.2dh"
+msgstr " %2.2dh"
+
+#. Translators: this is %2.2d minutes
+#: src/libtracker-common/tracker-utils.c:127
+#, c-format
+msgid " %2.2dm"
+msgstr " %2.2dm"
+
+#. Translators: this is %2.2d seconds
+#: src/libtracker-common/tracker-utils.c:131
+#, c-format
+msgid " %2.2ds"
+msgstr " %2.2ds"
+
+#: src/libtracker-common/tracker-utils.c:135
+#, c-format
+msgid " %d day"
+msgid_plural " %d days"
+msgstr[0] " %d dní"
+msgstr[1] " %d deň"
+msgstr[2] " %d dni"
+
+#: src/libtracker-common/tracker-utils.c:139
+#, c-format
+msgid " %2.2d hour"
+msgid_plural " %2.2d hours"
+msgstr[0] " %2.2d hodín"
+msgstr[1] " %2.2d hodina"
+msgstr[2] " %2.2d hodiny"
+
+#: src/libtracker-common/tracker-utils.c:143
+#, c-format
+msgid " %2.2d minute"
+msgid_plural " %2.2d minutes"
+msgstr[0] " %2.2d minút"
+msgstr[1] " %2.2d minúta"
+msgstr[2] " %2.2d minúty"
+
+#: src/libtracker-common/tracker-utils.c:147
+#, c-format
+msgid " %2.2d second"
+msgid_plural " %2.2d seconds"
+msgstr[0] " %2.2d sekúnd"
+msgstr[1] " %2.2d sekunda"
+msgstr[2] " %2.2d sekundy"
#. Translators: this is a '|' (U+007C) separated list of common
#. * title beginnings. Meant to be skipped for sorting purposes,
@@ -185,68 +194,64 @@ msgstr "Kam sa majú ukladať časti žurnálu, keď dosiahnu maximálnu veľkos
#. * advised to leave the untranslated articles in addition to
#. * the translated ones.
#.
-#: ../src/libtracker-data/tracker-collation.c:333
+#: src/libtracker-data/tracker-collation.c:333
msgid "the|a|an"
msgstr "the|a|an"
-#: ../src/libtracker-data/tracker-data-backup.c:495
-#: ../src/libtracker-data/tracker-data-backup.c:635
+#: src/libtracker-data/tracker-data-backup.c:494
+#: src/libtracker-data/tracker-data-backup.c:634
msgid "Error starting “tar” program"
msgstr "Chyba pri spúšťaní programu „tar“"
-#: ../src/libtracker-data/tracker-data-backup.c:496
-#: ../src/libtracker-data/tracker-data-backup.c:636
-#: ../src/tracker/tracker-config.c:61 ../src/tracker/tracker-daemon.c:462
-#: ../src/tracker/tracker-daemon.c:483 ../src/tracker/tracker-daemon.c:708
-#: ../src/tracker/tracker-daemon.c:740 ../src/tracker/tracker-daemon.c:890
-#: ../src/tracker/tracker-daemon.c:948 ../src/tracker/tracker-daemon.c:983
-#: ../src/tracker/tracker-daemon.c:1052 ../src/tracker/tracker-daemon.c:1243
-#: ../src/tracker/tracker-daemon.c:1309 ../src/tracker/tracker-daemon.c:1668
-#: ../src/tracker/tracker-dbus.c:45 ../src/tracker/tracker-dbus.c:63
-#: ../src/tracker/tracker-index.c:123 ../src/tracker/tracker-index.c:155
-#: ../src/tracker/tracker-index.c:196 ../src/tracker/tracker-index.c:272
-#: ../src/tracker/tracker-index.c:329 ../src/tracker/tracker-info.c:264
-#: ../src/tracker/tracker-process.c:80 ../src/tracker/tracker-process.c:206
-#: ../src/tracker/tracker-process.c:335 ../src/tracker/tracker-process.c:356
-#: ../src/tracker/tracker-search.c:1581 ../src/tracker/tracker-sparql.c:173
-#: ../src/tracker/tracker-sparql.c:1087 ../src/tracker/tracker-status.c:74
-#: ../src/tracker/tracker-status.c:303 ../src/tracker/tracker-status.c:313
-#: ../src/tracker/tracker-status.c:387 ../src/tracker/tracker-status.c:432
-#: ../src/tracker/tracker-status.c:461 ../src/tracker/tracker-tag.c:977
+#: src/libtracker-data/tracker-data-backup.c:495
+#: src/libtracker-data/tracker-data-backup.c:635
+#: src/tracker/tracker-config.c:61 src/tracker/tracker-daemon.c:462
+#: src/tracker/tracker-daemon.c:483 src/tracker/tracker-daemon.c:708
+#: src/tracker/tracker-daemon.c:740 src/tracker/tracker-daemon.c:890
+#: src/tracker/tracker-daemon.c:948 src/tracker/tracker-daemon.c:983
+#: src/tracker/tracker-daemon.c:1052 src/tracker/tracker-daemon.c:1243
+#: src/tracker/tracker-daemon.c:1309 src/tracker/tracker-daemon.c:1670
+#: src/tracker/tracker-dbus.c:45 src/tracker/tracker-dbus.c:63
+#: src/tracker/tracker-index.c:123 src/tracker/tracker-index.c:155
+#: src/tracker/tracker-index.c:196 src/tracker/tracker-index.c:272
+#: src/tracker/tracker-index.c:329 src/tracker/tracker-info.c:264
+#: src/tracker/tracker-process.c:80 src/tracker/tracker-process.c:206
+#: src/tracker/tracker-process.c:335 src/tracker/tracker-process.c:356
+#: src/tracker/tracker-search.c:1581 src/tracker/tracker-sparql.c:173
+#: src/tracker/tracker-sparql.c:1087 src/tracker/tracker-status.c:74
+#: src/tracker/tracker-status.c:303 src/tracker/tracker-status.c:313
+#: src/tracker/tracker-status.c:387 src/tracker/tracker-status.c:432
+#: src/tracker/tracker-status.c:461 src/tracker/tracker-tag.c:977
msgid "No error given"
msgstr "Chybová správa nebola poskytnutá"
-#: ../src/libtracker-data/tracker-data-backup.c:649
+#: src/libtracker-data/tracker-data-backup.c:648
#, c-format
msgid "Unknown error, “tar” exited with status %d"
msgstr "Neznáma chyba. Program „tar“ skončil so stavom %d"
-#: ../src/libtracker-miner/tracker-data-provider.c:108
-#: ../src/libtracker-miner/tracker-data-provider.c:168
+#: src/libtracker-miner/tracker-data-provider.c:108
+#: src/libtracker-miner/tracker-data-provider.c:168
msgid "Operation not supported"
msgstr "Operácia nie je podporovaná"
-#: ../src/libtracker-miner/tracker-miner-proxy.c:352
+#: src/libtracker-miner/tracker-miner-proxy.c:346
msgid "Cookie not recognized to resume paused miner"
msgstr "Cookie na obnovenie pozastaveného dolovača nebolo rozpoznané"
-#: ../src/libtracker-miner/tracker-miner-proxy.c:409
+#: src/libtracker-miner/tracker-miner-proxy.c:403
msgid "Pause application and reason match an already existing pause request"
msgstr ""
"Pozastavenie aplikácie a jeho dôvod už existuje v požiadavkách na "
"pozastavenie"
-#: ../src/libtracker-miner/tracker-miner-proxy.c:772
-msgid "Data store is not available"
-msgstr "Úložisko údajov nie je dostupné"
-
# cmd desc
#. Daemon options
-#: ../src/tracker-store/tracker-main.vala:57
+#: src/tracker-store/tracker-main.vala:60
msgid "Displays version information"
msgstr "Zobrazí informácie o verzii"
-#: ../src/tracker-store/tracker-main.vala:58
+#: src/tracker-store/tracker-main.vala:61
msgid ""
"Logging, 0 = errors only, 1 = minimal, 2 = detailed and 3 = debug (default = "
"0)"
@@ -254,57 +259,41 @@ msgstr ""
"Zaznamenávanie, 0 = len chyby, 1 = minimálne, 2 = podrobné a 3 = ladenie "
"(predvolené = 0)"
+#: src/tracker-store/tracker-main.vala:62
+msgid "Disable automatic shutdown"
+msgstr "Zakáže automatické vypnutie"
+
# cmd desc
#. Indexer options
-#: ../src/tracker-store/tracker-main.vala:61
+#: src/tracker-store/tracker-main.vala:65
msgid "Force a re-index of all content"
msgstr "Vynúti preindexovanie všetkého obsahu"
# cmd desc
-#: ../src/tracker-store/tracker-main.vala:62
+#: src/tracker-store/tracker-main.vala:66
msgid "Only allow read based actions on the database"
msgstr "Povolí nad databázou iba akcie súvisiace s čítaním"
-#: ../src/tracker-store/tracker-main.vala:63
+#: src/tracker-store/tracker-main.vala:67
msgid "Load a specified domain ontology"
msgstr "Načíta určenú ontológiu domény"
#. Translators: this messagge will apper immediately after the
#. * usage string - Usage: COMMAND <THIS_MESSAGE>
#.
-#: ../src/tracker-store/tracker-main.vala:194
+#: src/tracker-store/tracker-main.vala:229
msgid "— start the tracker daemon"
msgstr "— spustí démona sledovača"
-#: ../src/tracker-store/tracker-store.desktop.in.in.h:1
+#: src/tracker-store/tracker-store.desktop.in:3
msgid "Tracker Store"
msgstr "Úložisko sledovača"
-#: ../src/tracker-store/tracker-store.desktop.in.in.h:2
+#: src/tracker-store/tracker-store.desktop.in:4
msgid "Metadata database store and lookup manager"
msgstr "Správca úložiska databázy metaúdajov a vyhľadávania"
-#: ../src/tracker-store/org.freedesktop.Tracker.Store.gschema.xml.in.h:1
-msgid "Log verbosity"
-msgstr "Podrobnosť záznamu"
-
-#: ../src/tracker-store/org.freedesktop.Tracker.Store.gschema.xml.in.h:2
-msgid "Log verbosity."
-msgstr "Podrobnosť záznamu."
-
-#: ../src/tracker-store/org.freedesktop.Tracker.Store.gschema.xml.in.h:3
-msgid "GraphUpdated delay"
-msgstr "Oneskorenie signálu GraphUpdated"
-
-#: ../src/tracker-store/org.freedesktop.Tracker.Store.gschema.xml.in.h:4
-msgid ""
-"Period in milliseconds between GraphUpdated signals being emitted when "
-"indexed data has changed inside the database."
-msgstr ""
-"Doba v milisekundách medzi vyslaniami signálov GraphUpdated, keď sú "
-"indexované údaje zmenené v databáze."
-
-#: ../src/tracker/tracker-config.c:60
+#: src/tracker/tracker-config.c:60
#, c-format
msgid "Could not get GSettings for miners, manager could not be created, %s"
msgstr ""
@@ -312,55 +301,55 @@ msgstr ""
"vytvorený, %s"
# proces, dolovač
-#: ../src/tracker/tracker-daemon.c:121 ../src/tracker/tracker-daemon.c:429
+#: src/tracker/tracker-daemon.c:121 src/tracker/tracker-daemon.c:429
msgid "Unavailable"
msgstr "nedostupný"
#. generic
-#: ../src/tracker/tracker-daemon.c:122
+#: src/tracker/tracker-daemon.c:122
msgid "Initializing"
msgstr "Inicializuje sa"
-#: ../src/tracker/tracker-daemon.c:123
+#: src/tracker/tracker-daemon.c:123
msgid "Processing…"
msgstr "Spracováva sa…"
# RSS miner status
-#: ../src/tracker/tracker-daemon.c:124
+#: src/tracker/tracker-daemon.c:124
msgid "Fetching…"
msgstr "Získava údaje kanála…"
# miner status
#. miner/rss
-#: ../src/tracker/tracker-daemon.c:125
+#: src/tracker/tracker-daemon.c:125
#, c-format
msgid "Crawling single directory “%s”"
msgstr "Prechádza samostatný adresár „%s“"
# miner status
-#: ../src/tracker/tracker-daemon.c:126
+#: src/tracker/tracker-daemon.c:126
#, c-format
msgid "Crawling recursively directory “%s”"
msgstr "Prechádza rekurzívne adresár „%s“"
# miner status
-#: ../src/tracker/tracker-daemon.c:127
+#: src/tracker/tracker-daemon.c:127
msgid "Paused"
msgstr "Pozastavený"
# miner status
-#: ../src/tracker/tracker-daemon.c:128
+#: src/tracker/tracker-daemon.c:128
msgid "Idle"
msgstr "Nečinný"
# cmd desc
# PM: neviem či je preklad dobre
-#: ../src/tracker/tracker-daemon.c:134
+#: src/tracker/tracker-daemon.c:134
msgid "Follow status changes as they happen"
msgstr "Sleduje zmeny stavov keď nastanú"
# cmd desc
-#: ../src/tracker/tracker-daemon.c:138
+#: src/tracker/tracker-daemon.c:138
msgid ""
"Watch changes to the database in real time (e.g. resources or files being "
"added)"
@@ -368,26 +357,26 @@ msgstr ""
"Bude sledovať zmeny v databáze v reálnom čase (napr. pri pridaní zdrojov "
"alebo súborov)"
-#: ../src/tracker/tracker-daemon.c:139
+#: src/tracker/tracker-daemon.c:139
msgid "ONTOLOGY"
msgstr "ONTOLÓGIA"
# cmd desc
-#: ../src/tracker/tracker-daemon.c:142
+#: src/tracker/tracker-daemon.c:142
msgid "List common statuses for miners and the store"
msgstr "Zobrazí zoznam častých stavov dolovačov a úložiska"
# cmd desc
-#: ../src/tracker/tracker-daemon.c:147
+#: src/tracker/tracker-daemon.c:147
msgid "Pause a miner (you must use this with --miner)"
msgstr "Pozastaví dolovač (musíte použiť spolu s voľbou --miner)"
-#: ../src/tracker/tracker-daemon.c:148 ../src/tracker/tracker-daemon.c:152
+#: src/tracker/tracker-daemon.c:148 src/tracker/tracker-daemon.c:152
msgid "REASON"
msgstr "DÔVOD"
# cmd desc
-#: ../src/tracker/tracker-daemon.c:151
+#: src/tracker/tracker-daemon.c:151
msgid ""
"Pause a miner while the calling process is alive or until resumed (you must "
"use this with --miner)"
@@ -396,16 +385,16 @@ msgstr ""
"obnovený (musíte použiť spolu s voľbou --miner)"
# cmd desc
-#: ../src/tracker/tracker-daemon.c:155
+#: src/tracker/tracker-daemon.c:155
msgid "Resume a miner (you must use this with --miner)"
msgstr "Obnoví činnosť dolovača (musíte použiť spolu s voľbou --miner)"
-#: ../src/tracker/tracker-daemon.c:156
+#: src/tracker/tracker-daemon.c:156
msgid "COOKIE"
msgstr "COOKIE"
# cmd desc
-#: ../src/tracker/tracker-daemon.c:159
+#: src/tracker/tracker-daemon.c:159
msgid ""
"Miner to use with --resume or --pause (you can use suffixes, e.g. Files or "
"Applications)"
@@ -413,32 +402,32 @@ msgstr ""
"Dolovač, ktorý sa má použiť pre voľbu --resume alebo --pause (je možné "
"použiť prípony, t.j. Files (súbory) alebo Applications (aplikácie))"
-#: ../src/tracker/tracker-daemon.c:160
+#: src/tracker/tracker-daemon.c:160
msgid "MINER"
msgstr "DOLOVAČ"
# cmd desc
-#: ../src/tracker/tracker-daemon.c:163
+#: src/tracker/tracker-daemon.c:163
msgid "List all miners currently running"
msgstr "Zoznam všetkých aktuálne bežiacich dolovačov"
# cmd desc
-#: ../src/tracker/tracker-daemon.c:167
+#: src/tracker/tracker-daemon.c:167
msgid "List all miners installed"
msgstr "Zoznam všetkých nainštalovaných dolovačov"
# cmd desc
-#: ../src/tracker/tracker-daemon.c:171
+#: src/tracker/tracker-daemon.c:171
msgid "List pause reasons"
msgstr "Zobrazí zoznam dôvodov pozastavenia"
# cmd desc
-#: ../src/tracker/tracker-daemon.c:176
+#: src/tracker/tracker-daemon.c:176
msgid "List all Tracker processes"
msgstr "Vypíše všetky procesy sledovača"
# cmd desc
-#: ../src/tracker/tracker-daemon.c:178
+#: src/tracker/tracker-daemon.c:178
msgid ""
"Use SIGKILL to stop all matching processes, either “store”, “miners” or "
"“all” may be used, no parameter equals “all”"
@@ -447,12 +436,12 @@ msgstr ""
"môžete použiť buď „store“ (úložisko), „miners“ (dolovače) alebo "
"„all“ (všetko), bez parametra sa použije „all“"
-#: ../src/tracker/tracker-daemon.c:179 ../src/tracker/tracker-daemon.c:182
+#: src/tracker/tracker-daemon.c:179 src/tracker/tracker-daemon.c:182
msgid "APPS"
msgstr "APLIKÁCIE"
# cmd desc
-#: ../src/tracker/tracker-daemon.c:181
+#: src/tracker/tracker-daemon.c:181
msgid ""
"Use SIGTERM to stop all matching processes, either “store”, “miners” or "
"“all” may be used, no parameter equals “all”"
@@ -462,12 +451,12 @@ msgstr ""
"„all“ (všetko), bez parametra sa použije „all“"
# cmd desc
-#: ../src/tracker/tracker-daemon.c:184
+#: src/tracker/tracker-daemon.c:184
msgid "Starts miners (which indirectly starts tracker-store too)"
msgstr "Spustí dolovače (čím sa nepriamo spustí aj úložisko sledovača)"
# cmd desc
-#: ../src/tracker/tracker-daemon.c:187 ../src/tracker/tracker-extract.c:42
+#: src/tracker/tracker-daemon.c:187 src/tracker/tracker-extract.c:42
msgid ""
"Sets the logging verbosity to LEVEL (“debug”, “detailed”, “minimal”, "
"“errors”) for all processes"
@@ -475,119 +464,119 @@ msgstr ""
"Pre všetky procesy nastaví podrobnosť výpisov na ÚROVEŇ („debug“ (ladenie), "
"„detailed“ (podrobný), „minimal“ (minimálny), „errors“ (iba chyby))"
-#: ../src/tracker/tracker-daemon.c:188 ../src/tracker/tracker-extract.c:43
+#: src/tracker/tracker-daemon.c:188 src/tracker/tracker-extract.c:43
msgid "LEVEL"
msgstr "ÚROVEŇ"
# cmd desc
-#: ../src/tracker/tracker-daemon.c:190
+#: src/tracker/tracker-daemon.c:190
msgid "Show logging values in terms of log verbosity for each process"
msgstr "Pre každý proces zobrazí hodnotu určujúcu podrobnosť záznamov"
-#: ../src/tracker/tracker-daemon.c:264
+#: src/tracker/tracker-daemon.c:264
#, c-format
msgid "Could not get status from miner: %s"
msgstr "Nepodarilo sa získať stav od dolovača: %s"
#. Translators: %s is a time string
-#: ../src/tracker/tracker-daemon.c:326
+#: src/tracker/tracker-daemon.c:326
#, c-format
msgid "%s remaining"
msgstr "Zostáva: %s"
-#: ../src/tracker/tracker-daemon.c:329
+#: src/tracker/tracker-daemon.c:329
msgid "unknown time left"
msgstr "neznámy zostávajúci čas"
#. Work out lengths for output spacing
-#: ../src/tracker/tracker-daemon.c:342 ../src/tracker/tracker-daemon.c:1319
+#: src/tracker/tracker-daemon.c:342 src/tracker/tracker-daemon.c:1319
msgid "PAUSED"
msgstr "POZASTAVENÝ"
-#: ../src/tracker/tracker-daemon.c:359
+#: src/tracker/tracker-daemon.c:359
msgid "Not running or is a disabled plugin"
msgstr "Nie je spustený alebo je zakázaný zásuvný modul"
-#: ../src/tracker/tracker-daemon.c:461
+#: src/tracker/tracker-daemon.c:461
msgid "Could not retrieve tracker-store status"
msgstr "Nepodarilo sa získať stav úložiska sledovača"
-#: ../src/tracker/tracker-daemon.c:482
+#: src/tracker/tracker-daemon.c:482
msgid "Could not retrieve tracker-store progress"
msgstr "Nepodarilo sa získať priebeh úložiska sledovača"
-#: ../src/tracker/tracker-daemon.c:612 ../src/tracker/tracker-sparql.c:200
+#: src/tracker/tracker-daemon.c:612 src/tracker/tracker-sparql.c:200
msgid "Unable to retrieve namespace prefixes"
msgstr "Nepodarilo sa získať predpony menných priestorov"
-#: ../src/tracker/tracker-daemon.c:620 ../src/tracker/tracker-sparql.c:208
+#: src/tracker/tracker-daemon.c:620 src/tracker/tracker-sparql.c:208
msgid "No namespace prefixes were returned"
msgstr "Neboli vrátené žiadne predpony menných priestorov"
-#: ../src/tracker/tracker-daemon.c:699 ../src/tracker/tracker-daemon.c:726
+#: src/tracker/tracker-daemon.c:699 src/tracker/tracker-daemon.c:726
msgid "Could not run SPARQL query"
msgstr "Nepodarilo sa spustiť požiadavku SPARQL"
-#: ../src/tracker/tracker-daemon.c:707 ../src/tracker/tracker-daemon.c:739
+#: src/tracker/tracker-daemon.c:707 src/tracker/tracker-daemon.c:739
msgid "Could not call tracker_sparql_cursor_next() on SPARQL query"
msgstr ""
"V požiadavke SPARQL sa nepodarilo zavolať funkciu "
"tracker_sparql_cursor_next()"
-#: ../src/tracker/tracker-daemon.c:889
+#: src/tracker/tracker-daemon.c:889
#, c-format
msgid "Could not pause miner, manager could not be created, %s"
msgstr "Nepodarilo sa pozastaviť dolovač, správca nemohol byť vytvorený, %s"
-#: ../src/tracker/tracker-daemon.c:896
+#: src/tracker/tracker-daemon.c:896
#, c-format
msgid "Attempting to pause miner “%s” with reason “%s”"
msgstr "Pokus o pozastavenie dolovača „%s“ z dôvodu „%s“"
-#: ../src/tracker/tracker-daemon.c:904 ../src/tracker/tracker-daemon.c:910
+#: src/tracker/tracker-daemon.c:904 src/tracker/tracker-daemon.c:910
#, c-format
msgid "Could not pause miner: %s"
msgstr "Nepodarilo sa pozastaviť dolovač: %s"
-#: ../src/tracker/tracker-daemon.c:916
+#: src/tracker/tracker-daemon.c:916
#, c-format
msgid "Cookie is %d"
msgstr "Cookie je č. %d"
-#: ../src/tracker/tracker-daemon.c:923 ../src/tracker/tracker-daemon.c:1276
-#: ../src/tracker/tracker-daemon.c:1420
+#: src/tracker/tracker-daemon.c:923 src/tracker/tracker-daemon.c:1276
+#: src/tracker/tracker-daemon.c:1420
msgid "Press Ctrl+C to stop"
msgstr "Stlačením Ctrl+C zastavíte činnosť"
-#: ../src/tracker/tracker-daemon.c:947
+#: src/tracker/tracker-daemon.c:947
#, c-format
msgid "Could not resume miner, manager could not be created, %s"
msgstr ""
"Nepodarilo sa obnoviť činnosť dolovača, správca nemohol byť vytvorený, %s"
-#: ../src/tracker/tracker-daemon.c:954
+#: src/tracker/tracker-daemon.c:954
#, c-format
msgid "Attempting to resume miner %s with cookie %d"
msgstr "Pokus o obnovenie činnosti dolovača %s pomocou cookie č. %d"
-#: ../src/tracker/tracker-daemon.c:961
+#: src/tracker/tracker-daemon.c:961
#, c-format
msgid "Could not resume miner: %s"
msgstr "Nepodarilo sa obnoviť činnosť dolovača: %s"
# PM: nasleduje ako výpis po chybe
-#: ../src/tracker/tracker-daemon.c:965 ../src/tracker/tracker-index.c:222
-#: ../src/tracker/tracker-sparql.c:1396
+#: src/tracker/tracker-daemon.c:965 src/tracker/tracker-index.c:222
+#: src/tracker/tracker-sparql.c:1396
msgid "Done"
msgstr "Dokončené"
-#: ../src/tracker/tracker-daemon.c:982
+#: src/tracker/tracker-daemon.c:982
#, c-format
msgid "Could not list miners, manager could not be created, %s"
msgstr ""
"Nepodarilo sa vypísať zoznam dolovačov, správca nemohol byť vytvorený, %s"
-#: ../src/tracker/tracker-daemon.c:996
+#: src/tracker/tracker-daemon.c:996
#, c-format
msgid "Found %d miner installed"
msgid_plural "Found %d miners installed"
@@ -595,7 +584,7 @@ msgstr[0] "Nájdených %d nainštalovaných dolovačov"
msgstr[1] "Nájdený %d nainštalovaný dolovač"
msgstr[2] "Nájdené %d nainštalované dolovače"
-#: ../src/tracker/tracker-daemon.c:1019
+#: src/tracker/tracker-daemon.c:1019
#, c-format
msgid "Found %d miner running"
msgid_plural "Found %d miners running"
@@ -603,184 +592,192 @@ msgstr[0] "Nájdených %d bežiacich dolovačov"
msgstr[1] "Nájdený %d bežiaci dolovač"
msgstr[2] "Nájdené %d bežiace dolovače"
-#: ../src/tracker/tracker-daemon.c:1051
+#: src/tracker/tracker-daemon.c:1051
#, c-format
msgid "Could not get pause details, manager could not be created, %s"
msgstr ""
"Nepodarilo sa získať podrobnosti o pozastavení, správca nemohol byť "
"vytvorený, %s"
-#: ../src/tracker/tracker-daemon.c:1061
+#: src/tracker/tracker-daemon.c:1061
msgid "No miners are running"
msgstr "Žiadne dolovače nebežia"
-#: ../src/tracker/tracker-daemon.c:1101 ../src/tracker/tracker-daemon.c:1355
-#: ../src/tracker/tracker-daemon.c:1604 ../src/tracker/tracker-daemon.c:1646
+#: src/tracker/tracker-daemon.c:1101 src/tracker/tracker-daemon.c:1355
+#: src/tracker/tracker-daemon.c:1604 src/tracker/tracker-daemon.c:1647
msgid "Miners"
msgstr "Dolovače"
# PM: Nie som si istý ktorý tvar sa použije v prvom prípade
-#: ../src/tracker/tracker-daemon.c:1108
+#: src/tracker/tracker-daemon.c:1108
msgid "Application"
msgstr "Aplikácia"
-#: ../src/tracker/tracker-daemon.c:1110
+#: src/tracker/tracker-daemon.c:1110
msgid "Reason"
msgstr "Dôvod"
-#: ../src/tracker/tracker-daemon.c:1119
+#: src/tracker/tracker-daemon.c:1119
msgid "No miners are paused"
msgstr "Žiadne dolovače nie sú pozastavené"
-#: ../src/tracker/tracker-daemon.c:1207
+#: src/tracker/tracker-daemon.c:1207
msgid "Only one of “all”, “store” and “miners” options are allowed"
msgstr "Iba jedna z volieb „all“, „store“ a „miners“ je povolená"
-#: ../src/tracker/tracker-daemon.c:1242
+#: src/tracker/tracker-daemon.c:1242
msgid "Could not get SPARQL connection"
msgstr "Nepodarilo sa nadviazať pripojenie k SPARQL"
-#: ../src/tracker/tracker-daemon.c:1274
+#: src/tracker/tracker-daemon.c:1274
msgid "Now listening for resource updates to the database"
msgstr "Teraz sa sledujú aktualizácie zdrojov v databáze"
-#: ../src/tracker/tracker-daemon.c:1275
+#: src/tracker/tracker-daemon.c:1275
msgid "All nie:plainTextContent properties are omitted"
msgstr "Všetky vlastnosti nie:plainTextContent sú vynechané"
-#: ../src/tracker/tracker-daemon.c:1290
+#: src/tracker/tracker-daemon.c:1290
msgid "Common statuses include"
msgstr "Medzi bežné patria stavy"
-#: ../src/tracker/tracker-daemon.c:1308 ../src/tracker/tracker-status.c:460
+#: src/tracker/tracker-daemon.c:1308 src/tracker/tracker-status.c:460
#, c-format
msgid "Could not get status, manager could not be created, %s"
msgstr "Nepodarilo sa získať stav, správca nemohol byť vytvorený, %s"
#. Display states
-#: ../src/tracker/tracker-daemon.c:1329
+#: src/tracker/tracker-daemon.c:1329
msgid "Store"
msgstr "Úložisko"
-#: ../src/tracker/tracker-daemon.c:1363
+#: src/tracker/tracker-daemon.c:1363
#, c-format
msgid "Could not get display name for miner “%s”"
msgstr "Nepodarilo sa získať názov zobrazenia pre dolovač „%s“"
-#: ../src/tracker/tracker-daemon.c:1461
+#: src/tracker/tracker-daemon.c:1461
msgid "You can not use miner pause and resume switches together"
msgstr "Prepínače pause (pozastaviť) a resume (obnoviť) nemôžete použiť spolu"
-#: ../src/tracker/tracker-daemon.c:1467
+#: src/tracker/tracker-daemon.c:1467
msgid "You must provide the miner for pause or resume commands"
msgstr ""
"Musíte poskytnúť miner (dolovač), pre príkazy pause (pozastaviť) alebo "
"resume (obnoviť)"
-#: ../src/tracker/tracker-daemon.c:1473
+#: src/tracker/tracker-daemon.c:1473
msgid "You must provide a pause or resume command for the miner"
msgstr ""
"Pre zadaný miner (dolovač) musíte použiť jeden z príkazov pause (pozastaviť) "
"alebo resume (obnoviť)"
-#: ../src/tracker/tracker-daemon.c:1509
+#: src/tracker/tracker-daemon.c:1509
msgid "You can not use the --kill and --terminate arguments together"
msgstr "Parametre --kill a --terminate nemôžete použiť spolu"
-#: ../src/tracker/tracker-daemon.c:1515
+#: src/tracker/tracker-daemon.c:1515
msgid "You can not use the --get-logging and --set-logging arguments together"
msgstr "Parametre --get-logging a --set-logging nemôžete použiť spolu"
-#: ../src/tracker/tracker-daemon.c:1530 ../src/tracker/tracker-extract.c:105
+#: src/tracker/tracker-daemon.c:1530 src/tracker/tracker-extract.c:105
msgid "Invalid log verbosity, try “debug”, “detailed”, “minimal” or “errors”"
msgstr ""
"Neplatná hodnota pre podrobnosť záznamu, skúste použiť „debug“ (ladenie), "
"„detailed“ (podrobný), „minimal“ (minimálny), „errors“ (iba chyby)"
-#: ../src/tracker/tracker-daemon.c:1568
+#: src/tracker/tracker-daemon.c:1558 src/tracker/tracker-process.c:305
+#, c-format
+msgid "Found %d PID…"
+msgid_plural "Found %d PIDs…"
+msgstr[0] "Nájdených %d identifikátorov PID…"
+msgstr[1] "Nájdený %d identifikátor PID…"
+msgstr[2] "Nájdené %d identifikátory PID…"
+
+#: src/tracker/tracker-daemon.c:1568
#, c-format
msgid "Found process ID %d for “%s”"
msgstr "Nájdený identifikátor procesu %d pre „%s“"
-#: ../src/tracker/tracker-daemon.c:1598 ../src/tracker/tracker-daemon.c:1640
+#: src/tracker/tracker-daemon.c:1598 src/tracker/tracker-daemon.c:1641
msgid "Components"
msgstr "Súčasti"
-#: ../src/tracker/tracker-daemon.c:1605 ../src/tracker/tracker-daemon.c:1647
+#: src/tracker/tracker-daemon.c:1605 src/tracker/tracker-daemon.c:1648
msgid "Only those with config listed"
msgstr "Iba tie, ktoré sú uvedené v konfigurácii"
-#: ../src/tracker/tracker-daemon.c:1623
+#: src/tracker/tracker-daemon.c:1624
#, c-format
msgid "Setting log verbosity for all components to “%s”…"
msgstr "Nastavuje sa podrobnosť záznamov pre všetky súčasti na hodnotu „%s“…"
-#: ../src/tracker/tracker-daemon.c:1662
+#: src/tracker/tracker-daemon.c:1664
msgid "Starting miners…"
msgstr "Spúšťajú sa dolovače…"
-#: ../src/tracker/tracker-daemon.c:1667
+#: src/tracker/tracker-daemon.c:1669
#, c-format
msgid "Could not start miners, manager could not be created, %s"
msgstr "Nepodarilo sa spustiť dolovače, správca nemohol byť vytvorený, %s"
-#: ../src/tracker/tracker-daemon.c:1692
+#: src/tracker/tracker-daemon.c:1694
msgid "perhaps a disabled plugin?"
msgstr "že by zakázaný zásuvný modul?"
-#: ../src/tracker/tracker-daemon.c:1736
+#: src/tracker/tracker-daemon.c:1738
msgid ""
"If no arguments are given, the status of the store and data miners is shown"
msgstr ""
"Ak nie sú určené žiadne parametre, bude zobrazený stav úložiska a dolovačov "
"údajov"
-#: ../src/tracker/tracker-daemon.c:1741 ../src/tracker/tracker-extract.c:147
-#: ../src/tracker/tracker-index.c:412 ../src/tracker/tracker-info.c:422
-#: ../src/tracker/tracker-reset.c:449 ../src/tracker/tracker-search.c:1778
-#: ../src/tracker/tracker-sparql.c:1497 ../src/tracker/tracker-sql.c:238
-#: ../src/tracker/tracker-status.c:587 ../src/tracker/tracker-tag.c:1079
+#: src/tracker/tracker-daemon.c:1743 src/tracker/tracker-extract.c:147
+#: src/tracker/tracker-index.c:412 src/tracker/tracker-info.c:422
+#: src/tracker/tracker-reset.c:385 src/tracker/tracker-search.c:1778
+#: src/tracker/tracker-sparql.c:1497 src/tracker/tracker-sql.c:238
+#: src/tracker/tracker-status.c:587 src/tracker/tracker-tag.c:1079
msgid "Unrecognized options"
msgstr "Nerozpoznané voľby"
-#: ../src/tracker/tracker-dbus.c:44
+#: src/tracker/tracker-dbus.c:44
msgid "Could not get D-Bus connection"
msgstr "Nepodarilo sa nadviazať pripojenie k zbernici D-Bus"
-#: ../src/tracker/tracker-dbus.c:62
+#: src/tracker/tracker-dbus.c:62
msgid "Could not create D-Bus proxy to tracker-store"
msgstr ""
"Nepodarilo sa vytvoriť sprostredkovateľa proxy zbernice D-Bus k úložisku "
"sledovača"
-#: ../src/tracker/tracker-extract.c:45
-msgid "Output results format: “sparql”, or “turtle”"
-msgstr "Formát výstupných výsledkov: „sparql“ alebo „turtle“"
+#: src/tracker/tracker-extract.c:45
+msgid "Output results format: “sparql”, “turtle” or “json-ld”"
+msgstr "Formát výstupných výsledkov: „sparql“, „turtle“ alebo „json-ld“"
-#: ../src/tracker/tracker-extract.c:46
+#: src/tracker/tracker-extract.c:46
msgid "FORMAT"
msgstr "FORMÁT"
# cmd desc
-#: ../src/tracker/tracker-extract.c:48 ../src/tracker/tracker-extract.c:49
-#: ../src/tracker/tracker-index.c:60 ../src/tracker/tracker-index.c:71
-#: ../src/tracker/tracker-index.c:72 ../src/tracker/tracker-info.c:71
-#: ../src/tracker/tracker-info.c:72 ../src/tracker/tracker-reset.c:63
-#: ../src/tracker/tracker-sparql.c:106 ../src/tracker/tracker-sql.c:44
+#: src/tracker/tracker-extract.c:48 src/tracker/tracker-extract.c:49
+#: src/tracker/tracker-index.c:60 src/tracker/tracker-index.c:71
+#: src/tracker/tracker-index.c:72 src/tracker/tracker-info.c:71
+#: src/tracker/tracker-info.c:72 src/tracker/tracker-reset.c:63
+#: src/tracker/tracker-sparql.c:106 src/tracker/tracker-sql.c:44
msgid "FILE"
msgstr "SÚBOR"
-#: ../src/tracker/tracker-extract.c:77
+#: src/tracker/tracker-extract.c:77
msgid "Could not run tracker-extract: "
msgstr "Nepodarilo sa spustiť funkciu tracker-extract: "
-#: ../src/tracker/tracker-help.c:59 ../src/tracker/tracker-help.c:71
+#: src/tracker/tracker-help.c:59 src/tracker/tracker-help.c:71
#, c-format
msgid "failed to exec “%s”: %s"
msgstr "zlyhalo spustenie „%s“: %s"
# cmd desc
-#: ../src/tracker/tracker-index.c:56
+#: src/tracker/tracker-index.c:56
msgid ""
"Tell miners to reindex files which match the mime type supplied (for new "
"extractors), use -m MIME1 -m MIME2"
@@ -788,83 +785,83 @@ msgstr ""
"Povie dolovačom aby preindexovali súbory, ktoré zodpovedajú zadanému typu "
"MIME (pre nové extratory), použitie -m MIME1 -m MIME2"
-#: ../src/tracker/tracker-index.c:57
+#: src/tracker/tracker-index.c:57
msgid "MIME"
msgstr "MIME"
# cmd desc
-#: ../src/tracker/tracker-index.c:59
+#: src/tracker/tracker-index.c:59
msgid "Tell miners to (re)index a given file"
msgstr "Povie dolovačom aby (pre)indexovali zadaný súbor"
# cmd desc
-#: ../src/tracker/tracker-index.c:62
+#: src/tracker/tracker-index.c:62
msgid "Backup current index / database to the file provided"
msgstr "Zálohuje aktuálny index / databázu do zadaného súboru"
-#: ../src/tracker/tracker-index.c:65
+#: src/tracker/tracker-index.c:65
msgid "Restore a database from a previous backup (see --backup)"
msgstr "Obnoví databázu z predošlej zálohy (viď --backup)"
-#: ../src/tracker/tracker-index.c:68
+#: src/tracker/tracker-index.c:68
msgid "Import a dataset from the provided file (in Turtle format)"
msgstr "Importuje sadu údajov z poskytnutého súboru (vo formáte Turtle)"
-#: ../src/tracker/tracker-index.c:122
+#: src/tracker/tracker-index.c:122
#, c-format
msgid "Could not reindex mimetypes, manager could not be created, %s"
msgstr ""
"Nepodarilo sa preindexovať typy MIME, správca nemohol byť vytvorený, %s"
-#: ../src/tracker/tracker-index.c:132
+#: src/tracker/tracker-index.c:132
msgid "Could not reindex mimetypes"
msgstr "Nepodarilo sa preindexovať typy MIME"
-#: ../src/tracker/tracker-index.c:138
+#: src/tracker/tracker-index.c:138
msgid "Reindexing mime types was successful"
msgstr "Preindexovanie typov MIME bolo úspešné"
-#: ../src/tracker/tracker-index.c:154
+#: src/tracker/tracker-index.c:154
#, c-format
msgid "Could not (re)index file, manager could not be created, %s"
msgstr "Nepodarilo sa (pre)indexovať súbor, správca nemohol byť vytvorený, %s"
-#: ../src/tracker/tracker-index.c:169
+#: src/tracker/tracker-index.c:169
msgid "Could not (re)index file"
msgstr "Nepodarilo sa (pre)indexovať súbor"
-#: ../src/tracker/tracker-index.c:175
+#: src/tracker/tracker-index.c:175
msgid "(Re)indexing file was successful"
msgstr "(Pre)indexovanie súboru bolo úspešné"
-#: ../src/tracker/tracker-index.c:195 ../src/tracker/tracker-info.c:263
-#: ../src/tracker/tracker-search.c:1580 ../src/tracker/tracker-sparql.c:172
-#: ../src/tracker/tracker-sparql.c:1086 ../src/tracker/tracker-status.c:73
-#: ../src/tracker/tracker-status.c:386 ../src/tracker/tracker-tag.c:976
+#: src/tracker/tracker-index.c:195 src/tracker/tracker-info.c:263
+#: src/tracker/tracker-search.c:1580 src/tracker/tracker-sparql.c:172
+#: src/tracker/tracker-sparql.c:1086 src/tracker/tracker-status.c:73
+#: src/tracker/tracker-status.c:386 src/tracker/tracker-tag.c:976
msgid "Could not establish a connection to Tracker"
msgstr "Nepodarilo sa nadviazať pripojenie k sledovaču"
-#: ../src/tracker/tracker-index.c:206
+#: src/tracker/tracker-index.c:206
msgid "Importing Turtle file"
msgstr "Importuje sa súbor Turtle"
-#: ../src/tracker/tracker-index.c:215
+#: src/tracker/tracker-index.c:215
msgid "Unable to import Turtle file"
msgstr "Nepodarilo sa importovať súbor Turtle"
-#: ../src/tracker/tracker-index.c:251
+#: src/tracker/tracker-index.c:251
msgid "Backing up database"
msgstr "Zálohuje sa databáza"
-#: ../src/tracker/tracker-index.c:271 ../src/tracker/tracker-index.c:328
+#: src/tracker/tracker-index.c:271 src/tracker/tracker-index.c:328
msgid "Could not backup database"
msgstr "Nepodarilo sa zálohovať databázu"
-#: ../src/tracker/tracker-index.c:308
+#: src/tracker/tracker-index.c:308
msgid "Restoring database from backup"
msgstr "Obnovuje sa databáza zo zálohy"
-#: ../src/tracker/tracker-index.c:437
+#: src/tracker/tracker-index.c:437
msgid ""
"Only one action (--backup, --restore, --index-file or --import) can be used "
"at a time"
@@ -872,15 +869,15 @@ msgstr ""
"Súčasne môže byť použitá iba jedna akcia (--backup, --restore, --index-file "
"alebo --import)"
-#: ../src/tracker/tracker-index.c:439
+#: src/tracker/tracker-index.c:439
msgid "Missing one or more files which are required"
msgstr "Chýba jeden alebo viacero potrebných súborov"
-#: ../src/tracker/tracker-index.c:441
+#: src/tracker/tracker-index.c:441
msgid "Only one file can be used with --backup and --restore"
msgstr "S parametrami --backup a --restore môže byť použitý iba jeden súbor"
-#: ../src/tracker/tracker-index.c:443
+#: src/tracker/tracker-index.c:443
msgid ""
"Actions (--backup, --restore, --index-file and --import) can not be used "
"with --reindex-mime-type"
@@ -889,14 +886,14 @@ msgstr ""
"akciou --reindex-mime-type"
# cmd desc
-#: ../src/tracker/tracker-info.c:49
+#: src/tracker/tracker-info.c:49
msgid "Show full namespaces (i.e. don’t use nie:title, use full URLs)"
msgstr ""
"Zobrazí úplné menné priestory (t.j nepoužije nie:nazov, použije úplné adresy "
"URL)"
# cmd desc
-#: ../src/tracker/tracker-info.c:53
+#: src/tracker/tracker-info.c:53
msgid "Show plain text content if available for resources"
msgstr "Zobrazí obsah v podobe bežného textu ak je pre zdroje dostupný"
@@ -907,7 +904,7 @@ msgstr "Zobrazí obsah v podobe bežného textu ak je pre zdroje dostupný"
#. * fully supports international characters. In practice, UTF-8
#. * is the most popular encoding used for IRI.
#.
-#: ../src/tracker/tracker-info.c:63
+#: src/tracker/tracker-info.c:63
msgid ""
"Instead of looking up a file name, treat the FILE arguments as actual IRIs "
"(e.g. <file:///path/to/some/file.txt>)"
@@ -916,72 +913,72 @@ msgstr ""
"adries IRI (napr. <file:///path/to/some/file.txt>)"
# cmd desc
-#: ../src/tracker/tracker-info.c:67
+#: src/tracker/tracker-info.c:67
msgid "Output results as RDF in Turtle format"
msgstr "Vypíše výsledky ako RDF vo formáte Turtle"
-#: ../src/tracker/tracker-info.c:285
+#: src/tracker/tracker-info.c:285
msgid "Querying information for entity"
msgstr "Dopytujú sa informácie pre entitu"
-#: ../src/tracker/tracker-info.c:309
+#: src/tracker/tracker-info.c:309
msgid "Unable to retrieve URN for URI"
msgstr "Nepodarilo sa získať URN pre URI"
-#: ../src/tracker/tracker-info.c:319 ../src/tracker/tracker-info.c:353
+#: src/tracker/tracker-info.c:319 src/tracker/tracker-info.c:353
msgid "Unable to retrieve data for URI"
msgstr "Nepodarilo sa získať údaje pre URI"
-#: ../src/tracker/tracker-info.c:362
+#: src/tracker/tracker-info.c:362
msgid "No metadata available for that URI"
msgstr "Nie sú dostupné žiadne metaúdaje pre taký URI"
-#: ../src/tracker/tracker-info.c:367 ../src/tracker/tracker-search.c:1455
-#: ../src/tracker/tracker-sparql.c:1449 ../src/tracker/tracker-sparql.c:1452
-#: ../src/tracker/tracker-sql.c:146
+#: src/tracker/tracker-info.c:367 src/tracker/tracker-search.c:1455
+#: src/tracker/tracker-sparql.c:1449 src/tracker/tracker-sparql.c:1452
+#: src/tracker/tracker-sql.c:146
msgid "Results"
msgstr "Výsledky"
-#: ../src/tracker/tracker-main.c:48
+#: src/tracker/tracker-main.c:48
msgid "See “tracker help <command>” to read about a specific subcommand."
msgstr ""
"Informácie k danému príkazu môžete získať pomocou „tracker help <príkaz>“"
-#: ../src/tracker/tracker-main.c:97
+#: src/tracker/tracker-main.c:97
msgid "Start, stop, pause and list processes responsible for indexing content"
msgstr ""
"Spustí, zastaví, pozastaví a vypíše procesy zodpovedné za indexovaný obsah"
-#: ../src/tracker/tracker-main.c:98
+#: src/tracker/tracker-main.c:98
msgid "Extract information from a file"
msgstr "Extrahuje informácie zo súboru"
-#: ../src/tracker/tracker-main.c:99
+#: src/tracker/tracker-main.c:99
msgid "Get help on how to use Tracker and any of these commands"
msgstr "Získa pomocníka ako používať sledovač a všetky ostatné príkazy"
-#: ../src/tracker/tracker-main.c:100
+#: src/tracker/tracker-main.c:100
msgid "Show information known about local files or items indexed"
msgstr ""
"Zobrazí známe informácie o miestnych súboroch alebo indexovaných položkách"
-#: ../src/tracker/tracker-main.c:101
+#: src/tracker/tracker-main.c:101
msgid "Backup, restore, import and (re)index by MIME type or file name"
msgstr ""
"Spustí zálohu, obnovenie, importovanie a (znovu)indexovanie podľa typu MIME "
"alebo názvu súboru"
-#: ../src/tracker/tracker-main.c:102
+#: src/tracker/tracker-main.c:102
msgid "Reset or remove index and revert configurations to defaults"
msgstr ""
"Obnoví alebo odstráni index a navráti konfigurácie do predvoleného stavu"
-#: ../src/tracker/tracker-main.c:103
+#: src/tracker/tracker-main.c:103
msgid "Search for content indexed or show content by type"
msgstr "Vyhľadá v indexovanom obsahu alebo zobrazí obsah podľa typu"
# cmd desc
-#: ../src/tracker/tracker-main.c:104
+#: src/tracker/tracker-main.c:104
msgid ""
"Query and update the index using SPARQL or search, list and tree the ontology"
msgstr ""
@@ -989,108 +986,108 @@ msgstr ""
"zoznam a strom ontológie"
# cmd desc
-#: ../src/tracker/tracker-main.c:105
+#: src/tracker/tracker-main.c:105
msgid "Query the database at the lowest level using SQL"
msgstr "Uskutoční dopyt databázy na najnižšej úrovni pomocou SQL"
-#: ../src/tracker/tracker-main.c:106
+#: src/tracker/tracker-main.c:106
msgid "Show the indexing progress, content statistics and index state"
msgstr "Zobrazí priebeh indexovania, štatistiky obsahu a stav indexu"
-#: ../src/tracker/tracker-main.c:107
+#: src/tracker/tracker-main.c:107
msgid "Create, list or delete tags for indexed content"
msgstr "Vytvorí, vypíše alebo odstráni značky indexovaného obsahu"
-#: ../src/tracker/tracker-main.c:108
+#: src/tracker/tracker-main.c:108
msgid "Show the license and version in use"
msgstr "Vypíše používanú licenciu a verziu"
-#: ../src/tracker/tracker-main.c:153
+#: src/tracker/tracker-main.c:153
#, c-format
msgid "“%s” is not a tracker command. See “tracker --help”"
msgstr "„%s“ nie je príkaz pre sledovač. Viď „tracker --help“."
-#: ../src/tracker/tracker-main.c:176
+#: src/tracker/tracker-main.c:176
msgid "Available tracker commands are:"
msgstr "Dostupné príkazy pre sledovač:"
-#: ../src/tracker/tracker-process.c:79
+#: src/tracker/tracker-process.c:79
msgid "Could not open /proc"
msgstr "Nepodarilo sa otvoriť /proc"
-#: ../src/tracker/tracker-process.c:132
+#: src/tracker/tracker-process.c:132
msgid "Could not stat() file"
msgstr "Nepodarilo sa zistiť stav súboru pomocou funkcie stat()"
-#: ../src/tracker/tracker-process.c:203
+#: src/tracker/tracker-process.c:203
#, c-format
msgid "Could not open “%s”"
msgstr "Nepodarilo sa otvoriť „%s“"
-#: ../src/tracker/tracker-process.c:332
+#: src/tracker/tracker-process.c:332
#, c-format
msgid "Could not terminate process %d — “%s”"
msgstr "Nepodarilo sa ukončiť proces č. %d — „%s“"
-#: ../src/tracker/tracker-process.c:338
+#: src/tracker/tracker-process.c:338
#, c-format
msgid "Terminated process %d — “%s”"
msgstr "Ukončený proces č. %d — „%s“"
-#: ../src/tracker/tracker-process.c:353
+#: src/tracker/tracker-process.c:353
#, c-format
msgid "Could not kill process %d — “%s”"
msgstr "Nepodarilo sa zabiť proces č. %d — „%s“"
-#: ../src/tracker/tracker-process.c:359
+#: src/tracker/tracker-process.c:359
#, c-format
msgid "Killed process %d — “%s”"
msgstr "Zabitý proces č. %d — „%s“"
# cmd desc
-#: ../src/tracker/tracker-reset.c:53
+#: src/tracker/tracker-reset.c:53
msgid "Kill all Tracker processes and remove all databases"
msgstr "Zabije všetky procesy sledovača a odstráni všetky databázy"
# cmd desc
-#: ../src/tracker/tracker-reset.c:56
+#: src/tracker/tracker-reset.c:56
msgid "Same as --hard but the backup & journal are restored after restart"
msgstr "Podobné ako --hard, ale po reštarte bude obnovená záloha a žurnál"
# cmd desc
-#: ../src/tracker/tracker-reset.c:59
+#: src/tracker/tracker-reset.c:59
msgid "Remove all configuration files so they are re-generated on next start"
msgstr ""
"Odstráni všetky konfiguračné súbory, ktoré sa nanovo vygenerujú pri "
"nasledujúcom spustení"
-#: ../src/tracker/tracker-reset.c:62
+#: src/tracker/tracker-reset.c:62
msgid ""
"Erase indexed information about a file, works recursively for directories"
msgstr "Vymaže indexované informácie o súbore, rekurzívne pre adresáre"
# RSS miner status
#. Now, delete the element recursively
-#: ../src/tracker/tracker-reset.c:167
+#: src/tracker/tracker-reset.c:127
msgid "Deleting…"
msgstr "Odstraňuje sa…"
-#: ../src/tracker/tracker-reset.c:188
+#: src/tracker/tracker-reset.c:148
msgid ""
"The indexed data for this file has been deleted and will be reindexed again."
msgstr ""
"Indexované údaje pre tento súbor boli odstránené a budú znovu indexované."
#. TRANSLATORS: --hard and --soft are commandline arguments
-#: ../src/tracker/tracker-reset.c:215
+#: src/tracker/tracker-reset.c:175
msgid "You can not use the --hard and --soft arguments together"
msgstr "Parametre --hard a --soft nemôžete použiť spolu"
-#: ../src/tracker/tracker-reset.c:223
+#: src/tracker/tracker-reset.c:183
msgid "CAUTION: This process may irreversibly delete data."
msgstr "VÝSTRAHA: Tento proces môže nenávratne odstrániť údaje."
-#: ../src/tracker/tracker-reset.c:224
+#: src/tracker/tracker-reset.c:184
msgid ""
"Although most content indexed by Tracker can be safely reindexed, it can’t "
"be assured that this is the case for all data. Be aware that you may be "
@@ -1100,12 +1097,12 @@ msgstr ""
"indexovaná, nemôže to byť zaručené pre všetky údaje. Berte na vedomie, že "
"môžete stratiť údaje. Pokračujte na vlastné riziko."
-#: ../src/tracker/tracker-reset.c:229
+#: src/tracker/tracker-reset.c:189
msgid "Are you sure you want to proceed?"
msgstr "Skutočne chcete pokračovať?"
#. TRANSLATORS: This is to be displayed on command line output
-#: ../src/tracker/tracker-reset.c:231
+#: src/tracker/tracker-reset.c:191
msgid "[y|N]"
msgstr "[a|n]"
@@ -1113,107 +1110,103 @@ msgstr "[a|n]"
#. * A partial or full match will be considered an affirmative answer,
#. * it is intentionally lowercase, so please keep it like this.
#.
-#: ../src/tracker/tracker-reset.c:240
+#: src/tracker/tracker-reset.c:200
msgid "yes"
msgstr "ano"
-#: ../src/tracker/tracker-reset.c:366
-msgid "Removing configuration files…"
-msgstr "Odstraňujú sa konfiguračné súbory…"
-
-#: ../src/tracker/tracker-reset.c:371
+#: src/tracker/tracker-reset.c:307
msgid "Resetting existing configuration…"
msgstr "Odznova sa nastavuje existujúca konfigurácia…"
# cmd desc
-#: ../src/tracker/tracker-search.c:80
+#: src/tracker/tracker-search.c:80
msgid "Search for files"
msgstr "Hľadá súbory"
# cmd desc
-#: ../src/tracker/tracker-search.c:84
+#: src/tracker/tracker-search.c:84
msgid "Search for folders"
msgstr "Hľadá priečinky"
# cmd desc
-#: ../src/tracker/tracker-search.c:88
+#: src/tracker/tracker-search.c:88
msgid "Search for music files"
msgstr "Hľadá súbory hudby"
# cmd desc
-#: ../src/tracker/tracker-search.c:92
+#: src/tracker/tracker-search.c:92
msgid "Search for music albums (--all has no effect on this)"
msgstr "Hľadá hudobné albumy (voľba --all nemá na toto vplyv)"
# cmd desc
-#: ../src/tracker/tracker-search.c:96
+#: src/tracker/tracker-search.c:96
msgid "Search for music artists (--all has no effect on this)"
msgstr "Hľadá hudobných interpretov (voľba --all nemá na toto vplyv)"
# cmd desc
-#: ../src/tracker/tracker-search.c:100
+#: src/tracker/tracker-search.c:100
msgid "Search for image files"
msgstr "Hľadá súbory obrázkov"
# cmd desc
-#: ../src/tracker/tracker-search.c:104
+#: src/tracker/tracker-search.c:104
msgid "Search for video files"
msgstr "Hľadá súbory videa"
# cmd desc
-#: ../src/tracker/tracker-search.c:108
+#: src/tracker/tracker-search.c:108
msgid "Search for document files"
msgstr "Hľadá súbory dokumentov"
# cmd desc
-#: ../src/tracker/tracker-search.c:112
+#: src/tracker/tracker-search.c:112
msgid "Search for emails"
msgstr "Hľadá emaily"
# cmd desc
-#: ../src/tracker/tracker-search.c:116
+#: src/tracker/tracker-search.c:116
msgid "Search for contacts"
msgstr "Hľadá kontakty"
# cmd desc
-#: ../src/tracker/tracker-search.c:120
+#: src/tracker/tracker-search.c:120
msgid "Search for software (--all has no effect on this)"
msgstr "Hľadá softvér (voľba --all nemá na toto vplyv)"
# cmd desc
-#: ../src/tracker/tracker-search.c:124
+#: src/tracker/tracker-search.c:124
msgid "Search for software categories (--all has no effect on this)"
msgstr "Hľadá kategórie softvéru (voľba --all nemá na toto vplyv)"
# cmd desc
-#: ../src/tracker/tracker-search.c:128
+#: src/tracker/tracker-search.c:128
msgid "Search for feeds (--all has no effect on this)"
msgstr "Hľadá kanály (voľba --all nemá na toto vplyv)"
# cmd desc
-#: ../src/tracker/tracker-search.c:132
+#: src/tracker/tracker-search.c:132
msgid "Search for bookmarks (--all has no effect on this)"
msgstr "Hľadá záložky (voľba --all nemá na toto vplyv)"
# cmd desc
-#: ../src/tracker/tracker-search.c:138 ../src/tracker/tracker-tag.c:73
+#: src/tracker/tracker-search.c:138 src/tracker/tracker-tag.c:73
msgid "Limit the number of results shown"
msgstr "Obmedzí počet zobrazených výsledkov"
# cmd desc
-#: ../src/tracker/tracker-search.c:142 ../src/tracker/tracker-tag.c:77
+#: src/tracker/tracker-search.c:142 src/tracker/tracker-tag.c:77
msgid "Offset the results"
msgstr "Posunie výsledky"
# cmd desc
-#: ../src/tracker/tracker-search.c:146
+#: src/tracker/tracker-search.c:146
msgid "Use OR for search terms instead of AND (the default)"
msgstr ""
"Použije pri hľadaní pojmov OR (alebo) namiesto AND (a zároveň, čo je "
"predvolené)"
# cmd desc
-#: ../src/tracker/tracker-search.c:150
+#: src/tracker/tracker-search.c:150
msgid ""
"Show URNs for results (doesn’t apply to --music-albums, --music-artists, --"
"feeds, --software, --software-categories)"
@@ -1222,12 +1215,12 @@ msgstr ""
"artists, --feeds, --software, --software-categories)"
# cmd desc
-#: ../src/tracker/tracker-search.c:154
+#: src/tracker/tracker-search.c:154
msgid "Return all non-existing matches too (i.e. include unmounted volumes)"
msgstr "Vráti aj všetky neexistujúce zhody (t.j. zahrnie aj odpojené zväzky)"
# cmd desc
-#: ../src/tracker/tracker-search.c:158
+#: src/tracker/tracker-search.c:158
msgid ""
"Disable showing snippets with results. This is only shown for some "
"categories, e.g. Documents, Music…"
@@ -1236,131 +1229,130 @@ msgstr ""
"kategóriách ako Dokumenty, Hudba…"
# cmd desc
-#: ../src/tracker/tracker-search.c:162
+#: src/tracker/tracker-search.c:162
msgid "Disable Full Text Search (FTS). Implies --disable-snippets"
msgstr "Zakáže fulltextové vyhľadávanie. Zahŕňa --disable-snippets"
# cmd desc
-#: ../src/tracker/tracker-search.c:166
+#: src/tracker/tracker-search.c:166
msgid "Disable color when printing snippets and results"
msgstr "Zakáže farby pri tlači úryvkov a výsledkov"
# PM: nie som si istý či je toto popis voľby alebo nie
-#: ../src/tracker/tracker-search.c:173 ../src/tracker/tracker-status.c:57
+#: src/tracker/tracker-search.c:173 src/tracker/tracker-status.c:57
msgid "search terms"
msgstr "hľadané pojmy"
-#: ../src/tracker/tracker-search.c:174 ../src/tracker/tracker-status.c:58
+#: src/tracker/tracker-search.c:174 src/tracker/tracker-status.c:58
msgid "EXPRESSION"
msgstr "VÝRAZ"
-#: ../src/tracker/tracker-search.c:192 ../src/tracker/tracker-tag.c:103
+#: src/tracker/tracker-search.c:192 src/tracker/tracker-tag.c:103
msgid ""
"NOTE: Limit was reached, there are more items in the database not listed here"
msgstr ""
"OZNÁMENIE: Bol dosiahnutý limit, v databáze sa nachádza viac položiek, ktoré "
"tu nie sú uvedené"
-#: ../src/tracker/tracker-search.c:286 ../src/tracker/tracker-search.c:395
-#: ../src/tracker/tracker-search.c:497 ../src/tracker/tracker-search.c:811
-#: ../src/tracker/tracker-search.c:900 ../src/tracker/tracker-search.c:990
-#: ../src/tracker/tracker-search.c:1072 ../src/tracker/tracker-search.c:1156
-#: ../src/tracker/tracker-search.c:1238 ../src/tracker/tracker-search.c:1442
+#: src/tracker/tracker-search.c:286 src/tracker/tracker-search.c:395
+#: src/tracker/tracker-search.c:497 src/tracker/tracker-search.c:811
+#: src/tracker/tracker-search.c:900 src/tracker/tracker-search.c:990
+#: src/tracker/tracker-search.c:1072 src/tracker/tracker-search.c:1156
+#: src/tracker/tracker-search.c:1238 src/tracker/tracker-search.c:1442
msgid "Could not get search results"
msgstr "Nepodarilo sa získať výsledky hľadania"
-#: ../src/tracker/tracker-search.c:295
+#: src/tracker/tracker-search.c:295
msgid "No contacts were found"
msgstr "Nenašli sa žiadne kontakty"
-#: ../src/tracker/tracker-search.c:299
+#: src/tracker/tracker-search.c:299
msgid "Contacts"
msgstr "Kontakty"
-#: ../src/tracker/tracker-search.c:356 ../src/tracker/tracker-search.c:369
+#: src/tracker/tracker-search.c:356 src/tracker/tracker-search.c:369
msgid "No name"
msgstr "Bez mena"
-#: ../src/tracker/tracker-search.c:357 ../src/tracker/tracker-search.c:370
+#: src/tracker/tracker-search.c:357 src/tracker/tracker-search.c:370
msgid "No E-mail address"
msgstr "Bez emailovej adresy"
-#: ../src/tracker/tracker-search.c:404
+#: src/tracker/tracker-search.c:404
msgid "No emails were found"
msgstr "Nenašli sa žiadne emaily"
-#: ../src/tracker/tracker-search.c:408
+#: src/tracker/tracker-search.c:408
msgid "Emails"
msgstr "E-maily"
-#: ../src/tracker/tracker-search.c:506
+#: src/tracker/tracker-search.c:506
msgid "No files were found"
msgstr "Nenašli sa žiadne súbory"
# GtkLabel
-#: ../src/tracker/tracker-search.c:510 ../src/tracker/tracker-tag.c:439
+#: src/tracker/tracker-search.c:510 src/tracker/tracker-tag.c:439
msgid "Files"
msgstr "Súbory"
-#: ../src/tracker/tracker-search.c:820
+#: src/tracker/tracker-search.c:820
msgid "No artists were found"
msgstr "Nenašli sa žiadni umelci"
-#: ../src/tracker/tracker-search.c:824
+#: src/tracker/tracker-search.c:824
msgid "Artists"
msgstr "Umelci"
-#: ../src/tracker/tracker-search.c:909
+#: src/tracker/tracker-search.c:909
msgid "No music was found"
msgstr "Nenašla sa žiadna hudba"
-#: ../src/tracker/tracker-search.c:913
+#: src/tracker/tracker-search.c:913
msgid "Albums"
msgstr "Albumy"
-#: ../src/tracker/tracker-search.c:999
+#: src/tracker/tracker-search.c:999
msgid "No bookmarks were found"
msgstr "Nenašli sa žiadne záložky"
-#: ../src/tracker/tracker-search.c:1003
+#: src/tracker/tracker-search.c:1003
msgid "Bookmarks"
msgstr "Záložky"
-#: ../src/tracker/tracker-search.c:1081
+#: src/tracker/tracker-search.c:1081
msgid "No feeds were found"
msgstr "Nenašli sa žiadne kanály"
-#: ../src/tracker/tracker-search.c:1085
+#: src/tracker/tracker-search.c:1085
msgid "Feeds"
msgstr "Kanály"
-#: ../src/tracker/tracker-search.c:1165
+#: src/tracker/tracker-search.c:1165
msgid "No software was found"
msgstr "Nenašiel sa žiadny softvér"
-#: ../src/tracker/tracker-search.c:1169
+#: src/tracker/tracker-search.c:1169
msgid "Software"
msgstr "Softvér"
-#: ../src/tracker/tracker-search.c:1247
+#: src/tracker/tracker-search.c:1247
msgid "No software categories were found"
msgstr "Nenašli sa žiadne kategórie softvéru"
-#: ../src/tracker/tracker-search.c:1251
+#: src/tracker/tracker-search.c:1251
msgid "Software Categories"
msgstr "Kategórie softvéru"
-#: ../src/tracker/tracker-search.c:1451
+#: src/tracker/tracker-search.c:1451
msgid "No results were found matching your query"
msgstr "Nenašli sa žiadne výsledky zhodujúce sa s vašou požiadavkou"
-#: ../src/tracker/tracker-search.c:1553
+#: src/tracker/tracker-search.c:1553
#, c-format
msgid "Search term “%s” is a stop word."
msgstr "Hľadaný pojem „%s“ je stopslovo."
-#: ../src/tracker/tracker-search.c:1564
-#, c-format
+#: src/tracker/tracker-search.c:1564
msgid ""
"Stop words are common words which may be ignored during the indexing process."
msgstr ""
@@ -1368,71 +1360,71 @@ msgstr ""
"procesu indexovania ignorované."
# cmd desc
-#: ../src/tracker/tracker-sparql.c:105
+#: src/tracker/tracker-sparql.c:105
msgid "Path to use to run a query or update from file"
msgstr ""
"Cesta, ktorá sa má použiť na spustenie požiadavky alebo aktualizovanie zo "
"súboru"
# cmd desc
-#: ../src/tracker/tracker-sparql.c:109
+#: src/tracker/tracker-sparql.c:109
msgid "SPARQL query"
msgstr "Požiadavka SPARQL"
-#: ../src/tracker/tracker-sparql.c:110
+#: src/tracker/tracker-sparql.c:110
msgid "SPARQL"
msgstr "SPARQL"
# cmd desc
-#: ../src/tracker/tracker-sparql.c:113
+#: src/tracker/tracker-sparql.c:113
msgid "This is used with --query and for database updates only."
msgstr "Toto sa používa iba spolu s --query a pre aktualizovanie databázy"
# cmd desc
-#: ../src/tracker/tracker-sparql.c:117
+#: src/tracker/tracker-sparql.c:117
msgid "Retrieve classes"
msgstr "Získa triedy"
# cmd desc
-#: ../src/tracker/tracker-sparql.c:121
+#: src/tracker/tracker-sparql.c:121
msgid "Retrieve class prefixes"
msgstr "Získa predpony tried"
# cmd desc
-#: ../src/tracker/tracker-sparql.c:125
+#: src/tracker/tracker-sparql.c:125
msgid ""
"Retrieve properties for a class, prefixes can be used too (e.g. rdfs:"
"Resource)"
msgstr ""
"Získa vlastnosti pre triedu, môžu byť použité aj predpony (napr.: rdfs:Zdroj)"
-#: ../src/tracker/tracker-sparql.c:126 ../src/tracker/tracker-sparql.c:130
-#: ../src/tracker/tracker-sparql.c:138 ../src/tracker/tracker-sparql.c:146
-#: ../src/tracker/tracker-sparql.c:150
+#: src/tracker/tracker-sparql.c:126 src/tracker/tracker-sparql.c:130
+#: src/tracker/tracker-sparql.c:138 src/tracker/tracker-sparql.c:146
+#: src/tracker/tracker-sparql.c:150
msgid "CLASS"
msgstr "TRIEDA"
# cmd desc
-#: ../src/tracker/tracker-sparql.c:129
+#: src/tracker/tracker-sparql.c:129
msgid ""
"Retrieve classes which notify changes in the database (CLASS is optional)"
msgstr ""
"Získa triedy, ktoré upozorňujú na zmeny v databáze (TRIEDA je nepovinná)"
# cmd desc
-#: ../src/tracker/tracker-sparql.c:133
+#: src/tracker/tracker-sparql.c:133
msgid ""
"Retrieve indexes used in database to improve performance (PROPERTY is "
"optional)"
msgstr ""
"Získa indexy použité v databáze na zvýšenie výkonu (VLASTNOSŤ je voliteľná)"
-#: ../src/tracker/tracker-sparql.c:134
+#: src/tracker/tracker-sparql.c:134
msgid "PROPERTY"
msgstr "VLASTNOSŤ"
# cmd desc
-#: ../src/tracker/tracker-sparql.c:137
+#: src/tracker/tracker-sparql.c:137
msgid ""
"Describe subclasses, superclasses (can be used with -s to highlight parts of "
"the tree and -p to show properties)"
@@ -1441,39 +1433,39 @@ msgstr ""
"častí stromu a parametrom -p na zobrazenie vlastností)"
# cmd desc
-#: ../src/tracker/tracker-sparql.c:141
+#: src/tracker/tracker-sparql.c:141
msgid ""
"Search for a class or property and display more information (e.g. Document)"
msgstr ""
"Vyhľadá triedu alebo vlastnosť a zobrazí viac informácií (napr. Dokument)"
-#: ../src/tracker/tracker-sparql.c:142
+#: src/tracker/tracker-sparql.c:142
msgid "CLASS/PROPERTY"
msgstr "TRIEDA/VLASTNOSŤ"
# cmd desc
-#: ../src/tracker/tracker-sparql.c:145
+#: src/tracker/tracker-sparql.c:145
msgid "Returns the shorthand for a class (e.g. nfo:FileDataObject)."
msgstr "Vráti skrátený výpis pre triedu (napr. nfo:FileDataObject)."
# cmd desc
-#: ../src/tracker/tracker-sparql.c:149
+#: src/tracker/tracker-sparql.c:149
msgid "Returns the full namespace for a class."
msgstr "Vráti celý menný priestor pre triedu."
-#: ../src/tracker/tracker-sparql.c:153
+#: src/tracker/tracker-sparql.c:153
msgid "Remote service to query to"
msgstr "Vzdialená služba, ktorej odosielať požiadavky"
-#: ../src/tracker/tracker-sparql.c:154
+#: src/tracker/tracker-sparql.c:154
msgid "BASE_URL"
msgstr "ZÁKLADNÁ_URL"
-#: ../src/tracker/tracker-sparql.c:254
+#: src/tracker/tracker-sparql.c:254
msgid "Could not get namespace prefixes"
msgstr "Nepodarilo sa získať predpony menných priestorov"
-#: ../src/tracker/tracker-sparql.c:263
+#: src/tracker/tracker-sparql.c:263
msgid "No namespace prefixes were found"
msgstr "Neboli nájdené žiadne predpony menných priestorov"
@@ -1506,129 +1498,129 @@ msgstr "Neboli nájdené žiadne predpony menných priestorov"
#. * None
#. *
#.
-#: ../src/tracker/tracker-sparql.c:509 ../src/tracker/tracker-sparql.c:557
-#: ../src/tracker/tracker-status.c:136 ../src/tracker/tracker-tag.c:323
-#: ../src/tracker/tracker-tag.c:455 ../src/tracker/tracker-tag.c:577
-#: ../src/tracker/tracker-tag.c:955
+#: src/tracker/tracker-sparql.c:509 src/tracker/tracker-sparql.c:557
+#: src/tracker/tracker-status.c:136 src/tracker/tracker-tag.c:323
+#: src/tracker/tracker-tag.c:455 src/tracker/tracker-tag.c:577
+#: src/tracker/tracker-tag.c:955
msgid "None"
msgstr "Nič"
-#: ../src/tracker/tracker-sparql.c:963
+#: src/tracker/tracker-sparql.c:963
msgid "Could not create tree: subclass query failed"
msgstr "Nepodarilo sa vytvoriť strom: požiadavka na podtriedu zlyhala "
-#: ../src/tracker/tracker-sparql.c:1012
+#: src/tracker/tracker-sparql.c:1012
msgid "Could not create tree: class properties query failed"
msgstr "Nepodarilo sa vytvoriť strom: požiadavka na vlastnosti triedy zlyhala"
-#: ../src/tracker/tracker-sparql.c:1100
+#: src/tracker/tracker-sparql.c:1100
msgid "Could not list classes"
msgstr "Nepodarilo sa vypísať zoznam tried"
-#: ../src/tracker/tracker-sparql.c:1108
+#: src/tracker/tracker-sparql.c:1108
msgid "No classes were found"
msgstr "Nenašli sa žiadne triedy"
-#: ../src/tracker/tracker-sparql.c:1108 ../src/tracker/tracker-sparql.c:1299
+#: src/tracker/tracker-sparql.c:1108 src/tracker/tracker-sparql.c:1299
msgid "Classes"
msgstr "Triedy"
-#: ../src/tracker/tracker-sparql.c:1124
+#: src/tracker/tracker-sparql.c:1124
msgid "Could not list class prefixes"
msgstr "Nepodarilo sa vypísať zoznam predpôn triedy"
-#: ../src/tracker/tracker-sparql.c:1132
+#: src/tracker/tracker-sparql.c:1132
msgid "No class prefixes were found"
msgstr "Nenašli sa žiadne predpony"
-#: ../src/tracker/tracker-sparql.c:1132
+#: src/tracker/tracker-sparql.c:1132
msgid "Prefixes"
msgstr "Predpony"
-#: ../src/tracker/tracker-sparql.c:1152
+#: src/tracker/tracker-sparql.c:1152
msgid ""
"Could not find property for class prefix, e.g. :Resource in “rdfs:Resource”"
msgstr ""
"Nepodarilo sa nájsť vlastnosť pre predponu triedy, napr Zdroj v „rdfs:Zdroj“"
-#: ../src/tracker/tracker-sparql.c:1191
+#: src/tracker/tracker-sparql.c:1191
msgid "Could not list properties"
msgstr "Nepodarilo sa vypísať zoznam vlastností"
-#: ../src/tracker/tracker-sparql.c:1199
+#: src/tracker/tracker-sparql.c:1199
msgid "No properties were found"
msgstr "Nenašli sa žiadne vlastnosti"
-#: ../src/tracker/tracker-sparql.c:1199 ../src/tracker/tracker-sparql.c:1322
+#: src/tracker/tracker-sparql.c:1199 src/tracker/tracker-sparql.c:1322
msgid "Properties"
msgstr "Vlastnosti"
-#: ../src/tracker/tracker-sparql.c:1227
+#: src/tracker/tracker-sparql.c:1227
msgid "Could not find notify classes"
msgstr "Nepodarilo sa nájsť oznamujúce triedy"
-#: ../src/tracker/tracker-sparql.c:1235
+#: src/tracker/tracker-sparql.c:1235
msgid "No notifies were found"
msgstr "Nenašli sa žiadne oznámenia"
-#: ../src/tracker/tracker-sparql.c:1235
+#: src/tracker/tracker-sparql.c:1235
msgid "Notifies"
msgstr "Oznámenia"
-#: ../src/tracker/tracker-sparql.c:1261
+#: src/tracker/tracker-sparql.c:1261
msgid "Could not find indexed properties"
msgstr "Nepodarilo sa nájsť indexované vlastnosti"
-#: ../src/tracker/tracker-sparql.c:1269
+#: src/tracker/tracker-sparql.c:1269
msgid "No indexes were found"
msgstr "Nenašli sa žiadne indexy"
-#: ../src/tracker/tracker-sparql.c:1269
+#: src/tracker/tracker-sparql.c:1269
msgid "Indexes"
msgstr "Indexy"
-#: ../src/tracker/tracker-sparql.c:1291
+#: src/tracker/tracker-sparql.c:1291
msgid "Could not search classes"
msgstr "Nepodarilo sa vyhľadať triedy"
-#: ../src/tracker/tracker-sparql.c:1299
+#: src/tracker/tracker-sparql.c:1299
msgid "No classes were found to match search term"
msgstr "Nenašli sa žiadne vlastnosti zodpovedajúce hľadanému pojmu"
-#: ../src/tracker/tracker-sparql.c:1314
+#: src/tracker/tracker-sparql.c:1314
msgid "Could not search properties"
msgstr "Nepodarilo sa vyhľadať vlastnosti"
-#: ../src/tracker/tracker-sparql.c:1322
+#: src/tracker/tracker-sparql.c:1322
msgid "No properties were found to match search term"
msgstr "Nenašli sa žiadne vlastnosti zodpovedajúce hľadanému pojmu"
-#: ../src/tracker/tracker-sparql.c:1358 ../src/tracker/tracker-sql.c:65
+#: src/tracker/tracker-sparql.c:1358 src/tracker/tracker-sql.c:65
msgid "Could not get UTF-8 path from path"
msgstr "Nepodarilo sa získať cestu v UTF-8 z cesty"
-#: ../src/tracker/tracker-sparql.c:1370 ../src/tracker/tracker-sql.c:76
+#: src/tracker/tracker-sparql.c:1370 src/tracker/tracker-sql.c:76
msgid "Could not read file"
msgstr "Nepodarilo sa prečítať súbor"
-#: ../src/tracker/tracker-sparql.c:1389
+#: src/tracker/tracker-sparql.c:1389
msgid "Could not run update"
msgstr "Nepodarilo sa spustiť aktualizáciu"
-#: ../src/tracker/tracker-sparql.c:1437 ../src/tracker/tracker-sql.c:139
-#: ../src/tracker/tracker-sql.c:172
+#: src/tracker/tracker-sparql.c:1437 src/tracker/tracker-sql.c:139
+#: src/tracker/tracker-sql.c:172
msgid "Could not run query"
msgstr "Nepodarilo sa spustiť požiadavku"
-#: ../src/tracker/tracker-sparql.c:1449 ../src/tracker/tracker-sparql.c:1452
+#: src/tracker/tracker-sparql.c:1449 src/tracker/tracker-sparql.c:1452
msgid "No results found matching your query"
msgstr "Nájdené výsledky sa nezhodujú z vašou požiadavkou"
-#: ../src/tracker/tracker-sparql.c:1506 ../src/tracker/tracker-sql.c:247
+#: src/tracker/tracker-sparql.c:1506 src/tracker/tracker-sql.c:247
msgid "File and query can not be used together"
msgstr "Voľby file a query nemôžu byť použité súčasne"
-#: ../src/tracker/tracker-sparql.c:1508
+#: src/tracker/tracker-sparql.c:1508
msgid ""
"The --list-properties argument can only be empty when used with the --tree "
"argument"
@@ -1637,34 +1629,34 @@ msgstr ""
"argumentom --tree"
# cmd desc
-#: ../src/tracker/tracker-sql.c:43
+#: src/tracker/tracker-sql.c:43
msgid "Path to use to run a query from file"
msgstr "Cesta, ktorá sa má použiť na spustenie požiadavky zo súboru"
# cmd desc
-#: ../src/tracker/tracker-sql.c:47
+#: src/tracker/tracker-sql.c:47
msgid "SQL query"
msgstr "Požiadavka SQL"
-#: ../src/tracker/tracker-sql.c:48
+#: src/tracker/tracker-sql.c:48
msgid "SQL"
msgstr "SQL"
-#: ../src/tracker/tracker-sql.c:120
+#: src/tracker/tracker-sql.c:120
msgid "Failed to initialize data manager"
msgstr "Zlyhala inicializácia správcu údajov"
-#: ../src/tracker/tracker-sql.c:180
+#: src/tracker/tracker-sql.c:180
msgid "Empty result set"
msgstr "Množina výsledkov prázdna"
# tooltip
-#: ../src/tracker/tracker-status.c:49
+#: src/tracker/tracker-status.c:49
msgid "Show statistics for current index / data set"
msgstr "Zobrazí štatistiky o aktuálnej sade indexov / údajov"
# cmd desc
-#: ../src/tracker/tracker-status.c:53
+#: src/tracker/tracker-status.c:53
msgid ""
"Collect debug information useful for problem reporting and investigation, "
"results are output to terminal"
@@ -1672,252 +1664,270 @@ msgstr ""
"Zozbiera ladiace informácie užitočné pre nahlásenie a preskúmanie problému, "
"výsledky budú vypísané v termináli"
-#: ../src/tracker/tracker-status.c:85
+#: src/tracker/tracker-status.c:85
msgid "Could not get Tracker statistics"
msgstr "Nepodarilo sa získať štatistiky sledovača"
-#: ../src/tracker/tracker-status.c:92
+#: src/tracker/tracker-status.c:92
msgid "No statistics available"
msgstr "Žiadne dostupné štatistiky"
#. To translators: This is to say there are no
#. * statistics found. We use a "Statistics:
#. * None" with multiple print statements
-#: ../src/tracker/tracker-status.c:133
+#: src/tracker/tracker-status.c:133
msgid "Statistics:"
msgstr "Štatistiky:"
# cmd desc
-#: ../src/tracker/tracker-status.c:170
+#: src/tracker/tracker-status.c:170
msgid "Version"
msgstr "Verzia"
# cmd desc
-#: ../src/tracker/tracker-status.c:177
+#: src/tracker/tracker-status.c:177
msgid "Disk Information"
msgstr "Informácie o disku"
-#: ../src/tracker/tracker-status.c:184 ../src/tracker/tracker-status.c:538
+#: src/tracker/tracker-status.c:184 src/tracker/tracker-status.c:538
msgid "Remaining space on database partition"
msgstr "Zostávajúce miesto na oddieli s databázou"
#. 3. Size of dataset (tracker-stats), size of databases
-#: ../src/tracker/tracker-status.c:191
+#: src/tracker/tracker-status.c:191
msgid "Data Set"
msgstr "Sada údajov"
-#: ../src/tracker/tracker-status.c:223
+#: src/tracker/tracker-status.c:223
msgid "Configuration"
msgstr "Konfigurácia"
-#: ../src/tracker/tracker-status.c:251
+#: src/tracker/tracker-status.c:251
msgid "No configuration was found"
msgstr "Nenašla sa žiadna konfigurácia"
-#: ../src/tracker/tracker-status.c:255
+#: src/tracker/tracker-status.c:255
msgid "States"
msgstr "Stavy"
-#: ../src/tracker/tracker-status.c:296
+#: src/tracker/tracker-status.c:296
msgid "Data Statistics"
msgstr "Štatistiky údajov"
-#: ../src/tracker/tracker-status.c:302
+#: src/tracker/tracker-status.c:302
msgid "No connection available"
msgstr "Nie je dostupné žiadne pripojenie"
-#: ../src/tracker/tracker-status.c:312
+#: src/tracker/tracker-status.c:312
msgid "Could not get statistics"
msgstr "Nepodarilo sa získať štatistiky"
-#: ../src/tracker/tracker-status.c:318
+#: src/tracker/tracker-status.c:318
msgid "No statistics were available"
msgstr "Neboli dostupné žiadne štatistiky"
-#: ../src/tracker/tracker-status.c:331
+#: src/tracker/tracker-status.c:331
msgid "Database is currently empty"
msgstr "Databáza je momentálne prázdna"
-#: ../src/tracker/tracker-status.c:408 ../src/tracker/tracker-status.c:431
+#: src/tracker/tracker-status.c:408 src/tracker/tracker-status.c:431
msgid "Could not get basic status for Tracker"
msgstr "Nepodarilo sa získať základný stav sledovača"
-#: ../src/tracker/tracker-status.c:515
-#, c-format
+#: src/tracker/tracker-status.c:515
msgid "Currently indexed"
msgstr "Aktuálne indexované"
-#: ../src/tracker/tracker-status.c:550
+#: src/tracker/tracker-status.c:518 src/tracker/tracker-tag.c:559
+#, c-format
+#| msgid "Ignored files"
+msgid "%d file"
+msgid_plural "%d files"
+msgstr[0] "%d súborov"
+msgstr[1] "%d súbor"
+msgstr[2] "%d súbory"
+
+#: src/tracker/tracker-status.c:524
+#, c-format
+#| msgid "Folder"
+#| msgid_plural "Folders"
+msgid "%d folder"
+msgid_plural "%d folders"
+msgstr[0] "%d priečinkov"
+msgstr[1] "%d priečinok"
+msgstr[2] "%d priečinky"
+
+#: src/tracker/tracker-status.c:550
msgid "Data is still being indexed"
msgstr "Prebieha indexovanie údajov"
-#: ../src/tracker/tracker-status.c:551
+#: src/tracker/tracker-status.c:551
#, c-format
msgid "Estimated %s left"
msgstr "Odhadovaný zostávajúci čas: %s"
-#: ../src/tracker/tracker-status.c:555
+#: src/tracker/tracker-status.c:555
msgid "All data miners are idle, indexing complete"
msgstr "Všetky dolovače údajov sú nečinné. Indexovanie je dokončené."
# cmd desc
-#: ../src/tracker/tracker-tag.c:53
+#: src/tracker/tracker-tag.c:53
msgid ""
"List all tags (using FILTER if specified; FILTER always uses logical OR)"
msgstr ""
"Zoznam všetkých značiek (použije FILTER ak je zadaný; FILTER vždy použije "
"logické OR)"
-#: ../src/tracker/tracker-tag.c:54
+#: src/tracker/tracker-tag.c:54
msgid "FILTER"
msgstr "FILTER"
# cmd desc
-#: ../src/tracker/tracker-tag.c:57
+#: src/tracker/tracker-tag.c:57
msgid "Show files associated with each tag (this is only used with --list)"
msgstr ""
"Zobrazí súbory priradené ku každej značke (toto sa dá použiť iba s voľbou --"
"list)"
# cmd desc
-#: ../src/tracker/tracker-tag.c:61
+#: src/tracker/tracker-tag.c:61
msgid "Add a tag (if FILEs are omitted, TAG is not associated with any files)"
msgstr ""
"Pridá značku (Ak sú vynechané parametre SÚBOR, ZNAČKA nebude priradená "
"žiadnemu súboru)"
-#: ../src/tracker/tracker-tag.c:62 ../src/tracker/tracker-tag.c:66
+#: src/tracker/tracker-tag.c:62 src/tracker/tracker-tag.c:66
msgid "TAG"
msgstr "ZNAČKA"
# cmd desc
-#: ../src/tracker/tracker-tag.c:65
+#: src/tracker/tracker-tag.c:65
msgid "Delete a tag (if FILEs are omitted, TAG is removed for all files)"
msgstr ""
"Odstráni značku (Ak sú vynechané parametre SÚBOR, ZNAČKA sa odstráni zo "
"všetkých súborov)"
# cmd desc
-#: ../src/tracker/tracker-tag.c:69
+#: src/tracker/tracker-tag.c:69
msgid "Description for a tag (this is only used with --add)"
msgstr "Popis značky (toto je použité iba s voľbou --add)"
-#: ../src/tracker/tracker-tag.c:70
+#: src/tracker/tracker-tag.c:70
msgid "STRING"
msgstr "REŤAZEC"
# cmd desc
-#: ../src/tracker/tracker-tag.c:81
+#: src/tracker/tracker-tag.c:81
msgid "Use AND for search terms instead of OR (the default)"
msgstr ""
"Použije pri hľadaní pojmov AND (a zároveň) namiesto OR(alebo, čo je "
"predvolené)"
-#: ../src/tracker/tracker-tag.c:86
+#: src/tracker/tracker-tag.c:86
msgid "FILE…"
msgstr "SÚBOR…"
-#: ../src/tracker/tracker-tag.c:87
+#: src/tracker/tracker-tag.c:87
msgid "FILE [FILE…]"
msgstr "SÚBOR [SÚBOR…]"
-#: ../src/tracker/tracker-tag.c:249
+#: src/tracker/tracker-tag.c:249
msgid "Could not get file URNs"
msgstr "Nepodarilo sa získať názvy URN súboru"
-#: ../src/tracker/tracker-tag.c:314
+#: src/tracker/tracker-tag.c:314
msgid "Could not get files related to tag"
msgstr "Nepodarilo sa získať súbory súvisiace so značkou"
-#: ../src/tracker/tracker-tag.c:381
+#: src/tracker/tracker-tag.c:381
msgid "Could not get all tags in the database"
msgstr "Nepodarilo sa získať všetky značky v databáze"
-#: ../src/tracker/tracker-tag.c:391
+#: src/tracker/tracker-tag.c:391
msgid "No files have been tagged"
msgstr "Neboli označené žiadne súbory"
-#: ../src/tracker/tracker-tag.c:426
+#: src/tracker/tracker-tag.c:426
msgid "Could not get files for matching tags"
msgstr "Nepodarilo sa získať súbory zhodujúce sa so značkami"
-#: ../src/tracker/tracker-tag.c:435
+#: src/tracker/tracker-tag.c:435
msgid "No files were found matching ALL of those tags"
msgstr "Nenašli sa žiadne súbory zhodujúce sa so VŠETKÝMI značkami"
-#: ../src/tracker/tracker-tag.c:514 ../src/tracker/tracker-tag.c:929
+#: src/tracker/tracker-tag.c:514 src/tracker/tracker-tag.c:929
msgid "Could not get all tags"
msgstr "Nepodarilo sa získať všetky značky"
-#: ../src/tracker/tracker-tag.c:523 ../src/tracker/tracker-tag.c:938
+#: src/tracker/tracker-tag.c:523 src/tracker/tracker-tag.c:938
msgid "No tags were found"
msgstr "Nenašli sa žiadne značky"
-#: ../src/tracker/tracker-tag.c:527
+#: src/tracker/tracker-tag.c:527
msgid "Tags (shown by name)"
msgstr "Značky (zobrazené podľa názvu)"
-#: ../src/tracker/tracker-tag.c:601
+#: src/tracker/tracker-tag.c:601
msgid "No files were modified"
msgstr "Neboli zmenené žiadne súbory"
-#: ../src/tracker/tracker-tag.c:651 ../src/tracker/tracker-tag.c:659
+#: src/tracker/tracker-tag.c:651 src/tracker/tracker-tag.c:659
msgid "Files do not exist or aren’t indexed"
msgstr "Súbory neexistujú, alebo nie sú indexované"
-#: ../src/tracker/tracker-tag.c:709
+#: src/tracker/tracker-tag.c:709
msgid "Could not add tag"
msgstr "Nepodarilo sa pridať značku"
-#: ../src/tracker/tracker-tag.c:725
+#: src/tracker/tracker-tag.c:725
msgid "Tag was added successfully"
msgstr "Značka bola úspešne pridaná"
-#: ../src/tracker/tracker-tag.c:754
+#: src/tracker/tracker-tag.c:754
msgid "Could not add tag to files"
msgstr "Nepodarilo sa pridať značku k súborom"
-#: ../src/tracker/tracker-tag.c:764
+#: src/tracker/tracker-tag.c:764
msgid "Tagged"
msgstr "Označené"
-#: ../src/tracker/tracker-tag.c:765
+#: src/tracker/tracker-tag.c:765
msgid "Not tagged, file is not indexed"
msgstr "Neoznačené, súbor nie je indexovaný"
-#: ../src/tracker/tracker-tag.c:811
+#: src/tracker/tracker-tag.c:811
msgid "Could not get tag by label"
msgstr "Nepodarilo sa získať značku podľa menovky"
-#: ../src/tracker/tracker-tag.c:822
+#: src/tracker/tracker-tag.c:822
msgid "No tags were found by that name"
msgstr "Nenašli sa žiadne značky podľa takého názvu"
-#: ../src/tracker/tracker-tag.c:839
+#: src/tracker/tracker-tag.c:839
msgid "None of the files had this tag set"
msgstr "Žiadny súbor nemá nastavenú takúto značku"
-#: ../src/tracker/tracker-tag.c:885
+#: src/tracker/tracker-tag.c:885
msgid "Could not remove tag"
msgstr "Nepodarilo sa odstrániť značku"
-#: ../src/tracker/tracker-tag.c:892
+#: src/tracker/tracker-tag.c:892
msgid "Tag was removed successfully"
msgstr "Značka bola úspešne odstránená"
-#: ../src/tracker/tracker-tag.c:896
+#: src/tracker/tracker-tag.c:896
msgid "Untagged"
msgstr "Označenie zrušené"
-#: ../src/tracker/tracker-tag.c:897
+#: src/tracker/tracker-tag.c:897
msgid "File not indexed or already untagged"
msgstr "Súbor nie je indexovaný alebo je už označenie zrušené"
-#: ../src/tracker/tracker-tag.c:1088
+#: src/tracker/tracker-tag.c:1088
msgid "The --list option is required for --show-files"
msgstr "Pre --show-files sa vyžaduje voľba --list"
-#: ../src/tracker/tracker-tag.c:1090
+#: src/tracker/tracker-tag.c:1090
msgid ""
"The --and-operator option can only be used with --list and tag label "
"arguments"
@@ -1925,14 +1935,28 @@ msgstr ""
"Parameter --and-operator je možné použiť iba spolu s parametrami --list a "
"menovkou značky"
-#: ../src/tracker/tracker-tag.c:1092
+#: src/tracker/tracker-tag.c:1092
msgid "Add and delete actions can not be used together"
msgstr "Akcie add (pridať) a delete (odstrániť) nemôžu byť použité súčasne"
-#: ../src/tracker/tracker-tag.c:1094
+#: src/tracker/tracker-tag.c:1094
msgid "The --description option can only be used with --add"
msgstr "Voľbu --description je možné použiť iba spolu s voľbou --add "
+# tab
+#~ msgid "All posts"
+#~ msgstr "Všetky príspevky"
+
+# tab
+#~ msgid "By usage"
+#~ msgstr "Podľa použitia"
+
+#~ msgid "Data store is not available"
+#~ msgstr "Úložisko údajov nie je dostupné"
+
+#~ msgid "Removing configuration files…"
+#~ msgstr "Odstraňujú sa konfiguračné súbory…"
+
#~ msgid "Applications"
#~ msgstr "Aplikácie"
@@ -2102,9 +2126,6 @@ msgstr "Voľbu --description je možné použiť iba spolu s voľbou --add "
#~ "VIDEOS. Pozrite /etc/xdg/user-dirs.defaults a $HOME/.config/user-dirs."
#~ "default"
-#~ msgid "Ignored files"
-#~ msgstr "Ignorované súbory"
-
#~ msgid "List of file patterns to avoid"
#~ msgstr "Zoznam vzorov súborov, ktoré sa majú preskočiť"
@@ -2503,12 +2524,6 @@ msgstr "Voľbu --description je možné použiť iba spolu s voľbou --add "
#~ msgstr[1] "Súbor"
#~ msgstr[2] "Súbory"
-#~ msgid "Folder"
-#~ msgid_plural "Folders"
-#~ msgstr[0] "Priečinky"
-#~ msgstr[1] "Priečinok"
-#~ msgstr[2] "Priečinky"
-
#~ msgid "Image"
#~ msgid_plural "Images"
#~ msgstr[0] "Obrázky"
diff --git a/src/libtracker-common/tracker-date-time.c b/src/libtracker-common/tracker-date-time.c
index e8279d728..162d9081a 100644
--- a/src/libtracker-common/tracker-date-time.c
+++ b/src/libtracker-common/tracker-date-time.c
@@ -201,14 +201,15 @@ tracker_string_to_date (const gchar *date_string,
}
gchar *
-tracker_date_to_string (gdouble date_time)
+tracker_date_to_string (gdouble date_time,
+ gint offset)
{
- gchar buffer[30];
+ gchar buffer[35];
time_t seconds;
gint64 total_milliseconds;
gint milliseconds;
struct tm utc_time;
- size_t count;
+ size_t count, size;
memset (buffer, '\0', sizeof (buffer));
memset (&utc_time, 0, sizeof (struct tm));
@@ -218,7 +219,7 @@ tracker_date_to_string (gdouble date_time)
if (milliseconds < 0) {
milliseconds += 1000;
}
- seconds = (time_t) ((total_milliseconds - milliseconds) / 1000);
+ seconds = (time_t) ((total_milliseconds - milliseconds) / 1000) + offset;
gmtime_r (&seconds, &utc_time);
/* Output is ISO 8601 format : "YYYY-MM-DDThh:mm:ss" */
@@ -226,11 +227,25 @@ tracker_date_to_string (gdouble date_time)
/* Append milliseconds (if non-zero) and time zone */
if (milliseconds > 0) {
- snprintf (buffer + count, sizeof (buffer) - count, ".%03dZ", milliseconds);
+ size = snprintf (buffer + count, sizeof (buffer) - count, ".%03d", milliseconds);
+ count += size;
+ }
+
+ if (offset != 0) {
+ gint hours, mins;
+
+ hours = ABS (offset) / 3600;
+ mins = (ABS (offset) % 3600) / 60;
+ size = snprintf (buffer + count, sizeof (buffer) - count, "%c%.2d:%.2d",
+ offset < 0 ? '-' : '+',
+ hours, mins);
+ count += size;
} else {
- buffer[count] = 'Z';
+ buffer[count++] = 'Z';
}
+ g_assert (count <= sizeof (buffer));
+
return count > 0 ? g_strdup (buffer) : NULL;
}
diff --git a/src/libtracker-common/tracker-date-time.h b/src/libtracker-common/tracker-date-time.h
index 78b56eb1f..df949e90e 100644
--- a/src/libtracker-common/tracker-date-time.h
+++ b/src/libtracker-common/tracker-date-time.h
@@ -57,7 +57,8 @@ gint tracker_date_time_get_local_time (const GValue *value);
gdouble tracker_string_to_date (const gchar *date_string,
gint *offset,
GError **error);
-gchar * tracker_date_to_string (gdouble date_time);
+gchar * tracker_date_to_string (gdouble date_time,
+ gint offset);
G_END_DECLS
diff --git a/src/libtracker-common/tracker-log.c b/src/libtracker-common/tracker-log.c
index d300ee795..094b71789 100644
--- a/src/libtracker-common/tracker-log.c
+++ b/src/libtracker-common/tracker-log.c
@@ -151,6 +151,18 @@ hide_log_handler (const gchar *domain,
/* do nothing */
}
+static void
+ensure_g_messages_debug_set ()
+{
+ const gchar *value;
+
+ value = g_getenv ("G_MESSAGES_DEBUG");
+
+ if (value == NULL) {
+ g_setenv ("G_MESSAGES_DEBUG", "Tracker", TRUE);
+ }
+}
+
gboolean
tracker_log_init (gint this_verbosity,
gchar **used_filename)
@@ -192,8 +204,8 @@ tracker_log_init (gint this_verbosity,
/* If we have debug enabled, we imply G_MESSAGES_DEBUG or we
* see nothing, this came in since GLib 2.32.
*/
- if (this_verbosity > 1) {
- g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
+ if (this_verbosity > 0) {
+ ensure_g_messages_debug_set ();
}
if (use_log_files) {
@@ -229,7 +241,9 @@ tracker_log_init (gint this_verbosity,
g_free (filename);
}
} else {
- *used_filename = NULL;
+ if (used_filename) {
+ *used_filename = NULL;
+ }
}
verbosity = CLAMP (this_verbosity, 0, 3);
diff --git a/src/libtracker-common/tracker-utils.c b/src/libtracker-common/tracker-utils.c
index f6fb89348..530bb4a70 100644
--- a/src/libtracker-common/tracker-utils.c
+++ b/src/libtracker-common/tracker-utils.c
@@ -323,3 +323,119 @@ tracker_unescape_unichars (const gchar *str,
return g_string_free (copy, FALSE);
}
+
+gboolean
+parse_abs_uri (const gchar *uri,
+ gchar **base,
+ const gchar **rel_path)
+{
+ const gchar *loc, *end;
+
+ end = &uri[strlen (uri)];
+ loc = uri;
+
+ if (!g_ascii_isalpha (loc[0]))
+ return FALSE;
+
+ while (loc != end) {
+ if (loc[0] == ':')
+ break;
+ if (!g_ascii_isalpha (loc[0]) &&
+ loc[0] != '+' && loc[0] != '-' && loc[0] != '.')
+ return FALSE;
+ loc++;
+ }
+
+ if (loc == uri)
+ return FALSE;
+
+ if (strncmp (loc, "://", 3) == 0) {
+ /* Include authority in base */
+ loc += 3;
+ loc = strchr (loc, '/');
+ if (!loc)
+ loc = end;
+ }
+
+ *base = g_strndup (uri, loc - uri);
+ *rel_path = loc + 1;
+
+ return TRUE;
+}
+
+GPtrArray *
+remove_dot_segments (gchar **uri_elems)
+{
+ GPtrArray *array;
+ gint i;
+
+ array = g_ptr_array_new ();
+
+ for (i = 0; uri_elems[i] != NULL; i++) {
+ if (g_strcmp0 (uri_elems[i], ".") == 0) {
+ continue;
+ } else if (g_strcmp0 (uri_elems[i], "..") == 0) {
+ if (array->len > 0)
+ g_ptr_array_remove_index (array, array->len - 1);
+ continue;
+ } else if (*uri_elems[i] != '\0') {
+ /* NB: Not a copy */
+ g_ptr_array_add (array, uri_elems[i]);
+ }
+ }
+
+ return array;
+}
+
+gchar *
+tracker_resolve_relative_uri (const gchar *base,
+ const gchar *rel_uri)
+{
+ gchar **base_split, **rel_split, *host;
+ GPtrArray *base_norm, *rel_norm;
+ GString *str;
+ gint i;
+
+ /* Relative IRIs are combined with base IRIs with a simplified version
+ * of the algorithm described at RFC3986, Section 5.2. We don't care
+ * about query and fragment parts of an URI, and some simplifications
+ * are taken on base uri parsing and relative uri validation.
+ */
+ rel_split = g_strsplit (rel_uri, "/", -1);
+
+ /* Rel uri is a full uri? */
+ if (strchr (rel_split[0], ':')) {
+ g_strfreev (rel_split);
+ return g_strdup (rel_uri);
+ }
+
+ if (!parse_abs_uri (base, &host, &base)) {
+ g_strfreev (rel_split);
+ return g_strdup (rel_uri);
+ }
+
+ base_split = g_strsplit (base, "/", -1);
+
+ base_norm = remove_dot_segments (base_split);
+ rel_norm = remove_dot_segments (rel_split);
+
+ for (i = 0; i < rel_norm->len; i++) {
+ g_ptr_array_add (base_norm,
+ g_ptr_array_index (rel_norm, i));
+ }
+
+ str = g_string_new (host);
+ for (i = 0; i < base_norm->len; i++) {
+ g_string_append_c (str, '/');
+ g_string_append (str,
+ g_ptr_array_index (base_norm, i));
+ }
+
+ g_ptr_array_unref (base_norm);
+ g_ptr_array_unref (rel_norm);
+ g_strfreev (base_split);
+ g_strfreev (rel_split);
+ g_free (host);
+
+ return g_string_free (str, FALSE);
+}
diff --git a/src/libtracker-common/tracker-utils.h b/src/libtracker-common/tracker-utils.h
index 2cb78e5ba..c12c9ccae 100644
--- a/src/libtracker-common/tracker-utils.h
+++ b/src/libtracker-common/tracker-utils.h
@@ -47,6 +47,8 @@ gchar * tracker_utf8_truncate (const gchar *str,
gsize max_size);
gchar * tracker_unescape_unichars (const gchar *str,
gssize len);
+gchar * tracker_resolve_relative_uri (const gchar *base,
+ const gchar *rel_uri);
G_END_DECLS
diff --git a/src/libtracker-control/meson.build b/src/libtracker-control/meson.build
index 7efc65168..49f51a084 100644
--- a/src/libtracker-control/meson.build
+++ b/src/libtracker-control/meson.build
@@ -42,12 +42,14 @@ gnome.generate_vapi(
install : true,
)
-configure_file(
- input: 'tracker-control.pc.in',
- output: 'tracker-control-@0@.pc'.format(tracker_api_version),
- configuration: conf,
- install: true,
- install_dir: join_paths(get_option('prefix'), get_option('libdir'), 'pkgconfig'))
+pkg.generate(libtracker_control,
+ description: 'A library to monitor/control tracker miners',
+ requires: [glib, gio],
+ subdirs: 'tracker-' + tracker_api_version,
+ variables: [
+ 'exec_prefix=${prefix}'
+ ],
+)
install_headers (headers,
subdir: 'tracker-@0@/libtracker-control'.format(tracker_api_version))
diff --git a/src/libtracker-control/tracker-control.pc.in b/src/libtracker-control/tracker-control.pc.in
deleted file mode 100644
index cb7529c03..000000000
--- a/src/libtracker-control/tracker-control.pc.in
+++ /dev/null
@@ -1,11 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-
-Name: tracker-control
-Description: A library to monitor/control tracker miners
-Version: @VERSION@
-Requires: glib-2.0 gio-2.0
-Libs: -L${libdir} -ltracker-control-@TRACKER_API_VERSION@
-Cflags: -I${includedir}/tracker-@TRACKER_API_VERSION@
diff --git a/src/libtracker-control/tracker-miner-manager.c b/src/libtracker-control/tracker-miner-manager.c
index 740eae30d..c68be8d03 100644
--- a/src/libtracker-control/tracker-miner-manager.c
+++ b/src/libtracker-control/tracker-miner-manager.c
@@ -820,9 +820,16 @@ static void
initialize_miners_data (TrackerMinerManager *manager)
{
GFile *file;
+ const gchar *miners_dir;
+
+ if (g_getenv("TRACKER_MINER_SERVICES_DIR") != NULL) {
+ miners_dir = g_getenv("TRACKER_MINER_SERVICES_DIR");
+ } else {
+ miners_dir = TRACKER_MINERS_DIR;
+ }
/* Go through service files */
- file = g_file_new_for_path (TRACKER_MINERS_DIR);
+ file = g_file_new_for_path (miners_dir);
directory_foreach (file, ".service", (GFunc) check_file, manager);
g_object_unref (file);
}
diff --git a/src/libtracker-data/libtracker-data.vapi b/src/libtracker-data/libtracker-data.vapi
index be1bcf118..5f0c98391 100644
--- a/src/libtracker-data/libtracker-data.vapi
+++ b/src/libtracker-data/libtracker-data.vapi
@@ -122,7 +122,6 @@ namespace Tracker {
public class Class : GLib.Object {
public string name { get; set; }
public string uri { get; set; }
- public int count { get; set; }
[CCode (array_length = false, array_null_terminated = true)]
public unowned Class[] get_super_classes ();
public void transact_events ();
@@ -192,11 +191,11 @@ namespace Tracker {
public void update_sparql (string update) throws Sparql.Error;
public GLib.Variant update_sparql_blank (string update) throws Sparql.Error;
public void load_turtle_file (GLib.File file) throws Sparql.Error;
- public void delete_statement (string? graph, string subject, string predicate, string object) throws Sparql.Error, DateError;
- public void update_statement (string? graph, string subject, string predicate, string? object) throws Sparql.Error, DateError;
- public void insert_statement (string? graph, string subject, string predicate, string object) throws Sparql.Error, DateError;
- public void insert_statement_with_uri (string? graph, string subject, string predicate, string object) throws Sparql.Error;
- public void insert_statement_with_string (string? graph, string subject, string predicate, string object) throws Sparql.Error, DateError;
+ public void delete_statement (string? graph, string subject, string predicate, GLib.Bytes object) throws Sparql.Error, DateError;
+ public void update_statement (string? graph, string subject, string predicate, GLib.Bytes? object) throws Sparql.Error, DateError;
+ public void insert_statement (string? graph, string subject, string predicate, GLib.Bytes object) throws Sparql.Error, DateError;
+ public void insert_statement_with_uri (string? graph, string subject, string predicate, GLib.Bytes object) throws Sparql.Error;
+ public void insert_statement_with_string (string? graph, string subject, string predicate, GLib.Bytes object) throws Sparql.Error, DateError;
public void update_buffer_flush () throws DBInterfaceError;
public void update_buffer_might_flush () throws DBInterfaceError;
public void sync ();
diff --git a/src/libtracker-data/meson.build b/src/libtracker-data/meson.build
index 778c9d541..4f40c1fcc 100644
--- a/src/libtracker-data/meson.build
+++ b/src/libtracker-data/meson.build
@@ -47,7 +47,6 @@ libtracker_data = library('tracker-data',
'tracker-db-interface.c',
'tracker-db-interface-sqlite.c',
'tracker-db-manager.c',
- 'tracker-db-journal.c',
'tracker-db-backup.c',
'tracker-namespace.c',
'tracker-ontology.c',
@@ -58,6 +57,7 @@ libtracker_data = library('tracker-data',
'tracker-sparql-types.c',
'tracker-sparql.c',
'tracker-uuid.c',
+ 'tracker-vtab-service.c',
'tracker-vtab-triples.c',
tracker_common_enum_header,
tracker_data_enums[0],
diff --git a/src/libtracker-data/tracker-class.c b/src/libtracker-data/tracker-class.c
index 76d5416c9..47bf8439f 100644
--- a/src/libtracker-data/tracker-class.c
+++ b/src/libtracker-data/tracker-class.c
@@ -34,7 +34,6 @@ typedef struct _TrackerClassPrivate TrackerClassPrivate;
struct _TrackerClassPrivate {
gchar *uri;
gchar *name;
- gint count;
gint id;
gboolean is_new;
gboolean db_schema_changed;
@@ -141,18 +140,6 @@ tracker_class_get_name (TrackerClass *service)
}
gint
-tracker_class_get_count (TrackerClass *service)
-{
- TrackerClassPrivate *priv;
-
- g_return_val_if_fail (TRACKER_IS_CLASS (service), 0);
-
- priv = tracker_class_get_instance_private (service);
-
- return priv->count;
-}
-
-gint
tracker_class_get_id (TrackerClass *service)
{
TrackerClassPrivate *priv;
@@ -313,20 +300,6 @@ tracker_class_set_uri (TrackerClass *service,
}
void
-tracker_class_set_count (TrackerClass *service,
- gint value)
-{
- TrackerClassPrivate *priv;
-
- g_return_if_fail (TRACKER_IS_CLASS (service));
-
- priv = tracker_class_get_instance_private (service);
-
- priv->count = value;
-}
-
-
-void
tracker_class_set_id (TrackerClass *service,
gint value)
{
diff --git a/src/libtracker-data/tracker-class.h b/src/libtracker-data/tracker-class.h
index 1c01e8914..004c6660c 100644
--- a/src/libtracker-data/tracker-class.h
+++ b/src/libtracker-data/tracker-class.h
@@ -53,7 +53,6 @@ GType tracker_class_get_type (void) G_GNUC_CONST;
TrackerClass * tracker_class_new (gboolean use_gvdb);
const gchar * tracker_class_get_uri (TrackerClass *service);
const gchar * tracker_class_get_name (TrackerClass *service);
-gint tracker_class_get_count (TrackerClass *service);
gint tracker_class_get_id (TrackerClass *service);
gboolean tracker_class_get_is_new (TrackerClass *service);
gboolean tracker_class_get_db_schema_changed (TrackerClass *service);
@@ -66,8 +65,6 @@ TrackerClass **tracker_class_get_last_super_classes (TrackerClass *ser
void tracker_class_set_uri (TrackerClass *service,
const gchar *value);
-void tracker_class_set_count (TrackerClass *service,
- gint value);
void tracker_class_add_super_class (TrackerClass *service,
TrackerClass *value);
void tracker_class_add_domain_index (TrackerClass *service,
diff --git a/src/libtracker-data/tracker-data-backup.c b/src/libtracker-data/tracker-data-backup.c
index b68ab4004..3ae82962b 100644
--- a/src/libtracker-data/tracker-data-backup.c
+++ b/src/libtracker-data/tracker-data-backup.c
@@ -29,7 +29,6 @@
#include "tracker-data-backup.h"
#include "tracker-data-manager.h"
#include "tracker-db-manager.h"
-#include "tracker-db-journal.h"
#include "tracker-db-backup.h"
typedef struct {
@@ -40,21 +39,6 @@ typedef struct {
GError *error;
} BackupSaveInfo;
-#ifndef DISABLE_JOURNAL
-
-typedef struct {
- GPid pid;
- guint stdout_watch_id;
- guint stderr_watch_id;
- GIOChannel *stdin_channel;
- GIOChannel *stdout_channel;
- GIOChannel *stderr_channel;
- gpointer data;
- GString *lines;
-} ProcessContext;
-
-#endif /* DISABLE_JOURNAL */
-
static void
free_backup_save_info (BackupSaveInfo *info)
{
@@ -82,151 +66,6 @@ tracker_data_backup_error_quark (void)
return g_quark_from_static_string (TRACKER_DATA_BACKUP_ERROR_DOMAIN);
}
-#ifndef DISABLE_JOURNAL
-
-static void
-on_journal_copied (BackupSaveInfo *info, GError *error)
-{
- if (info->callback) {
- info->callback (error, info->user_data);
- }
-
- free_backup_save_info (info);
-}
-
-
-
-static void
-process_context_destroy (ProcessContext *context, GError *error)
-{
- on_journal_copied (context->data, error);
-
- if (context->lines) {
- g_string_free (context->lines, TRUE);
- }
-
- if (context->stdin_channel) {
- g_io_channel_shutdown (context->stdin_channel, FALSE, NULL);
- g_io_channel_unref (context->stdin_channel);
- context->stdin_channel = NULL;
- }
-
- if (context->stdout_watch_id != 0) {
- g_source_remove (context->stdout_watch_id);
- context->stdout_watch_id = 0;
- }
-
- if (context->stdout_channel) {
- g_io_channel_shutdown (context->stdout_channel, FALSE, NULL);
- g_io_channel_unref (context->stdout_channel);
- context->stdout_channel = NULL;
- }
-
- if (context->stderr_watch_id != 0) {
- g_source_remove (context->stderr_watch_id);
- context->stderr_watch_id = 0;
- }
-
- if (context->stderr_channel) {
- g_io_channel_shutdown (context->stderr_channel, FALSE, NULL);
- g_io_channel_unref (context->stderr_channel);
- context->stderr_channel = NULL;
- }
-
- if (context->pid != 0) {
- g_spawn_close_pid (context->pid);
- context->pid = 0;
- }
-
- g_free (context);
-}
-
-static gboolean
-read_line_of_tar_output (GIOChannel *channel,
- GIOCondition condition,
- gpointer user_data)
-{
-
- if (condition & G_IO_ERR || condition & G_IO_HUP) {
- ProcessContext *context = user_data;
-
- context->stdout_watch_id = 0;
- return FALSE;
- }
-
- /* TODO: progress support */
- return TRUE;
-}
-
-static gboolean
-read_error_of_tar_output (GIOChannel *channel,
- GIOCondition condition,
- gpointer user_data)
-{
- ProcessContext *context;
- GIOStatus status;
- gchar *line;
-
- context = user_data;
- status = G_IO_STATUS_NORMAL;
-
- if (condition & G_IO_IN || condition & G_IO_PRI) {
- do {
- GError *error = NULL;
-
- status = g_io_channel_read_line (channel, &line, NULL, NULL, &error);
-
- if (status == G_IO_STATUS_NORMAL) {
- if (context->lines == NULL)
- context->lines = g_string_new (NULL);
- g_string_append (context->lines, line);
- g_free (line);
- } else if (error) {
- g_warning ("%s", error->message);
- g_error_free (error);
- }
- } while (status == G_IO_STATUS_NORMAL);
-
- if (status == G_IO_STATUS_EOF ||
- status == G_IO_STATUS_ERROR) {
- context->stderr_watch_id = 0;
- return FALSE;
- }
- }
-
- if (condition & G_IO_ERR || condition & G_IO_HUP) {
- context->stderr_watch_id = 0;
- return FALSE;
- }
-
- return TRUE;
-}
-
-static void
-process_context_child_watch_cb (GPid pid,
- gint status,
- gpointer user_data)
-{
- ProcessContext *context;
- GError *error = NULL;
-
- g_debug ("Process '%d' exited with code %d", pid, status);
-
- context = (ProcessContext *) user_data;
-
- if (context->lines) {
- g_set_error (&error, TRACKER_DATA_BACKUP_ERROR,
- TRACKER_DATA_BACKUP_ERROR_UNKNOWN,
- "%s", context->lines->str);
- }
-
- process_context_destroy (context, error);
-}
-#endif /* DISABLE_JOURNAL */
-
-
-
-#ifdef DISABLE_JOURNAL
static void
on_backup_finished (GError *error,
gpointer user_data)
@@ -240,8 +79,6 @@ on_backup_finished (GError *error,
free_backup_save_info (info);
}
-#endif /* DISABLE_JOURNAL */
-
/* delete all regular files from the directory */
static void
dir_remove_files (const gchar *path)
@@ -328,23 +165,6 @@ dir_move_to_temp (const gchar *path,
}
static void
-dir_move_from_temp (const gchar *path,
- const gchar *tmpname)
-{
- gchar *temp_dir;
-
- temp_dir = g_build_filename (path, tmpname, NULL);
-
- /* ensure that no obsolete files are around */
- dir_remove_files (path);
- dir_move_files (temp_dir, path);
-
- g_rmdir (temp_dir);
-
- g_free (temp_dir);
-}
-
-static void
move_to_temp (GFile *cache_location,
GFile *data_location)
{
@@ -362,51 +182,6 @@ move_to_temp (GFile *cache_location,
g_free (data_dir);
}
-static void
-remove_temp (GFile *cache_location,
- GFile *data_location)
-{
- gchar *tmp_data_dir, *tmp_cache_dir;
- GFile *child;
-
- g_info ("Removing all database files from temporary location");
-
- child = g_file_get_child (data_location, "tmp.data");
- tmp_data_dir = g_file_get_path (child);
- g_object_unref (child);
-
- child = g_file_get_child (cache_location, "tmp.cache");
- tmp_cache_dir = g_file_get_path (child);
- g_object_unref (child);
-
- dir_remove_files (tmp_data_dir);
- dir_remove_files (tmp_cache_dir);
-
- g_rmdir (tmp_data_dir);
- g_rmdir (tmp_cache_dir);
-
- g_free (tmp_cache_dir);
- g_free (tmp_data_dir);
-}
-
-static void
-restore_from_temp (GFile *cache_location,
- GFile *data_location)
-{
- gchar *data_dir, *cache_dir;
-
- g_info ("Restoring all database files from temporary location");
-
- data_dir = g_file_get_path (data_location);
- cache_dir = g_file_get_path (cache_location);
-
- dir_move_from_temp (data_dir, "tmp.data");
- dir_move_from_temp (cache_dir, "tmp.cache");
-
- g_free (cache_dir);
- g_free (data_dir);
-}
-
void
tracker_data_backup_save (TrackerDataManager *data_manager,
GFile *destination,
@@ -415,118 +190,6 @@ tracker_data_backup_save (TrackerDataManager *data_manager,
gpointer user_data,
GDestroyNotify destroy)
{
-#ifndef DISABLE_JOURNAL
- BackupSaveInfo *info;
- ProcessContext *context;
- gchar **argv;
- gchar *path, *directory;
- GError *local_error = NULL;
- GDir *journal_dir;
- GPid pid;
- GPtrArray *files;
- const gchar *f_name;
- gboolean result;
- gint stdin_fd, stdout_fd, stderr_fd;
- guint i;
-
- info = g_new0 (BackupSaveInfo, 1);
- info->destination = g_object_ref (destination);
- info->callback = callback;
- info->user_data = user_data;
- info->destroy = destroy;
-
- path = g_file_get_path (destination);
-
- directory = g_file_get_path (data_location);
- journal_dir = g_dir_open (directory, 0, NULL);
- f_name = g_dir_read_name (journal_dir);
- files = g_ptr_array_new ();
-
- while (f_name) {
- if (f_name) {
- if (!g_str_has_prefix (f_name, TRACKER_DB_JOURNAL_FILENAME ".")) {
- f_name = g_dir_read_name (journal_dir);
- continue;
- }
- g_ptr_array_add (files, g_strdup (f_name));
- }
- f_name = g_dir_read_name (journal_dir);
- }
-
- g_dir_close (journal_dir);
-
- argv = g_new0 (gchar*, files->len + 8);
-
- argv[0] = g_strdup ("tar");
- argv[1] = g_strdup ("-zcf");
- argv[2] = path;
- argv[3] = g_strdup ("-C");
- argv[4] = directory;
- argv[5] = g_strdup (TRACKER_DB_JOURNAL_FILENAME);
- argv[6] = g_strdup (TRACKER_DB_JOURNAL_ONTOLOGY_FILENAME);
-
- for (i = 0; i < files->len; i++) {
- argv[i+7] = g_ptr_array_index (files, i);
- }
-
- /* It's fine to untar this asynchronous: the journal replay code can or
- * should cope with unfinished entries at the end of the file, while
- * restoring a backup made this way. */
- result = g_spawn_async_with_pipes (NULL, /* working dir */
- (gchar **) argv,
- NULL, /* env */
- G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
- NULL, /* func to call before exec() */
- 0,
- &pid,
- &stdin_fd,
- &stdout_fd,
- &stderr_fd,
- &local_error);
-
- if (!result || local_error) {
- GError *error = NULL;
-
- g_set_error (&error,
- TRACKER_DATA_BACKUP_ERROR,
- TRACKER_DATA_BACKUP_ERROR_UNKNOWN,
- "%s, %s",
- _("Error starting “tar” program"),
- local_error ? local_error->message : _("No error given"));
-
- g_warning ("%s", error->message);
-
- on_journal_copied (info, error);
-
- g_strfreev (argv);
- g_clear_error (&local_error);
-
- return;
- }
-
- context = g_new0 (ProcessContext, 1);
- context->lines = NULL;
- context->data = info;
- context->pid = pid;
- context->stdin_channel = g_io_channel_unix_new (stdin_fd);
- context->stdout_channel = g_io_channel_unix_new (stdout_fd);
- context->stderr_channel = g_io_channel_unix_new (stderr_fd);
- context->stdout_watch_id = g_io_add_watch (context->stdout_channel,
- G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
- read_line_of_tar_output,
- context);
- context->stderr_watch_id = g_io_add_watch (context->stderr_channel,
- G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
- read_error_of_tar_output,
- context);
-
- g_child_watch_add (context->pid, process_context_child_watch_cb, context);
-
- g_debug ("Process '%d' spawned for command:'%s %s %s'",
- pid, argv[0], argv[1], argv[2]);
-
- g_strfreev (argv);
-#else
BackupSaveInfo *info;
TrackerDBManager *db_manager;
GFile *db_file;
@@ -546,7 +209,6 @@ tracker_data_backup_save (TrackerDataManager *data_manager,
NULL);
g_object_unref (db_file);
-#endif /* DISABLE_JOURNAL */
}
void
@@ -573,27 +235,13 @@ tracker_data_backup_restore (TrackerDataManager *manager,
db_manager = tracker_data_manager_get_db_manager (manager);
info = g_new0 (BackupSaveInfo, 1);
-#ifndef DISABLE_JOURNAL
- info->destination = g_file_get_child (data_location, TRACKER_DB_JOURNAL_FILENAME);
-#else
info->destination = g_file_new_for_path (tracker_db_manager_get_file (db_manager));
-#endif /* DISABLE_JOURNAL */
info->journal = g_object_ref (journal);
if (g_file_query_exists (info->journal, NULL)) {
TrackerDBManagerFlags flags;
- TrackerDBJournal *journal_writer;
guint select_cache_size, update_cache_size;
-#ifndef DISABLE_JOURNAL
- GError *n_error = NULL;
- GFile *parent = g_file_get_parent (info->destination);
- gchar *tmp_stdout = NULL;
- gchar *tmp_stderr = NULL;
- gchar **argv;
- gboolean result;
- gint exit_status;
-#endif /* DISABLE_JOURNAL */
flags = tracker_db_manager_get_flags (db_manager, &select_cache_size, &update_cache_size);
@@ -601,58 +249,6 @@ tracker_data_backup_restore (TrackerDataManager *manager,
move_to_temp (cache_location, data_location);
-#ifndef DISABLE_JOURNAL
- argv = g_new0 (char*, 6);
-
- argv[0] = g_strdup ("tar");
- argv[1] = g_strdup ("-zxf");
- argv[2] = g_file_get_path (info->journal);
- argv[3] = g_strdup ("-C");
- argv[4] = g_file_get_path (parent);
-
- g_object_unref (parent);
-
- /* Synchronous: we don't want the mainloop to run while copying the
- * journal, as nobody should be writing anything at this point
- */
- result = g_spawn_sync (NULL, /* working dir */
- argv,
- NULL, /* env */
- G_SPAWN_SEARCH_PATH,
- NULL,
- 0, /* timeout */
- &tmp_stdout,
- &tmp_stderr,
- &exit_status,
- &n_error);
-
- if (!result || n_error) {
- g_set_error (&info->error,
- TRACKER_DATA_BACKUP_ERROR,
- TRACKER_DATA_BACKUP_ERROR_UNKNOWN,
- "%s, %s",
- _("Error starting “tar” program"),
- n_error ? n_error->message : _("No error given"));
- g_warning ("%s", info->error->message);
- g_clear_error (&n_error);
- } else if (tmp_stderr && strlen (tmp_stderr) > 0) {
- g_set_error (&info->error,
- TRACKER_DATA_BACKUP_ERROR,
- TRACKER_DATA_BACKUP_ERROR_UNKNOWN,
- "%s",
- tmp_stderr);
- } else if (exit_status != 0) {
- g_set_error (&info->error,
- TRACKER_DATA_BACKUP_ERROR,
- TRACKER_DATA_BACKUP_ERROR_UNKNOWN,
- _("Unknown error, “tar” exited with status %d"),
- exit_status);
- }
-
- g_free (tmp_stderr);
- g_free (tmp_stdout);
- g_strfreev (argv);
-#else
/* Turn off force-reindex here, no journal to replay so it wouldn't work */
flags &= ~TRACKER_DB_MANAGER_FORCE_REINDEX;
@@ -660,61 +256,15 @@ tracker_data_backup_restore (TrackerDataManager *manager,
G_FILE_COPY_OVERWRITE,
NULL, NULL, NULL,
&info->error);
-#endif /* DISABLE_JOURNAL */
tracker_db_manager_ensure_locations (db_manager, cache_location, data_location);
- /* Re-set the DB version file, so that its mtime changes. The mtime of this
- * file will change only when the whole DB is recreated (after a hard reset
- * or after a backup restoration). */
- tracker_db_manager_create_version_file (db_manager);
-
-#ifndef DISABLE_JOURNAL
- journal_writer = tracker_db_journal_new (data_location, FALSE, &n_error);
-
- if (n_error) {
- if (!info->error) {
- g_propagate_error (&info->error, n_error);
- } else {
- g_warning ("Ignored error while initializing journal during backup (another higher priority error already took place): %s",
- n_error->message ? n_error->message : "No error given");
- g_error_free (n_error);
- }
- n_error = NULL;
- }
-
- if (info->error) {
- restore_from_temp (cache_location, data_location);
- } else {
- remove_temp (cache_location, data_location);
- }
-
- tracker_db_journal_free (journal_writer, &n_error);
-
- if (n_error) {
- g_warning ("Ignored error while shuting down journal during backup: %s",
- n_error->message ? n_error->message : "No error given");
- g_error_free (n_error);
- }
-#endif /* DISABLE_JOURNAL */
+ tracker_db_manager_update_version (db_manager);
manager = tracker_data_manager_new (flags, cache_location, data_location, ontology_location,
- TRUE, TRUE, select_cache_size, update_cache_size);
+ TRUE, select_cache_size, update_cache_size);
g_initable_init (G_INITABLE (manager), NULL, &internal_error);
-#ifdef DISABLE_JOURNAL
- if (internal_error) {
- restore_from_temp (cache_location, data_location);
- g_object_unref (manager);
-
- manager = tracker_data_manager_new (flags, cache_location, data_location, ontology_location,
- TRUE, TRUE, select_cache_size, update_cache_size);
- g_initable_init (G_INITABLE (manager), NULL, &internal_error);
- } else {
- remove_temp (cache_location, data_location);
- }
-#endif /* DISABLE_JOURNAL */
-
if (internal_error) {
g_propagate_error (error, internal_error);
}
diff --git a/src/libtracker-data/tracker-data-manager.c b/src/libtracker-data/tracker-data-manager.c
index dadb0f585..fbd43d86c 100644
--- a/src/libtracker-data/tracker-data-manager.c
+++ b/src/libtracker-data/tracker-data-manager.c
@@ -41,7 +41,6 @@
#include "tracker-data-update.h"
#include "tracker-db-interface-sqlite.h"
#include "tracker-db-manager.h"
-#include "tracker-db-journal.h"
#include "tracker-namespace.h"
#include "tracker-ontologies.h"
#include "tracker-ontology.h"
@@ -73,24 +72,20 @@ struct _TrackerDataManager {
GFile *cache_location;
GFile *data_location;
guint initialized : 1;
- guint journal_check : 1;
guint restoring_backup : 1;
guint first_time_index : 1;
guint flags;
gint select_cache_size;
gint update_cache_size;
-
-#ifndef DISABLE_JOURNAL
- gboolean in_journal_replay;
- TrackerDBJournal *journal_writer;
- TrackerDBJournal *ontology_writer;
-#endif
+ guint generation;
TrackerDBManager *db_manager;
TrackerOntologies *ontologies;
TrackerData *data_update;
+ GHashTable *graphs;
+
gchar *status;
};
@@ -139,7 +134,8 @@ enum {
#if HAVE_TRACKER_FTS
static gboolean tracker_data_manager_fts_changed (TrackerDataManager *manager);
static void tracker_data_manager_update_fts (TrackerDataManager *manager,
- TrackerDBInterface *iface);
+ TrackerDBInterface *iface,
+ const gchar *database);
#endif
static void tracker_data_manager_initable_iface_init (GInitableIface *iface);
@@ -150,6 +146,7 @@ G_DEFINE_TYPE_WITH_CODE (TrackerDataManager, tracker_data_manager, G_TYPE_OBJECT
static void
tracker_data_manager_init (TrackerDataManager *manager)
{
+ manager->generation = 1;
}
GQuark
@@ -158,6 +155,60 @@ tracker_data_ontology_error_quark (void)
return g_quark_from_static_string ("tracker-data-ontology-error-quark");
}
+static GHashTable *
+tracker_data_manager_ensure_graphs (TrackerDataManager *manager,
+ TrackerDBInterface *iface,
+ GError **error)
+{
+ TrackerDBCursor *cursor = NULL;
+ TrackerDBStatement *stmt;
+ GHashTable *graphs;
+
+ if (manager->graphs)
+ return manager->graphs;
+
+ graphs = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+
+ stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE, error,
+ "SELECT ID, Uri FROM Resource WHERE ID IN (SELECT ID FROM Graph)");
+ if (!stmt) {
+ g_hash_table_unref (graphs);
+ return FALSE;
+ }
+
+ cursor = tracker_db_statement_start_cursor (stmt, error);
+ g_object_unref (stmt);
+
+ if (!cursor) {
+ g_hash_table_unref (graphs);
+ return NULL;
+ }
+
+ while (tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
+ const gchar *name;
+ gint id;
+
+ id = tracker_db_cursor_get_int (cursor, 0);
+ name = tracker_db_cursor_get_string (cursor, 1, NULL);
+
+ g_hash_table_insert (graphs, g_strdup (name),
+ GINT_TO_POINTER (id));
+ }
+
+ g_object_unref (cursor);
+ manager->graphs = graphs;
+ return graphs;
+}
+
+GHashTable *
+tracker_data_manager_get_graphs (TrackerDataManager *manager)
+{
+ return manager->graphs;
+}
+
static void
handle_unsupported_ontology_change (TrackerDataManager *manager,
const gchar *ontology_path,
@@ -167,11 +218,6 @@ handle_unsupported_ontology_change (TrackerDataManager *manager,
const gchar *attempted_new,
GError **error)
{
-#ifndef DISABLE_JOURNAL
- /* force reindex on restart */
- tracker_db_manager_remove_version_file (manager->db_manager);
-#endif /* DISABLE_JOURNAL */
-
g_set_error (error, TRACKER_DATA_ONTOLOGY_ERROR,
TRACKER_DATA_UNSUPPORTED_ONTOLOGY_CHANGE,
"%s: Unsupported ontology change for %s: can't change %s (old=%s, attempted new=%s)",
@@ -184,6 +230,7 @@ handle_unsupported_ontology_change (TrackerDataManager *manager,
static void
set_secondary_index_for_single_value_property (TrackerDBInterface *iface,
+ const gchar *database,
const gchar *service_name,
const gchar *field_name,
const gchar *second_field_name,
@@ -197,7 +244,8 @@ set_secondary_index_for_single_value_property (TrackerDBInterface *iface,
service_name, field_name);
tracker_db_interface_execute_query (iface, &internal_error,
- "DROP INDEX IF EXISTS \"%s_%s\"",
+ "DROP INDEX IF EXISTS \"%s\".\"%s_%s\"",
+ database,
service_name,
field_name);
@@ -212,7 +260,8 @@ set_secondary_index_for_single_value_property (TrackerDBInterface *iface,
service_name, field_name, service_name, field_name, second_field_name);
tracker_db_interface_execute_query (iface, &internal_error,
- "CREATE INDEX \"%s_%s\" ON \"%s\" (\"%s\", \"%s\")",
+ "CREATE INDEX \"%s\".\"%s_%s\" ON \"%s\" (\"%s\", \"%s\")",
+ database,
service_name,
field_name,
service_name,
@@ -227,9 +276,11 @@ set_secondary_index_for_single_value_property (TrackerDBInterface *iface,
static void
set_index_for_single_value_property (TrackerDBInterface *iface,
+ const gchar *database,
const gchar *service_name,
const gchar *field_name,
gboolean enabled,
+ gboolean datetime,
GError **error)
{
GError *internal_error = NULL;
@@ -239,7 +290,8 @@ set_index_for_single_value_property (TrackerDBInterface *iface,
service_name, field_name);
tracker_db_interface_execute_query (iface, &internal_error,
- "DROP INDEX IF EXISTS \"%s_%s\"",
+ "DROP INDEX IF EXISTS \"%s\".\"%s_%s\"",
+ database,
service_name,
field_name);
@@ -249,16 +301,25 @@ set_index_for_single_value_property (TrackerDBInterface *iface,
}
if (enabled) {
+ gchar *expr;
+
+ if (datetime)
+ expr = g_strdup_printf ("SparqlTimeSort(\"%s\")", field_name);
+ else
+ expr = g_strdup_printf ("\"%s\"", field_name);
+
g_debug ("Creating index (single-value property): "
- "CREATE INDEX \"%s_%s\" ON \"%s\" (\"%s\")",
- service_name, field_name, service_name, field_name);
+ "CREATE INDEX \"%s_%s\" ON \"%s\" (%s)",
+ service_name, field_name, service_name, expr);
tracker_db_interface_execute_query (iface, &internal_error,
- "CREATE INDEX \"%s_%s\" ON \"%s\" (\"%s\")",
+ "CREATE INDEX \"%s\".\"%s_%s\" ON \"%s\" (%s)",
+ database,
service_name,
field_name,
service_name,
- field_name);
+ expr);
+ g_free (expr);
if (internal_error) {
g_propagate_error (error, internal_error);
@@ -268,20 +329,24 @@ set_index_for_single_value_property (TrackerDBInterface *iface,
static void
set_index_for_multi_value_property (TrackerDBInterface *iface,
+ const gchar *database,
const gchar *service_name,
const gchar *field_name,
gboolean enabled,
gboolean recreate,
+ gboolean datetime,
GError **error)
{
GError *internal_error = NULL;
+ gchar *expr;
g_debug ("Dropping index (multi-value property): "
"DROP INDEX IF EXISTS \"%s_%s_ID_ID\"",
service_name, field_name);
tracker_db_interface_execute_query (iface, &internal_error,
- "DROP INDEX IF EXISTS \"%s_%s_ID_ID\"",
+ "DROP INDEX IF EXISTS \"%s\".\"%s_%s_ID_ID\"",
+ database,
service_name,
field_name);
@@ -298,7 +363,8 @@ set_index_for_multi_value_property (TrackerDBInterface *iface,
service_name,
field_name);
tracker_db_interface_execute_query (iface, &internal_error,
- "DROP INDEX IF EXISTS \"%s_%s_ID\"",
+ "DROP INDEX IF EXISTS \"%s\".\"%s_%s_ID\"",
+ database,
service_name,
field_name);
@@ -311,6 +377,11 @@ set_index_for_multi_value_property (TrackerDBInterface *iface,
return;
}
+ if (datetime)
+ expr = g_strdup_printf ("SparqlTimeSort(\"%s\")", field_name);
+ else
+ expr = g_strdup_printf ("\"%s\"", field_name);
+
if (enabled) {
g_debug ("Creating index (multi-value property): "
"CREATE INDEX \"%s_%s_ID\" ON \"%s_%s\" (ID)",
@@ -320,7 +391,8 @@ set_index_for_multi_value_property (TrackerDBInterface *iface,
field_name);
tracker_db_interface_execute_query (iface, &internal_error,
- "CREATE INDEX \"%s_%s_ID\" ON \"%s_%s\" (ID)",
+ "CREATE INDEX \"%s\".\"%s_%s_ID\" ON \"%s_%s\" (ID)",
+ database,
service_name,
field_name,
service_name,
@@ -332,20 +404,21 @@ set_index_for_multi_value_property (TrackerDBInterface *iface,
}
g_debug ("Creating index (multi-value property): "
- "CREATE UNIQUE INDEX \"%s_%s_ID_ID\" ON \"%s_%s\" (\"%s\", ID)",
+ "CREATE UNIQUE INDEX \"%s_%s_ID_ID\" ON \"%s_%s\" (%s, ID)",
service_name,
field_name,
service_name,
field_name,
- field_name);
+ expr);
tracker_db_interface_execute_query (iface, &internal_error,
- "CREATE UNIQUE INDEX \"%s_%s_ID_ID\" ON \"%s_%s\" (\"%s\", ID)",
+ "CREATE UNIQUE INDEX \"%s\".\"%s_%s_ID_ID\" ON \"%s_%s\" (%s, ID)",
+ database,
service_name,
field_name,
service_name,
field_name,
- field_name);
+ expr);
if (internal_error) {
g_propagate_error (error, internal_error);
@@ -353,25 +426,28 @@ set_index_for_multi_value_property (TrackerDBInterface *iface,
}
} else {
g_debug ("Creating index (multi-value property): "
- "CREATE UNIQUE INDEX \"%s_%s_ID_ID\" ON \"%s_%s\" (ID, \"%s\")",
+ "CREATE UNIQUE INDEX \"%s_%s_ID_ID\" ON \"%s_%s\" (ID, %s)",
service_name,
field_name,
service_name,
field_name,
- field_name);
+ expr);
tracker_db_interface_execute_query (iface, &internal_error,
- "CREATE UNIQUE INDEX \"%s_%s_ID_ID\" ON \"%s_%s\" (ID, \"%s\")",
+ "CREATE UNIQUE INDEX \"%s\".\"%s_%s_ID_ID\" ON \"%s_%s\" (ID, %s)",
+ database,
service_name,
field_name,
service_name,
field_name,
- field_name);
+ expr);
if (internal_error) {
g_propagate_error (error, internal_error);
}
}
+
+ g_free (expr);
}
static gboolean
@@ -393,53 +469,6 @@ is_allowed_conversion (const gchar *oldv,
}
static gboolean
-check_unsupported_property_value_change (TrackerDataManager *manager,
- const gchar *ontology_path,
- const gchar *kind,
- const gchar *subject,
- const gchar *predicate,
- const gchar *object)
-{
- GError *error = NULL;
- gboolean needed = TRUE;
- gchar *query = NULL;
- TrackerDBCursor *cursor;
-
- query = g_strdup_printf ("SELECT ?old_value WHERE { "
- "<%s> %s ?old_value "
- "}", subject, kind);
-
- cursor = tracker_data_query_sparql_cursor (manager, query, &error);
-
- if (cursor && tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
- if (g_strcmp0 (object, tracker_db_cursor_get_string (cursor, 0, NULL)) == 0) {
- needed = FALSE;
- } else {
- needed = TRUE;
- }
-
- } else {
- if (object && (g_strcmp0 (object, "false") == 0)) {
- needed = FALSE;
- } else {
- needed = (object != NULL);
- }
- }
-
- g_free (query);
- if (cursor) {
- g_object_unref (cursor);
- }
-
- if (error) {
- g_critical ("Ontology change, %s", error->message);
- g_clear_error (&error);
- }
-
- return needed;
-}
-
-static gboolean
update_property_value (TrackerDataManager *manager,
const gchar *ontology_path,
const gchar *kind,
@@ -454,6 +483,7 @@ update_property_value (TrackerDataManager *manager,
GError *error = NULL;
gboolean needed = TRUE;
gboolean is_new = FALSE;
+ GBytes *bytes;
if (class) {
is_new = tracker_class_get_is_new (class);
@@ -494,7 +524,10 @@ update_property_value (TrackerDataManager *manager,
}
if (!unsup_onto_err) {
- tracker_data_delete_statement (manager->data_update, NULL, subject, predicate, str, &error);
+ bytes = g_bytes_new (str, strlen (str) + 1);
+ tracker_data_delete_statement (manager->data_update, NULL, subject, predicate, bytes, &error);
+ g_bytes_unref (bytes);
+
if (!error)
tracker_data_update_buffer_flush (manager->data_update, &error);
}
@@ -517,9 +550,12 @@ update_property_value (TrackerDataManager *manager,
if (!error && needed && object) {
+ bytes = g_bytes_new (object, strlen (object) + 1);
tracker_data_insert_statement (manager->data_update, NULL, subject,
- predicate, object,
+ predicate, bytes,
&error);
+ g_bytes_unref (bytes);
+
if (!error)
tracker_data_update_buffer_flush (manager->data_update, &error);
}
@@ -575,27 +611,31 @@ check_range_conversion_is_allowed (TrackerDataManager *manager,
}
static void
-fix_indexed (TrackerDataManager *manager,
- TrackerProperty *property,
- gboolean recreate,
- GError **error)
+fix_indexed_on_db (TrackerDataManager *manager,
+ const gchar *database,
+ TrackerProperty *property,
+ gboolean recreate,
+ GError **error)
{
GError *internal_error = NULL;
TrackerDBInterface *iface;
TrackerClass *class;
const gchar *service_name;
const gchar *field_name;
+ gboolean datetime;
iface = tracker_db_manager_get_writable_db_interface (manager->db_manager);
class = tracker_property_get_domain (property);
field_name = tracker_property_get_name (property);
service_name = tracker_class_get_name (class);
+ datetime = tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME;
if (tracker_property_get_multiple_values (property)) {
- set_index_for_multi_value_property (iface, service_name, field_name,
+ set_index_for_multi_value_property (iface, database, service_name, field_name,
tracker_property_get_indexed (property),
recreate,
+ datetime,
&internal_error);
} else {
TrackerProperty *secondary_index;
@@ -603,11 +643,12 @@ fix_indexed (TrackerDataManager *manager,
secondary_index = tracker_property_get_secondary_index (property);
if (secondary_index == NULL) {
- set_index_for_single_value_property (iface, service_name, field_name,
+ set_index_for_single_value_property (iface, database, service_name, field_name,
recreate && tracker_property_get_indexed (property),
+ datetime,
&internal_error);
} else {
- set_secondary_index_for_single_value_property (iface, service_name, field_name,
+ set_secondary_index_for_single_value_property (iface, database, service_name, field_name,
tracker_property_get_name (secondary_index),
recreate && tracker_property_get_indexed (property),
&internal_error);
@@ -617,9 +658,11 @@ fix_indexed (TrackerDataManager *manager,
domain_index_classes = tracker_property_get_domain_indexes (property);
while (!internal_error && domain_index_classes && *domain_index_classes) {
set_index_for_single_value_property (iface,
+ database,
tracker_class_get_name (*domain_index_classes),
field_name,
recreate,
+ datetime,
&internal_error);
domain_index_classes++;
}
@@ -630,6 +673,36 @@ fix_indexed (TrackerDataManager *manager,
}
}
+static void
+fix_indexed (TrackerDataManager *manager,
+ TrackerProperty *property,
+ gboolean recreate,
+ GError **error)
+{
+ TrackerDBInterface *iface;
+ GHashTable *graphs;
+ GHashTableIter iter;
+ GError *internal_error = NULL;
+ gpointer value;
+
+ iface = tracker_data_manager_get_writable_db_interface (manager);
+ graphs = tracker_data_manager_ensure_graphs (manager, iface, &internal_error);
+ if (internal_error) {
+ g_propagate_error (error, internal_error);
+ return;
+ }
+
+ g_hash_table_iter_init (&iter, graphs);
+
+ while (g_hash_table_iter_next (&iter, &value, NULL)) {
+ fix_indexed_on_db (manager, value, property, recreate,
+ &internal_error);
+ if (internal_error) {
+ g_propagate_error (error, internal_error);
+ break;
+ }
+ }
+}
static void
tracker_data_ontology_load_statement (TrackerDataManager *manager,
@@ -790,7 +863,7 @@ tracker_data_ontology_load_statement (TrackerDataManager *manager,
is_new = tracker_class_get_is_new (class);
if (is_new != in_update) {
gboolean ignore = FALSE;
- /* Detect unsupported ontology change (this needs a journal replay) */
+ /* Detect unsupported ontology change */
if (in_update == TRUE && is_new == FALSE && g_strcmp0 (object, TRACKER_PREFIX_RDFS "Resource") != 0) {
TrackerClass **super_classes = tracker_class_get_super_classes (class);
gboolean had = FALSE;
@@ -962,17 +1035,6 @@ tracker_data_ontology_load_statement (TrackerDataManager *manager,
}
tracker_property_set_writeback (property, (strcmp (object, "true") == 0));
- } else if (g_strcmp0 (predicate, TRACKER_PREFIX_TRACKER "forceJournal") == 0) {
- TrackerProperty *property;
-
- property = tracker_ontologies_get_property_by_uri (manager->ontologies, subject);
-
- if (property == NULL) {
- g_critical ("%s: Unknown property %s", ontology_path, subject);
- return;
- }
-
- tracker_property_set_force_journal (property, (strcmp (object, "true") == 0));
} else if (g_strcmp0 (predicate, RDFS_SUB_PROPERTY_OF) == 0) {
TrackerProperty *property, *super_property;
gboolean is_new;
@@ -986,7 +1048,7 @@ tracker_data_ontology_load_statement (TrackerDataManager *manager,
is_new = tracker_property_get_is_new (property);
if (is_new != in_update) {
gboolean ignore = FALSE;
- /* Detect unsupported ontology change (this needs a journal replay) */
+ /* Detect unsupported ontology change */
if (in_update == TRUE && is_new == FALSE) {
TrackerProperty **super_properties = tracker_property_get_super_properties (property);
gboolean had = FALSE;
@@ -1067,7 +1129,7 @@ tracker_data_ontology_load_statement (TrackerDataManager *manager,
is_new = tracker_property_get_is_new (property);
if (is_new != in_update) {
- /* Detect unsupported ontology change (this needs a journal replay) */
+ /* Detect unsupported ontology change */
if (in_update == TRUE && is_new == FALSE) {
TrackerClass *old_domain = tracker_property_get_domain (property);
if (old_domain != domain) {
@@ -1158,44 +1220,6 @@ tracker_data_ontology_load_statement (TrackerDataManager *manager,
}
tracker_property_set_secondary_index (property, secondary_index);
- } else if (g_strcmp0 (predicate, TRACKER_PREFIX_TRACKER "transient") == 0) {
- TrackerProperty *property;
- gboolean is_new;
-
- property = tracker_ontologies_get_property_by_uri (manager->ontologies, subject);
- if (property == NULL) {
- g_critical ("%s: Unknown property %s", ontology_path, subject);
- return;
- }
-
- is_new = tracker_property_get_is_new (property);
- if (is_new != in_update) {
- /* Detect unsupported ontology change (this needs a journal replay).
- * Wouldn't be very hard to support this, just dropping the tabtle
- * or creating the table in the-non memdisk db file, but afaik this
- * isn't supported right now */
- if (in_update == TRUE && is_new == FALSE) {
- if (check_unsupported_property_value_change (manager,
- ontology_path,
- "tracker:transient",
- subject,
- predicate,
- object)) {
- handle_unsupported_ontology_change (manager,
- ontology_path,
- tracker_property_get_name (property),
- "tracker:transient",
- tracker_property_get_transient (property) ? "true" : "false",
- g_strcmp0 (object, "true") ==0 ? "true" : "false",
- error);
- }
- }
- return;
- }
-
- if (g_strcmp0 (object, "true") == 0) {
- tracker_property_set_transient (property, TRUE);
- }
} else if (g_strcmp0 (predicate, TRACKER_PREFIX_TRACKER "fulltextIndexed") == 0) {
TrackerProperty *property;
@@ -1317,17 +1341,22 @@ check_for_deleted_domain_index (TrackerDataManager *manager,
for (l = deleted; l != NULL; l = l->next) {
GError *error = NULL;
TrackerProperty *prop = l->data;
+ const gchar *uri;
+ GBytes *bytes;
g_debug ("Ontology change: deleting tracker:domainIndex: %s",
tracker_property_get_name (prop));
tracker_property_del_domain_index (prop, class);
tracker_class_del_domain_index (class, prop);
+ uri = tracker_property_get_uri (prop);
+ bytes = g_bytes_new (uri, strlen (uri) + 1);
tracker_data_delete_statement (manager->data_update, NULL,
tracker_class_get_uri (class),
TRACKER_PREFIX_TRACKER "domainIndex",
- tracker_property_get_uri (prop),
+ bytes,
&error);
+ g_bytes_unref (bytes);
if (error) {
g_critical ("Ontology change, %s", error->message);
@@ -1496,12 +1525,15 @@ check_for_deleted_super_properties (TrackerDataManager *manager,
TrackerProperty *prop_to_remove = copy->data;
const gchar *object = tracker_property_get_uri (prop_to_remove);
const gchar *subject = tracker_property_get_uri (property);
+ GBytes *bytes;
tracker_property_del_super_property (property, prop_to_remove);
+ bytes = g_bytes_new (object, strlen (object) + 1);
tracker_data_delete_statement (manager->data_update, NULL, subject,
TRACKER_PREFIX_RDFS "subPropertyOf",
- object, &n_error);
+ bytes, &n_error);
+ g_bytes_unref (bytes);
if (!n_error) {
tracker_data_update_buffer_flush (manager->data_update, &n_error);
@@ -1921,48 +1953,16 @@ get_ontology_from_file (TrackerDataManager *manager,
return ret;
}
-#ifndef DISABLE_JOURNAL
-static void
-load_ontology_ids_from_journal (TrackerDBJournalReader *reader,
- GHashTable **uri_id_map_out,
- gint *max_id)
-{
- GHashTable *uri_id_map;
-
- uri_id_map = g_hash_table_new_full (g_str_hash, g_str_equal,
- g_free, NULL);
-
- while (tracker_db_journal_reader_next (reader, NULL)) {
- TrackerDBJournalEntryType type;
-
- type = tracker_db_journal_reader_get_entry_type (reader);
- if (type == TRACKER_DB_JOURNAL_RESOURCE) {
- gint id;
- const gchar *uri;
-
- tracker_db_journal_reader_get_resource (reader, &id, &uri);
- g_hash_table_insert (uri_id_map, g_strdup (uri), GINT_TO_POINTER (id));
- if (id > *max_id) {
- *max_id = id;
- }
- }
- }
-
- *uri_id_map_out = uri_id_map;
-}
-#endif /* DISABLE_JOURNAL */
-
static void
tracker_data_ontology_process_statement (TrackerDataManager *manager,
- const gchar *graph,
const gchar *subject,
const gchar *predicate,
const gchar *object,
gboolean is_uri,
- gboolean in_update,
- gboolean ignore_nao_last_modified)
+ gboolean in_update)
{
GError *error = NULL;
+ GBytes *bytes;
if (g_strcmp0 (predicate, RDF_TYPE) == 0) {
if (g_strcmp0 (object, RDFS_CLASS) == 0) {
@@ -2011,7 +2011,6 @@ tracker_data_ontology_process_statement (TrackerDataManager *manager,
g_strcmp0 (predicate, RDFS_RANGE) == 0 ||
/* g_strcmp0 (predicate, NRL_MAX_CARDINALITY) == 0 || */
g_strcmp0 (predicate, TRACKER_PREFIX_TRACKER "indexed") == 0 ||
- g_strcmp0 (predicate, TRACKER_PREFIX_TRACKER "transient") == 0 ||
g_strcmp0 (predicate, TRACKER_PREFIX_TRACKER "fulltextIndexed") == 0) {
TrackerProperty *prop;
@@ -2036,41 +2035,33 @@ tracker_data_ontology_process_statement (TrackerDataManager *manager,
if (ontology && tracker_ontology_get_is_new (ontology) != in_update) {
return;
}
-
- if (ignore_nao_last_modified) {
- return;
- }
}
+ bytes = g_bytes_new (object, strlen (object) + 1);
+
if (is_uri) {
- tracker_data_insert_statement_with_uri (manager->data_update, graph, subject,
- predicate, object,
+ tracker_data_insert_statement_with_uri (manager->data_update, NULL, subject,
+ predicate, bytes,
&error);
-
- if (error != NULL) {
- g_critical ("%s", error->message);
- g_error_free (error);
- return;
- }
-
} else {
- tracker_data_insert_statement_with_string (manager->data_update, graph, subject,
- predicate, object,
+ tracker_data_insert_statement_with_string (manager->data_update, NULL, subject,
+ predicate, bytes,
&error);
+ }
- if (error != NULL) {
- g_critical ("%s", error->message);
- g_error_free (error);
- return;
- }
+ g_bytes_unref (bytes);
+
+ if (error != NULL) {
+ g_critical ("%s", error->message);
+ g_error_free (error);
+ return;
}
}
static void
import_ontology_file (TrackerDataManager *manager,
GFile *file,
- gboolean in_update,
- gboolean ignore_nao_last_modified)
+ gboolean in_update)
{
GError *error = NULL;
TrackerTurtleReader* reader;
@@ -2084,17 +2075,14 @@ import_ontology_file (TrackerDataManager *manager,
}
while (tracker_turtle_reader_next (reader, &error)) {
-
- const gchar *graph = tracker_turtle_reader_get_graph (reader);
const gchar *subject = tracker_turtle_reader_get_subject (reader);
const gchar *predicate = tracker_turtle_reader_get_predicate (reader);
const gchar *object = tracker_turtle_reader_get_object (reader);
tracker_data_ontology_process_statement (manager,
- graph, subject, predicate, object,
+ subject, predicate, object,
tracker_turtle_reader_get_object_is_uri (reader),
- in_update, ignore_nao_last_modified);
-
+ in_update);
}
g_object_unref (reader);
@@ -2378,11 +2366,9 @@ db_get_static_data (TrackerDBInterface *iface,
"\"tracker:indexed\", "
"(SELECT Uri FROM Resource WHERE ID = \"tracker:secondaryIndex\"), "
"\"tracker:fulltextIndexed\", "
- "\"tracker:transient\", "
"\"tracker:writeback\", "
"(SELECT 1 FROM \"rdfs:Resource_rdf:type\" WHERE ID = \"rdf:Property\".ID AND "
"\"rdf:type\" = (SELECT ID FROM Resource WHERE Uri = '" NRL_INVERSE_FUNCTIONAL_PROPERTY "')), "
- "\"tracker:forceJournal\", "
"\"tracker:defaultValue\" "
"FROM \"rdf:Property\" ORDER BY ID");
@@ -2397,8 +2383,8 @@ db_get_static_data (TrackerDBInterface *iface,
TrackerProperty *property;
const gchar *uri, *domain_uri, *range_uri, *secondary_index_uri, *default_value;
gboolean multi_valued, indexed, fulltext_indexed;
- gboolean transient, is_inverse_functional_property;
- gboolean writeback, force_journal;
+ gboolean is_inverse_functional_property;
+ gboolean writeback;
gint id;
property = tracker_property_new (FALSE);
@@ -2441,18 +2427,8 @@ db_get_static_data (TrackerDBInterface *iface,
fulltext_indexed = FALSE;
}
- tracker_db_cursor_get_value (cursor, 8, &value);
-
- if (G_VALUE_TYPE (&value) != 0) {
- transient = (g_value_get_int64 (&value) == 1);
- g_value_unset (&value);
- } else {
- /* NULL */
- transient = FALSE;
- }
-
/* tracker:writeback column */
- tracker_db_cursor_get_value (cursor, 9, &value);
+ tracker_db_cursor_get_value (cursor, 8, &value);
if (G_VALUE_TYPE (&value) != 0) {
writeback = (g_value_get_int64 (&value) == 1);
@@ -2463,7 +2439,7 @@ db_get_static_data (TrackerDBInterface *iface,
}
/* NRL_INVERSE_FUNCTIONAL_PROPERTY column */
- tracker_db_cursor_get_value (cursor, 10, &value);
+ tracker_db_cursor_get_value (cursor, 9, &value);
if (G_VALUE_TYPE (&value) != 0) {
is_inverse_functional_property = TRUE;
@@ -2473,24 +2449,12 @@ db_get_static_data (TrackerDBInterface *iface,
is_inverse_functional_property = FALSE;
}
- /* tracker:forceJournal column */
- tracker_db_cursor_get_value (cursor, 11, &value);
-
- if (G_VALUE_TYPE (&value) != 0) {
- force_journal = (g_value_get_int64 (&value) == 1);
- g_value_unset (&value);
- } else {
- /* NULL */
- force_journal = TRUE;
- }
-
- default_value = tracker_db_cursor_get_string (cursor, 12, NULL);
+ default_value = tracker_db_cursor_get_string (cursor, 10, NULL);
tracker_property_set_ontologies (property, manager->ontologies);
tracker_property_set_is_new_domain_index (property, tracker_ontologies_get_class_by_uri (manager->ontologies, domain_uri), FALSE);
tracker_property_set_is_new (property, FALSE);
tracker_property_set_cardinality_changed (property, FALSE);
- tracker_property_set_transient (property, transient);
tracker_property_set_uri (property, uri);
tracker_property_set_id (property, id);
tracker_property_set_domain (property, tracker_ontologies_get_class_by_uri (manager->ontologies, domain_uri));
@@ -2499,7 +2463,6 @@ db_get_static_data (TrackerDBInterface *iface,
tracker_property_set_orig_multiple_values (property, multi_valued);
tracker_property_set_indexed (property, indexed);
tracker_property_set_default_value (property, default_value);
- tracker_property_set_force_journal (property, force_journal);
tracker_property_set_db_schema_changed (property, FALSE);
tracker_property_set_writeback (property, writeback);
@@ -2553,7 +2516,7 @@ insert_uri_in_resource_table (TrackerDataManager *manager,
stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE,
&internal_error,
"INSERT OR IGNORE "
- "INTO Resource "
+ "INTO main.Resource "
"(ID, Uri) "
"VALUES (?, ?)");
if (internal_error) {
@@ -2571,12 +2534,6 @@ insert_uri_in_resource_table (TrackerDataManager *manager,
return;
}
-#ifndef DISABLE_JOURNAL
- if (!manager->in_journal_replay) {
- tracker_db_journal_append_resource (manager->ontology_writer, id, uri);
- }
-#endif /* DISABLE_JOURNAL */
-
g_object_unref (stmt);
}
@@ -2591,40 +2548,27 @@ range_change_for (TrackerProperty *property,
* should forbid conversion from anything to resource or datetime in error
* handling earlier */
- g_string_append_printf (in_col_sql, ", \"%s\", \"%s:graph\"",
- field_name, field_name);
+ g_string_append_printf (in_col_sql, ", \"%s\"", field_name);
if (tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_INTEGER ||
tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DOUBLE) {
- g_string_append_printf (sel_col_sql, ", \"%s\" + 0, \"%s:graph\"",
- field_name, field_name);
+ g_string_append_printf (sel_col_sql, ", \"%s\" + 0", field_name);
} else if (tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME) {
/* TODO (see above) */
- g_string_append_printf (sel_col_sql, ", \"%s\", \"%s:graph\"",
- field_name, field_name);
-
- g_string_append_printf (in_col_sql, ", \"%s:localDate\", \"%s:localTime\"",
- tracker_property_get_name (property),
- tracker_property_get_name (property));
-
- g_string_append_printf (sel_col_sql, ", \"%s:localDate\", \"%s:localTime\"",
- tracker_property_get_name (property),
- tracker_property_get_name (property));
-
+ g_string_append_printf (sel_col_sql, ", \"%s\"", field_name);
} else if (tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_BOOLEAN) {
- g_string_append_printf (sel_col_sql, ", \"%s\" != 0, \"%s:graph\"",
- field_name, field_name);
+ g_string_append_printf (sel_col_sql, ", \"%s\" != 0", field_name);
} else {
- g_string_append_printf (sel_col_sql, ", \"%s\", \"%s:graph\"",
- field_name, field_name);
+ g_string_append_printf (sel_col_sql, ", \"%s\"", field_name);
}
}
static void
create_decomposed_metadata_property_table (TrackerDBInterface *iface,
TrackerProperty *property,
+ const gchar *database,
const gchar *service_name,
TrackerClass *service,
const gchar **sql_type_for_single_value,
@@ -2635,7 +2579,7 @@ create_decomposed_metadata_property_table (TrackerDBInterface *iface,
GError *internal_error = NULL;
const char *field_name;
const char *sql_type;
- gboolean not_single;
+ gboolean not_single, datetime;
field_name = tracker_property_get_name (property);
@@ -2643,6 +2587,7 @@ create_decomposed_metadata_property_table (TrackerDBInterface *iface,
switch (tracker_property_get_data_type (property)) {
case TRACKER_PROPERTY_TYPE_STRING:
+ case TRACKER_PROPERTY_TYPE_LANGSTRING:
sql_type = "TEXT";
break;
case TRACKER_PROPERTY_TYPE_INTEGER:
@@ -2660,6 +2605,8 @@ create_decomposed_metadata_property_table (TrackerDBInterface *iface,
break;
}
+ datetime = tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME;
+
if (!in_update || (in_update && (tracker_property_get_is_new (property) ||
tracker_property_get_is_new_domain_index (property, service) ||
tracker_property_get_cardinality_changed (property) ||
@@ -2682,7 +2629,8 @@ create_decomposed_metadata_property_table (TrackerDBInterface *iface,
service_name, field_name);
tracker_db_interface_execute_query (iface, &internal_error,
- "DROP INDEX IF EXISTS \"%s_%s_ID\"",
+ "DROP INDEX IF EXISTS \"%s\".\"%s_%s_ID\"",
+ database,
service_name,
field_name);
@@ -2692,8 +2640,9 @@ create_decomposed_metadata_property_table (TrackerDBInterface *iface,
}
tracker_db_interface_execute_query (iface, &internal_error,
- "ALTER TABLE \"%s_%s\" RENAME TO \"%s_%s_TEMP\"",
- service_name, field_name, service_name, field_name);
+ "ALTER TABLE \"%s\".\"%s_%s\" RENAME TO \"%s_%s_TEMP\"",
+ database, service_name, field_name,
+ service_name, field_name);
if (internal_error) {
g_propagate_error (error, internal_error);
@@ -2702,21 +2651,22 @@ create_decomposed_metadata_property_table (TrackerDBInterface *iface,
} else if (in_change && tracker_property_get_cardinality_changed (property)) {
/* We should be dropping all indices colliding with the new table name */
tracker_db_interface_execute_query (iface, &internal_error,
- "DROP INDEX IF EXISTS \"%s_%s\"",
+ "DROP INDEX IF EXISTS \"%s\".\"%s_%s\"",
+ database,
service_name,
field_name);
}
sql = g_string_new ("");
- g_string_append_printf (sql, "CREATE TABLE \"%s_%s\" ("
- "ID INTEGER NOT NULL, "
- "\"%s\" %s NOT NULL, "
- "\"%s:graph\" INTEGER",
- service_name,
- field_name,
- field_name,
- sql_type,
- field_name);
+ g_string_append_printf (sql,
+ "CREATE TABLE \"%s\".\"%s_%s\" ("
+ "ID INTEGER NOT NULL, "
+ "\"%s\" %s NOT NULL",
+ database,
+ service_name,
+ field_name,
+ field_name,
+ sql_type);
if (in_change && !tracker_property_get_is_new (property)) {
in_col_sql = g_string_new ("ID");
@@ -2725,16 +2675,6 @@ create_decomposed_metadata_property_table (TrackerDBInterface *iface,
range_change_for (property, in_col_sql, sel_col_sql, field_name);
}
- if (tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME) {
- /* xsd:dateTime is stored in three columns:
- * universal time, local date, local time of day */
- g_string_append_printf (sql,
- ", \"%s:localDate\" INTEGER NOT NULL"
- ", \"%s:localTime\" INTEGER NOT NULL",
- tracker_property_get_name (property),
- tracker_property_get_name (property));
- }
-
tracker_db_interface_execute_query (iface, &internal_error,
"%s)", sql->str);
@@ -2747,14 +2687,16 @@ create_decomposed_metadata_property_table (TrackerDBInterface *iface,
if (tracker_property_get_indexed (property)) {
/* use different UNIQUE index for properties whose
* value should be indexed to minimize index size */
- set_index_for_multi_value_property (iface, service_name, field_name, TRUE, TRUE,
+ set_index_for_multi_value_property (iface, database, service_name, field_name, TRUE, TRUE,
+ datetime,
&internal_error);
if (internal_error) {
g_propagate_error (error, internal_error);
goto error_out;
}
} else {
- set_index_for_multi_value_property (iface, service_name, field_name, FALSE, TRUE,
+ set_index_for_multi_value_property (iface, database, service_name, field_name, FALSE, TRUE,
+ datetime,
&internal_error);
/* we still have to include the property value in
* the unique index for proper constraints */
@@ -2768,10 +2710,10 @@ create_decomposed_metadata_property_table (TrackerDBInterface *iface,
!tracker_property_get_cardinality_changed (property) && in_col_sql && sel_col_sql) {
gchar *query;
- query = g_strdup_printf ("INSERT INTO \"%s_%s\"(%s) "
- "SELECT %s FROM \"%s_%s_TEMP\"",
- service_name, field_name, in_col_sql->str,
- sel_col_sql->str, service_name, field_name);
+ query = g_strdup_printf ("INSERT INTO \"%s\".\"%s_%s\"(%s) "
+ "SELECT %s FROM \"%s\".\"%s_%s_TEMP\"",
+ database, service_name, field_name, in_col_sql->str,
+ sel_col_sql->str, database, service_name, field_name);
tracker_db_interface_execute_query (iface, &internal_error, "%s", query);
@@ -2782,8 +2724,8 @@ create_decomposed_metadata_property_table (TrackerDBInterface *iface,
}
g_free (query);
- tracker_db_interface_execute_query (iface, &internal_error, "DROP TABLE \"%s_%s_TEMP\"",
- service_name, field_name);
+ tracker_db_interface_execute_query (iface, &internal_error, "DROP TABLE \"%s\".\"%s_%s_TEMP\"",
+ database, service_name, field_name);
if (internal_error) {
g_propagate_error (error, internal_error);
@@ -2795,14 +2737,16 @@ create_decomposed_metadata_property_table (TrackerDBInterface *iface,
if (tracker_property_get_indexed (property)) {
/* use different UNIQUE index for properties whose
* value should be indexed to minimize index size */
- set_index_for_multi_value_property (iface, service_name, field_name, TRUE, TRUE,
+ set_index_for_multi_value_property (iface, database, service_name, field_name, TRUE, TRUE,
+ datetime,
&internal_error);
if (internal_error) {
g_propagate_error (error, internal_error);
goto error_out;
}
} else {
- set_index_for_multi_value_property (iface, service_name, field_name, FALSE, TRUE,
+ set_index_for_multi_value_property (iface, database, service_name, field_name, FALSE, TRUE,
+ datetime,
&internal_error);
if (internal_error) {
g_propagate_error (error, internal_error);
@@ -2848,6 +2792,7 @@ is_a_domain_index (TrackerProperty **domain_indexes, TrackerProperty *property)
static void
copy_from_domain_to_domain_index (TrackerDBInterface *iface,
+ const gchar *database,
TrackerProperty *domain_index,
const gchar *column_name,
const gchar *column_suffix,
@@ -2863,14 +2808,16 @@ copy_from_domain_to_domain_index (TrackerDBInterface *iface,
source_name = tracker_class_get_name (source_domain);
dest_name = tracker_class_get_name (dest_domain);
- query = g_strdup_printf ("UPDATE \"%s\" SET \"%s%s\"=("
- "SELECT \"%s%s\" FROM \"%s\" "
+ query = g_strdup_printf ("UPDATE \"%s\".\"%s\" SET \"%s%s\"=("
+ "SELECT \"%s%s\" FROM \"%s\".\"%s\" "
"WHERE \"%s\".ID = \"%s\".ID)",
+ database,
dest_name,
column_name,
column_suffix ? column_suffix : "",
column_name,
column_suffix ? column_suffix : "",
+ database,
source_name,
source_name,
dest_name);
@@ -2907,6 +2854,7 @@ schedule_copy (GPtrArray *schedule,
static void
create_insert_delete_triggers (TrackerDBInterface *iface,
+ const gchar *database,
const gchar *table_name,
const gchar * const *properties,
gint n_properties,
@@ -2918,7 +2866,8 @@ create_insert_delete_triggers (TrackerDBInterface *iface,
/* Insert trigger */
tracker_db_interface_execute_query (iface, &internal_error,
- "DROP TRIGGER IF EXISTS \"trigger_insert_%s\" ",
+ "DROP TRIGGER IF EXISTS \"%s\".\"trigger_insert_%s\" ",
+ database,
table_name);
if (internal_error) {
g_propagate_error (error, internal_error);
@@ -2927,15 +2876,18 @@ create_insert_delete_triggers (TrackerDBInterface *iface,
trigger_query = g_string_new (NULL);
g_string_append_printf (trigger_query,
- "CREATE TRIGGER \"trigger_insert_%s\" "
+ "CREATE TRIGGER \"%s\".\"trigger_insert_%s\" "
"AFTER INSERT ON \"%s\" "
"FOR EACH ROW BEGIN ",
- table_name, table_name);
+ database, table_name,
+ table_name);
for (i = 0; i < n_properties; i++) {
g_string_append_printf (trigger_query,
- "UPDATE Resource "
- "SET Refcount = Refcount + 1 "
- "WHERE Resource.rowid = NEW.\"%s\"; ",
+ "INSERT OR IGNORE INTO Refcount (ROWID, Refcount) "
+ "SELECT NEW.\"%s\", 0 WHERE NEW.\"%s\" IS NOT NULL; "
+ "UPDATE Refcount SET Refcount = Refcount + 1 WHERE Refcount.ROWID = NEW.\"%s\"; ",
+ properties[i],
+ properties[i],
properties[i]);
}
@@ -2951,7 +2903,8 @@ create_insert_delete_triggers (TrackerDBInterface *iface,
/* Delete trigger */
tracker_db_interface_execute_query (iface, &internal_error,
- "DROP TRIGGER IF EXISTS \"trigger_delete_%s\" ",
+ "DROP TRIGGER IF EXISTS \"%s\".\"trigger_delete_%s\" ",
+ database,
table_name);
if (internal_error) {
g_propagate_error (error, internal_error);
@@ -2960,16 +2913,16 @@ create_insert_delete_triggers (TrackerDBInterface *iface,
trigger_query = g_string_new (NULL);
g_string_append_printf (trigger_query,
- "CREATE TRIGGER \"trigger_delete_%s\" "
+ "CREATE TRIGGER \"%s\".\"trigger_delete_%s\" "
"AFTER DELETE ON \"%s\" "
"FOR EACH ROW BEGIN ",
- table_name, table_name);
+ database, table_name,
+ table_name);
for (i = 0; i < n_properties; i++) {
g_string_append_printf (trigger_query,
- "UPDATE Resource "
- "SET Refcount = Refcount - 1 "
- "WHERE Resource.rowid = OLD.\"%s\"; ",
- properties[i]);
+ "UPDATE Refcount SET Refcount = Refcount - 1 WHERE Refcount.rowid = OLD.\"%s\"; "
+ "DELETE FROM Refcount WHERE Refcount.ROWID = OLD.\"%s\" AND Refcount.Refcount = 0; ",
+ properties[i], properties[i]);
}
g_string_append (trigger_query, "END; ");
@@ -2986,6 +2939,7 @@ create_insert_delete_triggers (TrackerDBInterface *iface,
static void
create_table_triggers (TrackerDataManager *manager,
TrackerDBInterface *iface,
+ const gchar *database,
TrackerClass *klass,
GError **error)
{
@@ -3020,7 +2974,7 @@ create_table_triggers (TrackerDataManager *manager,
tracker_class_get_name (klass),
property_name);
- create_insert_delete_triggers (iface, table_name, properties,
+ create_insert_delete_triggers (iface, database, table_name, properties,
G_N_ELEMENTS (properties),
&internal_error);
if (internal_error) {
@@ -3046,16 +3000,21 @@ create_table_triggers (TrackerDataManager *manager,
}
tracker_db_interface_execute_query (iface, &internal_error,
- "CREATE TRIGGER \"trigger_update_%s_%s\" "
+ "CREATE TRIGGER \"%s\".\"trigger_update_%s_%s\" "
"AFTER UPDATE OF \"%s\" ON \"%s\" "
"FOR EACH ROW BEGIN "
- "UPDATE Resource SET Refcount = Refcount + 1 WHERE Resource.rowid = NEW.\"%s\";"
- "UPDATE Resource SET Refcount = Refcount - 1 WHERE Resource.rowid = OLD.\"%s\";"
+ "INSERT OR IGNORE INTO Refcount (ROWID, Refcount) "
+ "SELECT NEW.\"%s\", 0 WHERE NEW.\"%s\" IS NOT NULL; "
+ "UPDATE Refcount SET Refcount = Refcount + 1 WHERE Refcount.ROWID = NEW.\"%s\"; "
+ "UPDATE Refcount SET Refcount = Refcount - 1 WHERE Refcount.rowid = OLD.\"%s\";"
+ "DELETE FROM Refcount WHERE Refcount.ROWID = OLD.\"%s\" AND Refcount.Refcount = 0; "
"END",
+ database,
tracker_class_get_name (klass),
property_name,
property_name, table_name,
- property_name, property_name);
+ property_name, property_name,
+ property_name, property_name, property_name);
g_free (table_name);
if (internal_error) {
@@ -3065,7 +3024,7 @@ create_table_triggers (TrackerDataManager *manager,
}
}
- create_insert_delete_triggers (iface,
+ create_insert_delete_triggers (iface, database,
tracker_class_get_name (klass),
(const gchar * const *) trigger_properties->pdata,
trigger_properties->len,
@@ -3081,6 +3040,7 @@ create_table_triggers (TrackerDataManager *manager,
static void
create_decomposed_metadata_tables (TrackerDataManager *manager,
TrackerDBInterface *iface,
+ const gchar *database,
TrackerClass *service,
gboolean in_update,
gboolean in_change,
@@ -3092,7 +3052,6 @@ create_decomposed_metadata_tables (TrackerDataManager *manager,
GString *sel_col_sql = NULL;
TrackerProperty **properties, *property, **domain_indexes;
GSList *class_properties = NULL, *field_it;
- gboolean main_class;
guint i, n_props;
gboolean in_alter = in_update;
GError *internal_error = NULL;
@@ -3104,8 +3063,6 @@ create_decomposed_metadata_tables (TrackerDataManager *manager,
g_return_if_fail (service_name != NULL);
- main_class = (strcmp (service_name, "rdfs:Resource") == 0);
-
if (g_str_has_prefix (service_name, "xsd:")) {
/* xsd classes do not derive from rdfs:Resource and do not need separate tables */
return;
@@ -3114,8 +3071,8 @@ create_decomposed_metadata_tables (TrackerDataManager *manager,
if (in_change && !tracker_class_get_is_new (service)) {
g_debug ("Rename: ALTER TABLE \"%s\" RENAME TO \"%s_TEMP\"", service_name, service_name);
tracker_db_interface_execute_query (iface, &internal_error,
- "ALTER TABLE \"%s\" RENAME TO \"%s_TEMP\"",
- service_name, service_name);
+ "ALTER TABLE \"%s\".\"%s\" RENAME TO \"%s_TEMP\"",
+ database, service_name, service_name);
in_col_sql = g_string_new ("ID");
sel_col_sql = g_string_new ("ID");
if (internal_error) {
@@ -3129,21 +3086,19 @@ create_decomposed_metadata_tables (TrackerDataManager *manager,
g_debug ("Altering database with new class '%s' (create)", service_name);
in_alter = FALSE;
create_sql = g_string_new ("");
- g_string_append_printf (create_sql, "CREATE TABLE \"%s\" (ID INTEGER NOT NULL PRIMARY KEY", service_name);
- if (main_class) {
- /* FIXME: This column is unneeded */
- g_string_append (create_sql, ", Available INTEGER DEFAULT 1");
- }
+ g_string_append_printf (create_sql, "CREATE TABLE \"%s\".\"%s\" (ID INTEGER NOT NULL PRIMARY KEY",
+ database, service_name);
}
properties = tracker_ontologies_get_properties (manager->ontologies, &n_props);
domain_indexes = tracker_class_get_domain_indexes (service);
for (i = 0; i < n_props; i++) {
- gboolean is_domain_index;
+ gboolean is_domain_index, datetime;
property = properties[i];
is_domain_index = is_a_domain_index (domain_indexes, property);
+ datetime = tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME;
if (tracker_property_get_domain (property) == service || is_domain_index) {
gboolean put_change;
@@ -3151,6 +3106,7 @@ create_decomposed_metadata_tables (TrackerDataManager *manager,
const gchar *field_name;
create_decomposed_metadata_property_table (iface, property,
+ database,
service_name,
service,
&sql_type_for_single_value,
@@ -3196,7 +3152,8 @@ create_decomposed_metadata_tables (TrackerDataManager *manager,
schedule_copy (copy_schedule, property, field_name, NULL);
}
- if (g_ascii_strcasecmp (sql_type_for_single_value, "TEXT") == 0) {
+ if (g_ascii_strcasecmp (sql_type_for_single_value, "TEXT") == 0 ||
+ g_ascii_strcasecmp (sql_type_for_single_value, "BLOB") == 0) {
g_string_append (create_sql, " COLLATE " TRACKER_COLLATION_NAME);
}
@@ -3209,28 +3166,6 @@ create_decomposed_metadata_tables (TrackerDataManager *manager,
if (tracker_property_get_is_inverse_functional_property (property)) {
g_string_append (create_sql, " UNIQUE");
}
-
- g_string_append_printf (create_sql, ", \"%s:graph\" INTEGER",
- field_name);
-
- if (is_domain_index && tracker_property_get_is_new_domain_index (property, service)) {
- schedule_copy (copy_schedule, property, field_name, ":graph");
- }
-
- if (tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME) {
- /* xsd:dateTime is stored in three columns:
- * universal time, local date, local time of day */
- g_string_append_printf (create_sql, ", \"%s:localDate\" INTEGER, \"%s:localTime\" INTEGER",
- tracker_property_get_name (property),
- tracker_property_get_name (property));
-
- if (is_domain_index && tracker_property_get_is_new_domain_index (property, service)) {
- schedule_copy (copy_schedule, property, field_name, ":localTime");
- schedule_copy (copy_schedule, property, field_name, ":localDate");
- }
-
- }
-
} else if ((!is_domain_index && tracker_property_get_is_new (property)) ||
(is_domain_index && tracker_property_get_is_new_domain_index (property, service))) {
GString *alter_sql = NULL;
@@ -3239,12 +3174,14 @@ create_decomposed_metadata_tables (TrackerDataManager *manager,
class_properties = g_slist_prepend (class_properties, property);
alter_sql = g_string_new ("ALTER TABLE ");
- g_string_append_printf (alter_sql, "\"%s\" ADD COLUMN \"%s\" %s",
+ g_string_append_printf (alter_sql, "\"%s\".\"%s\" ADD COLUMN \"%s\" %s",
+ database,
service_name,
field_name,
sql_type_for_single_value);
- if (g_ascii_strcasecmp (sql_type_for_single_value, "TEXT") == 0) {
+ if (g_ascii_strcasecmp (sql_type_for_single_value, "TEXT") == 0 ||
+ g_ascii_strcasecmp (sql_type_for_single_value, "BLOB") == 0) {
g_string_append (alter_sql, " COLLATE " TRACKER_COLLATION_NAME);
}
@@ -3265,7 +3202,7 @@ create_decomposed_metadata_tables (TrackerDataManager *manager,
g_propagate_error (error, internal_error);
goto error_out;
} else if (is_domain_index) {
- copy_from_domain_to_domain_index (iface, property,
+ copy_from_domain_to_domain_index (iface, database, property,
field_name, NULL,
service,
&internal_error);
@@ -3276,8 +3213,9 @@ create_decomposed_metadata_tables (TrackerDataManager *manager,
}
/* This is implicit for all domain-specific-indices */
- set_index_for_single_value_property (iface, service_name,
+ set_index_for_single_value_property (iface, database, service_name,
field_name, TRUE,
+ datetime,
&internal_error);
if (internal_error) {
g_string_free (alter_sql, TRUE);
@@ -3287,83 +3225,6 @@ create_decomposed_metadata_tables (TrackerDataManager *manager,
}
g_string_free (alter_sql, TRUE);
-
- alter_sql = g_string_new ("ALTER TABLE ");
- g_string_append_printf (alter_sql, "\"%s\" ADD COLUMN \"%s:graph\" INTEGER",
- service_name,
- field_name);
- g_debug ("Altering: '%s'", alter_sql->str);
- tracker_db_interface_execute_query (iface, &internal_error,
- "%s", alter_sql->str);
- if (internal_error) {
- g_string_free (alter_sql, TRUE);
- g_propagate_error (error, internal_error);
- goto error_out;
- } else if (is_domain_index) {
- copy_from_domain_to_domain_index (iface, property,
- field_name, ":graph",
- service,
- &internal_error);
- if (internal_error) {
- g_string_free (alter_sql, TRUE);
- g_propagate_error (error, internal_error);
- goto error_out;
- }
- }
-
- g_string_free (alter_sql, TRUE);
-
- if (tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME) {
- alter_sql = g_string_new ("ALTER TABLE ");
- g_string_append_printf (alter_sql, "\"%s\" ADD COLUMN \"%s:localDate\" INTEGER",
- service_name,
- field_name);
- g_debug ("Altering: '%s'", alter_sql->str);
- tracker_db_interface_execute_query (iface, &internal_error,
- "%s", alter_sql->str);
-
- if (internal_error) {
- g_string_free (alter_sql, TRUE);
- g_propagate_error (error, internal_error);
- goto error_out;
- } else if (is_domain_index) {
- copy_from_domain_to_domain_index (iface, property,
- field_name, ":localDate",
- service,
- &internal_error);
- if (internal_error) {
- g_string_free (alter_sql, TRUE);
- g_propagate_error (error, internal_error);
- goto error_out;
- }
- }
-
- g_string_free (alter_sql, TRUE);
-
- alter_sql = g_string_new ("ALTER TABLE ");
- g_string_append_printf (alter_sql, "\"%s\" ADD COLUMN \"%s:localTime\" INTEGER",
- service_name,
- field_name);
- g_debug ("Altering: '%s'", alter_sql->str);
- tracker_db_interface_execute_query (iface, &internal_error,
- "%s", alter_sql->str);
- if (internal_error) {
- g_string_free (alter_sql, TRUE);
- g_propagate_error (error, internal_error);
- goto error_out;
- } else if (is_domain_index) {
- copy_from_domain_to_domain_index (iface, property,
- field_name, ":localTime",
- service,
- &internal_error);
- if (internal_error) {
- g_string_free (alter_sql, TRUE);
- g_propagate_error (error, internal_error);
- goto error_out;
- }
- }
- g_string_free (alter_sql, TRUE);
- }
} else {
put_change = TRUE;
}
@@ -3390,7 +3251,7 @@ create_decomposed_metadata_tables (TrackerDataManager *manager,
for (field_it = class_properties; field_it != NULL; field_it = field_it->next) {
TrackerProperty *field, *secondary_index;
const char *field_name;
- gboolean is_domain_index;
+ gboolean is_domain_index, datetime;
field = field_it->data;
@@ -3401,18 +3262,20 @@ create_decomposed_metadata_tables (TrackerDataManager *manager,
&& (tracker_property_get_indexed (field) || is_domain_index)) {
field_name = tracker_property_get_name (field);
+ datetime = tracker_property_get_data_type (field) == TRACKER_PROPERTY_TYPE_DATETIME;
secondary_index = tracker_property_get_secondary_index (field);
if (secondary_index == NULL) {
- set_index_for_single_value_property (iface, service_name,
+ set_index_for_single_value_property (iface, database, service_name,
field_name, TRUE,
+ datetime,
&internal_error);
if (internal_error) {
g_propagate_error (error, internal_error);
goto error_out;
}
} else {
- set_secondary_index_for_single_value_property (iface, service_name, field_name,
+ set_secondary_index_for_single_value_property (iface, database, service_name, field_name,
tracker_property_get_name (secondary_index),
TRUE, &internal_error);
if (internal_error) {
@@ -3427,10 +3290,10 @@ create_decomposed_metadata_tables (TrackerDataManager *manager,
guint i;
gchar *query;
- query = g_strdup_printf ("INSERT INTO \"%s\"(%s) "
- "SELECT %s FROM \"%s_TEMP\"",
- service_name, in_col_sql->str,
- sel_col_sql->str, service_name);
+ query = g_strdup_printf ("INSERT INTO \"%s\".\"%s\"(%s) "
+ "SELECT %s FROM \"%s\".\"%s_TEMP\"",
+ database, service_name, in_col_sql->str,
+ sel_col_sql->str, database, service_name);
g_debug ("Copy: %s", query);
@@ -3458,12 +3321,12 @@ create_decomposed_metadata_tables (TrackerDataManager *manager,
/* Columns happen to be the same for decomposed multi-value and single value atm */
- query = g_strdup_printf ("INSERT INTO \"%s_%s\"(%s) "
- "SELECT %s FROM \"%s_TEMP\" "
+ query = g_strdup_printf ("INSERT INTO \"%s\".\"%s_%s\"(%s) "
+ "SELECT %s FROM \"%s\".\"%s_TEMP\" "
"WHERE ID IS NOT NULL AND \"%s\" IS NOT NULL",
- service_name, field_name,
+ database, service_name, field_name,
n_in_col_sql->str, n_sel_col_sql->str,
- service_name, field_name);
+ database, service_name, field_name);
g_string_free (n_in_col_sql, TRUE);
g_string_free (n_sel_col_sql, TRUE);
@@ -3483,7 +3346,8 @@ create_decomposed_metadata_tables (TrackerDataManager *manager,
g_debug ("Rename (drop): DROP TABLE \"%s_TEMP\"", service_name);
tracker_db_interface_execute_query (iface, &internal_error,
- "DROP TABLE \"%s_TEMP\"", service_name);
+ "DROP TABLE \"%s\".\"%s_TEMP\"",
+ database, service_name);
if (internal_error) {
g_propagate_error (error, internal_error);
@@ -3496,7 +3360,7 @@ create_decomposed_metadata_tables (TrackerDataManager *manager,
* ontology changes. One situation where this is not true are
* removal or properties with rdfs:Resource range.
*/
- create_table_triggers (manager, iface, service, &internal_error);
+ create_table_triggers (manager, iface, database, service, &internal_error);
if (internal_error) {
g_propagate_error (error, internal_error);
@@ -3508,7 +3372,7 @@ create_decomposed_metadata_tables (TrackerDataManager *manager,
guint i;
for (i = 0; i < copy_schedule->len; i++) {
ScheduleCopy *sched = g_ptr_array_index (copy_schedule, i);
- copy_from_domain_to_domain_index (iface, sched->prop,
+ copy_from_domain_to_domain_index (iface, database, sched->prop,
sched->field_name, sched->suffix,
service,
&internal_error);
@@ -3542,52 +3406,6 @@ error_out:
}
static void
-clean_decomposed_transient_metadata (TrackerDataManager *manager,
- TrackerDBInterface *iface)
-{
- TrackerProperty **properties;
- TrackerProperty *property;
- guint i, n_props;
-
- properties = tracker_ontologies_get_properties (manager->ontologies, &n_props);
-
- for (i = 0; i < n_props; i++) {
- property = properties[i];
-
- if (tracker_property_get_transient (property)) {
- TrackerClass *domain;
- const gchar *service_name;
- const gchar *prop_name;
- GError *error = NULL;
-
- domain = tracker_property_get_domain (property);
- service_name = tracker_class_get_name (domain);
- prop_name = tracker_property_get_name (property);
-
- if (tracker_property_get_multiple_values (property)) {
- /* create the disposable table */
- tracker_db_interface_execute_query (iface, &error, "DELETE FROM \"%s_%s\"",
- service_name,
- prop_name);
- } else {
- /* create the disposable table */
- tracker_db_interface_execute_query (iface, &error, "UPDATE \"%s\" SET \"%s\" = NULL",
- service_name,
- prop_name);
- }
-
- if (error) {
- g_critical ("Cleaning transient propery '%s:%s' failed: %s",
- service_name,
- prop_name,
- error->message);
- g_error_free (error);
- }
- }
- }
-}
-
-static void
tracker_data_ontology_import_finished (TrackerDataManager *manager)
{
TrackerClass **classes;
@@ -3640,34 +3458,29 @@ query_table_exists (TrackerDBInterface *iface,
static gboolean
create_base_tables (TrackerDataManager *manager,
TrackerDBInterface *iface,
- gboolean *altered,
GError **error)
{
GError *internal_error = NULL;
- if (!query_table_exists (iface, "Resource", &internal_error) && !internal_error) {
- tracker_db_interface_execute_query (iface, &internal_error,
- "CREATE TABLE Resource (ID INTEGER NOT NULL PRIMARY KEY,"
- " Uri TEXT NOT NULL, Refcount INTEGER DEFAULT 0, UNIQUE (Uri))");
- } else if (!internal_error) {
- tracker_db_interface_execute_query (iface, &internal_error,
- "ALTER TABLE Resource ADD COLUMN Refcount INTEGER DEFAULT 0");
+ tracker_db_interface_execute_query (iface, &internal_error,
+ "CREATE TABLE Resource (ID INTEGER NOT NULL PRIMARY KEY,"
+ " Uri TEXT NOT NULL, BlankNode INTEGER DEFAULT 0, UNIQUE (Uri))");
- if (!internal_error)
- *altered = TRUE;
- else
- g_clear_error (&internal_error);
+ if (internal_error) {
+ g_propagate_error (error, internal_error);
+ return FALSE;
}
+ tracker_db_interface_execute_query (iface, &internal_error,
+ "CREATE TABLE Graph (ID INTEGER NOT NULL PRIMARY KEY)");
+
if (internal_error) {
g_propagate_error (error, internal_error);
return FALSE;
}
- if (!query_table_exists (iface, "Graph", &internal_error) && !internal_error) {
- tracker_db_interface_execute_query (iface, &internal_error,
- "CREATE TABLE Graph (ID INTEGER NOT NULL PRIMARY KEY)");
- }
+ tracker_db_interface_execute_query (iface, &internal_error,
+ "CREATE TABLE metadata (key TEXT NOT NULL PRIMARY KEY, value TEXT)");
if (internal_error) {
g_propagate_error (error, internal_error);
@@ -3677,55 +3490,62 @@ create_base_tables (TrackerDataManager *manager,
return TRUE;
}
-static void
-tracker_data_ontology_import_into_db (TrackerDataManager *manager,
- gboolean in_update,
- GError **error)
+static gboolean
+tracker_data_ontology_setup_db (TrackerDataManager *manager,
+ TrackerDBInterface *iface,
+ const gchar *database,
+ gboolean in_update,
+ GError **error)
{
- TrackerDBInterface *iface;
-
+ GError *internal_error = NULL;
TrackerClass **classes;
- TrackerProperty **properties;
- guint i, n_props, n_classes;
- gboolean base_tables_altered = FALSE;
-#if HAVE_TRACKER_FTS
- gboolean update_fts = FALSE;
-#endif
-
- iface = tracker_db_manager_get_writable_db_interface (manager->db_manager);
+ guint i, n_classes;
- classes = tracker_ontologies_get_classes (manager->ontologies, &n_classes);
- properties = tracker_ontologies_get_properties (manager->ontologies, &n_props);
+ tracker_db_interface_execute_query (iface, &internal_error,
+ "CREATE TABLE IF NOT EXISTS "
+ " \"%s\".Refcount (ID INTEGER NOT NULL PRIMARY KEY,"
+ " Refcount INTEGER DEFAULT 0)",
+ database);
- if (!create_base_tables (manager, iface, &base_tables_altered, error)) {
- return;
+ if (internal_error) {
+ g_propagate_error (error, internal_error);
+ return FALSE;
}
-#if HAVE_TRACKER_FTS
- if (base_tables_altered || in_update) {
- update_fts = base_tables_altered | tracker_data_manager_fts_changed (manager);
-
- if (update_fts)
- tracker_db_interface_sqlite_fts_delete_table (iface);
- }
-#endif
+ classes = tracker_ontologies_get_classes (manager->ontologies, &n_classes);
/* create tables */
for (i = 0; i < n_classes; i++) {
- GError *internal_error = NULL;
-
/* Also !is_new classes are processed, they might have new properties */
- create_decomposed_metadata_tables (manager, iface, classes[i], in_update,
- base_tables_altered ||
+ create_decomposed_metadata_tables (manager, iface, database, classes[i], in_update,
tracker_class_get_db_schema_changed (classes[i]),
&internal_error);
if (internal_error) {
g_propagate_error (error, internal_error);
- return;
+ return FALSE;
}
}
+ return TRUE;
+}
+
+static void
+tracker_data_ontology_import_into_db (TrackerDataManager *manager,
+ TrackerDBInterface *iface,
+ gboolean in_update,
+ GError **error)
+{
+ TrackerClass **classes;
+ TrackerProperty **properties;
+ guint i, n_props, n_classes;
+
+ if (!tracker_data_manager_update_union_views (manager, iface, NULL, error))
+ return;
+
+ classes = tracker_ontologies_get_classes (manager->ontologies, &n_classes);
+ properties = tracker_ontologies_get_properties (manager->ontologies, &n_props);
+
/* insert classes into rdfs:Resource table */
for (i = 0; i < n_classes; i++) {
if (tracker_class_get_is_new (classes[i]) == in_update) {
@@ -3760,13 +3580,6 @@ tracker_data_ontology_import_into_db (TrackerDataManager *manager,
}
}
-#if HAVE_TRACKER_FTS
- if (update_fts) {
- tracker_data_manager_update_fts (manager, iface);
- } else {
- tracker_data_manager_init_fts (iface, !in_update);
- }
-#endif
}
static gint
@@ -4033,7 +3846,10 @@ ontology_get_fts_properties (TrackerDataManager *manager,
list = g_list_prepend (NULL, (gpointer) name);
g_hash_table_insert (*fts_properties, (gpointer) table_name, list);
} else {
- list = g_list_append (list, (gpointer) name);
+ g_hash_table_steal (*fts_properties, (gpointer) table_name);
+ list = g_list_insert_sorted (list, (gpointer) name,
+ (GCompareFunc) strcmp);
+ g_hash_table_insert (*fts_properties, (gpointer) table_name, list);
}
}
}
@@ -4042,24 +3858,33 @@ static void
rebuild_fts_tokens (TrackerDataManager *manager,
TrackerDBInterface *iface)
{
+ GHashTableIter iter;
+ gchar *graph;
+
g_debug ("Rebuilding FTS tokens, this may take a moment...");
- tracker_db_interface_sqlite_fts_rebuild_tokens (iface);
- g_debug ("FTS tokens rebuilt");
+ tracker_db_interface_sqlite_fts_rebuild_tokens (iface, "main");
+
+ g_hash_table_iter_init (&iter, manager->graphs);
+ while (g_hash_table_iter_next (&iter, (gpointer*) &graph, NULL))
+ tracker_db_interface_sqlite_fts_rebuild_tokens (iface, graph);
+ g_debug ("FTS tokens rebuilt");
/* Update the stamp file */
tracker_db_manager_tokenizer_update (manager->db_manager);
}
-gboolean
-tracker_data_manager_init_fts (TrackerDBInterface *iface,
+static gboolean
+tracker_data_manager_init_fts (TrackerDataManager *manager,
+ TrackerDBInterface *iface,
+ const gchar *database,
gboolean create)
{
GHashTable *fts_props, *multivalued;
- TrackerDataManager *manager;
- manager = tracker_db_interface_get_user_data (iface);
ontology_get_fts_properties (manager, &fts_props, &multivalued);
- tracker_db_interface_sqlite_fts_init (iface, fts_props,
+ tracker_db_interface_sqlite_fts_init (iface,
+ database,
+ fts_props,
multivalued, create);
g_hash_table_unref (fts_props);
g_hash_table_unref (multivalued);
@@ -4068,12 +3893,15 @@ tracker_data_manager_init_fts (TrackerDBInterface *iface,
static void
tracker_data_manager_update_fts (TrackerDataManager *manager,
- TrackerDBInterface *iface)
+ TrackerDBInterface *iface,
+ const gchar *database)
{
GHashTable *fts_properties, *multivalued;
ontology_get_fts_properties (manager, &fts_properties, &multivalued);
- tracker_db_interface_sqlite_fts_alter_table (iface, fts_properties, multivalued);
+ tracker_db_interface_sqlite_fts_alter_table (iface, database,
+ fts_properties,
+ multivalued);
g_hash_table_unref (fts_properties);
g_hash_table_unref (multivalued);
}
@@ -4091,12 +3919,254 @@ tracker_data_manager_get_data_location (TrackerDataManager *manager)
return manager->data_location ? g_object_ref (manager->data_location) : NULL;
}
+gboolean
+tracker_data_manager_update_union_views (TrackerDataManager *manager,
+ TrackerDBInterface *iface,
+ GHashTable *tables,
+ GError **error)
+{
+ TrackerOntologies *ontologies = manager->ontologies;
+ TrackerClass **classes;
+ TrackerProperty **properties;
+ TrackerDBStatement *stmt;
+ guint i, n_classes, n_properties;
+ GError *inner_error = NULL;
+ GHashTableIter iter;
+ GHashTable *graphs;
+ gpointer graph_name, graph_id;
+ GString *str;
+ GHashTable *view_generations;
+ gpointer generation;
+
+ generation = GUINT_TO_POINTER (manager->generation);
+
+ classes = tracker_ontologies_get_classes (ontologies, &n_classes);
+ properties = tracker_ontologies_get_properties (ontologies, &n_properties);
+ graphs = tracker_data_manager_ensure_graphs (manager, iface, error);
+
+ if (!graphs)
+ return FALSE;
+
+ view_generations = g_object_get_data (G_OBJECT (iface),
+ "tracker-data-view-generations");
+
+ if (!view_generations) {
+ view_generations = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ g_object_set_data_full (G_OBJECT (iface),
+ "tracker-data-view-generations",
+ view_generations,
+ (GDestroyNotify) g_hash_table_unref);
+ }
+
+ for (i = 0; !inner_error && i < n_classes; i++) {
+ const gchar *name;
+
+ if (g_str_has_prefix (tracker_class_get_name (classes[i]), "xsd:"))
+ continue;
+
+ name = tracker_class_get_name (classes[i]);
+ if ((tables && !g_hash_table_contains (tables, name)) ||
+ g_hash_table_lookup (view_generations, name) == generation)
+ continue;
+
+ stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE, &inner_error,
+ "DROP VIEW IF EXISTS temp.\"unionGraph_%s\"",
+ tracker_class_get_name (classes[i]));
+ if (!stmt)
+ goto error;
+
+ tracker_db_statement_execute (stmt, NULL);
+ g_object_unref (stmt);
+
+ str = g_string_new (NULL);
+ g_string_append_printf (str,
+ "CREATE VIEW temp.\"unionGraph_%s\" AS "
+ "SELECT 0 AS graph, * FROM \"main\".\"%s\" ",
+ tracker_class_get_name (classes[i]),
+ tracker_class_get_name (classes[i]));
+
+ g_hash_table_iter_init (&iter, graphs);
+ while (g_hash_table_iter_next (&iter, &graph_name, &graph_id)) {
+ g_string_append_printf (str, "UNION ALL SELECT %d AS graph, * FROM \"%s\".\"%s\" ",
+ GPOINTER_TO_INT (graph_id),
+ (gchar *) graph_name,
+ tracker_class_get_name (classes[i]));
+ }
+
+ stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE, &inner_error,
+ "%s", str->str);
+ g_string_free (str, TRUE);
+ if (!stmt)
+ goto error;
+
+ tracker_db_statement_execute (stmt, &inner_error);
+ g_object_unref (stmt);
+
+ if (inner_error)
+ goto error;
+
+ g_hash_table_insert (view_generations,
+ g_strdup (tracker_class_get_name (classes[i])),
+ generation);
+ }
+
+ for (i = 0; !inner_error && i < n_properties; i++) {
+ TrackerClass *service;
+ gchar *name;
+
+ if (!tracker_property_get_multiple_values (properties[i]))
+ continue;
+
+ service = tracker_property_get_domain (properties[i]);
+ name = g_strdup_printf ("%s_%s",
+ tracker_class_get_name (service),
+ tracker_property_get_name (properties[i]));
+
+ if ((tables && !g_hash_table_contains (tables, name)) ||
+ g_hash_table_lookup (view_generations, name) == generation) {
+ g_free (name);
+ continue;
+ }
+
+ stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE, &inner_error,
+ "DROP VIEW IF EXISTS temp.\"unionGraph_%s_%s\"",
+ tracker_class_get_name (service),
+ tracker_property_get_name (properties[i]));
+ if (!stmt) {
+ g_free (name);
+ goto error;
+ }
+
+ tracker_db_statement_execute (stmt, NULL);
+ g_object_unref (stmt);
+
+ str = g_string_new (NULL);
+ g_string_append_printf (str,
+ "CREATE VIEW temp.\"unionGraph_%s_%s\" AS "
+ "SELECT 0 AS graph, * FROM \"main\".\"%s_%s\" ",
+ tracker_class_get_name (service),
+ tracker_property_get_name (properties[i]),
+ tracker_class_get_name (service),
+ tracker_property_get_name (properties[i]));
+
+ g_hash_table_iter_init (&iter, graphs);
+ while (g_hash_table_iter_next (&iter, &graph_name, &graph_id)) {
+ g_string_append_printf (str, "UNION ALL SELECT %d AS graph, * FROM \"%s\".\"%s_%s\" ",
+ GPOINTER_TO_INT (graph_id),
+ (gchar *) graph_name,
+ tracker_class_get_name (service),
+ tracker_property_get_name (properties[i]));
+ }
+
+ stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE, &inner_error,
+ "%s", str->str);
+ g_string_free (str, TRUE);
+
+ if (!stmt) {
+ g_free (name);
+ goto error;
+ }
+
+ tracker_db_statement_execute (stmt, &inner_error);
+ g_object_unref (stmt);
+
+ if (inner_error) {
+ g_free (name);
+ goto error;
+ }
+
+ g_hash_table_insert (view_generations, name, generation);
+ }
+
+ /* Update FTS5 union view */
+ if ((!tables || g_hash_table_contains (tables, "fts5")) &&
+ g_hash_table_lookup (view_generations, "fts5") != generation) {
+ stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE, &inner_error,
+ "DROP VIEW IF EXISTS temp.\"unionGraph_fts5\"");
+ if (!stmt)
+ goto error;
+
+ tracker_db_statement_execute (stmt, NULL);
+ g_object_unref (stmt);
+
+ str = g_string_new (NULL);
+ g_string_append (str,
+ "CREATE VIEW temp.\"unionGraph_fts5\" AS "
+ "SELECT 0 AS graph, ROWID, *, fts5, rank FROM \"main\".\"fts5\" ");
+
+ g_hash_table_iter_init (&iter, graphs);
+ while (g_hash_table_iter_next (&iter, &graph_name, &graph_id)) {
+ g_string_append_printf (str, "UNION ALL SELECT %d AS graph, ROWID, *, fts5, rank FROM \"%s\".\"fts5\" ",
+ GPOINTER_TO_INT (graph_id),
+ (gchar *) graph_name);
+ }
+
+ stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE, &inner_error,
+ "%s", str->str);
+ g_string_free (str, TRUE);
+
+ if (stmt) {
+ tracker_db_statement_execute (stmt, &inner_error);
+ g_object_unref (stmt);
+ }
+
+ if (inner_error)
+ goto error;
+
+ g_hash_table_insert (view_generations, g_strdup ("fts5"), generation);
+ }
+
+ /* Refcounts */
+ if (g_hash_table_lookup (view_generations, "refcount") != generation) {
+ stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE, &inner_error,
+ "DROP VIEW IF EXISTS temp.\"unionGraph_Refcount\"");
+ if (!stmt)
+ goto error;
+
+ tracker_db_statement_execute (stmt, NULL);
+ g_object_unref (stmt);
+
+ str = g_string_new (NULL);
+ g_string_append (str,
+ "CREATE VIEW temp.\"unionGraph_Refcount\" AS "
+ "SELECT 0 AS graph, * FROM \"main\".\"Refcount\" ");
+
+ g_hash_table_iter_init (&iter, graphs);
+ while (g_hash_table_iter_next (&iter, &graph_name, &graph_id)) {
+ g_string_append_printf (str, "UNION ALL SELECT %d AS graph, * FROM \"%s\".\"Refcount\" ",
+ GPOINTER_TO_INT (graph_id),
+ (gchar *) graph_name);
+ }
+
+ stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE, &inner_error,
+ "%s", str->str);
+ g_string_free (str, TRUE);
+
+ if (stmt) {
+ tracker_db_statement_execute (stmt, &inner_error);
+ g_object_unref (stmt);
+ }
+
+ if (inner_error)
+ goto error;
+
+ g_hash_table_insert (view_generations, g_strdup ("refcount"), generation);
+ }
+
+error:
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
TrackerDataManager *
tracker_data_manager_new (TrackerDBManagerFlags flags,
GFile *cache_location,
GFile *data_location,
GFile *ontology_location,
- gboolean journal_check,
gboolean restoring_backup,
guint select_cache_size,
guint update_cache_size)
@@ -4115,7 +4185,6 @@ tracker_data_manager_new (TrackerDBManagerFlags flags,
g_set_object (&manager->ontology_location, ontology_location);
g_set_object (&manager->data_location, data_location);
manager->flags = flags;
- manager->journal_check = journal_check;
manager->restoring_backup = restoring_backup;
manager->select_cache_size = select_cache_size;
manager->update_cache_size = update_cache_size;
@@ -4150,6 +4219,163 @@ update_ontology_last_modified (TrackerDataManager *manager,
}
static gboolean
+tracker_data_manager_initialize_iface (TrackerDataManager *data_manager,
+ TrackerDBInterface *iface,
+ GError **error)
+{
+ GHashTable *graphs;
+
+ graphs = tracker_data_manager_ensure_graphs (data_manager, iface, NULL);
+
+ if (graphs) {
+ GHashTableIter iter;
+ gpointer value;
+
+ g_hash_table_iter_init (&iter, graphs);
+
+ while (g_hash_table_iter_next (&iter, &value, NULL)) {
+ if (!tracker_db_manager_attach_database (data_manager->db_manager,
+ iface, value, FALSE,
+ error)) {
+ return FALSE;
+ }
+
+#if HAVE_TRACKER_FTS
+ tracker_data_manager_init_fts (data_manager, iface, value, FALSE);
+#endif
+ }
+ }
+
+#if HAVE_TRACKER_FTS
+ tracker_data_manager_init_fts (data_manager, iface, "main", FALSE);
+#endif
+
+ return TRUE;
+}
+
+static void
+setup_interface_cb (TrackerDBManager *db_manager,
+ TrackerDBInterface *iface,
+ TrackerDataManager *data_manager)
+{
+ GError *error = NULL;
+ guint flags;
+
+ if (!tracker_data_manager_initialize_iface (data_manager, iface, &error)) {
+ g_critical ("Could not set up interface %p: %s",
+ iface, error->message);
+ g_error_free (error);
+ }
+
+ g_object_get (iface, "flags", &flags, NULL);
+
+ if (flags & TRACKER_DB_INTERFACE_READONLY) {
+ tracker_data_manager_update_union_views (data_manager, iface, NULL, NULL);
+ }
+}
+
+static gboolean
+update_attached_databases (TrackerDBInterface *iface,
+ TrackerDataManager *data_manager,
+ gboolean *changed,
+ GError **error)
+{
+ TrackerDBStatement *stmt;
+ TrackerDBCursor *cursor;
+ gboolean retval = TRUE;
+
+ *changed = FALSE;
+ stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT, error,
+ "SELECT name, 1, 0, 0 FROM pragma_database_list "
+ "WHERE name NOT IN (SELECT Uri FROM RESOURCE WHERE ID IN (SELECT ID FROM Graph))"
+ "UNION "
+ "SELECT Uri, 0, 1, ID FROM Resource "
+ "WHERE ID IN (SELECT ID FROM Graph) "
+ "AND Uri NOT IN (SELECT name FROM pragma_database_list)");
+
+ if (!stmt)
+ return FALSE;
+
+ cursor = tracker_db_statement_start_cursor (stmt, error);
+ g_object_unref (stmt);
+
+ if (!cursor)
+ return FALSE;
+
+ while (retval && tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
+ const gchar *name;
+
+ name = tracker_db_cursor_get_string (cursor, 0, NULL);
+
+ /* Ignore the main and temp databases, they are always attached */
+ if (strcmp (name, "main") == 0 || strcmp (name, "temp") == 0)
+ continue;
+
+ if (tracker_db_cursor_get_int (cursor, 1)) {
+ if (!tracker_db_manager_detach_database (data_manager->db_manager,
+ iface, name, error)) {
+ retval = FALSE;
+ break;
+ }
+
+ g_hash_table_remove (data_manager->graphs, name);
+ *changed = TRUE;
+ } else if (tracker_db_cursor_get_int (cursor, 2)) {
+ if (!tracker_db_manager_attach_database (data_manager->db_manager,
+ iface, name, FALSE, error)) {
+ retval = FALSE;
+ break;
+ }
+
+ g_hash_table_insert (data_manager->graphs, g_strdup (name),
+ GINT_TO_POINTER (tracker_db_cursor_get_int (cursor, 3)));
+ *changed = TRUE;
+ }
+ }
+
+ g_object_unref (cursor);
+
+ return retval;
+}
+
+static void
+update_interface_cb (TrackerDBManager *db_manager,
+ TrackerDBInterface *iface,
+ TrackerDataManager *data_manager)
+{
+ GError *error = NULL;
+ guint iface_generation;
+ gboolean update = FALSE, changed, readonly;
+
+ readonly = (tracker_db_manager_get_flags (db_manager, NULL, NULL) & TRACKER_DB_MANAGER_READONLY) != 0;
+
+ if (readonly) {
+ update = TRUE;
+ } else {
+ iface_generation = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (iface),
+ "tracker-data-iface-generation"));
+ update = (iface_generation != data_manager->generation);
+ }
+
+ if (update) {
+ if (update_attached_databases (iface, data_manager, &changed, &error)) {
+ /* This is where we bump the generation for the readonly case, in response to
+ * tables being attached or detached due to graph changes.
+ */
+ if (readonly && changed)
+ data_manager->generation++;
+ } else {
+ g_critical ("Could not update attached databases: %s\n",
+ error->message);
+ g_error_free (error);
+ }
+
+ g_object_set_data (G_OBJECT (iface), "tracker-data-iface-generation",
+ GUINT_TO_POINTER (data_manager->generation));
+ }
+}
+
+static gboolean
check_db_consistency (TrackerDBInterface *iface)
{
TrackerDBStatement *stmt;
@@ -4186,14 +4412,12 @@ tracker_data_manager_initable_init (GInitable *initable,
TrackerDBCursor *cursor;
TrackerDBStatement *stmt;
GHashTable *ontos_table;
+ GHashTable *graphs;
GList *sorted = NULL, *l;
gint max_id = 0;
gboolean read_only;
GHashTable *uri_id_map = NULL;
GError *internal_error = NULL;
-#ifndef DISABLE_JOURNAL
- gboolean read_journal;
-#endif
if (manager->initialized) {
return TRUE;
@@ -4209,9 +4433,6 @@ tracker_data_manager_initable_init (GInitable *initable,
}
read_only = (manager->flags & TRACKER_DB_MANAGER_READONLY) ? TRUE : FALSE;
-#ifndef DISABLE_JOURNAL
- read_journal = FALSE;
-#endif
/* Make sure we initialize all other modules we depend on */
manager->data_update = tracker_data_new (manager);
@@ -4234,6 +4455,11 @@ tracker_data_manager_initable_init (GInitable *initable,
return FALSE;
}
+ g_signal_connect (manager->db_manager, "setup-interface",
+ G_CALLBACK (setup_interface_cb), manager);
+ g_signal_connect (manager->db_manager, "update-interface",
+ G_CALLBACK (update_interface_cb), manager);
+
manager->first_time_index = is_first_time_index;
tracker_data_manager_update_status (manager, "Initializing data manager");
@@ -4250,34 +4476,6 @@ tracker_data_manager_initable_init (GInitable *initable,
return FALSE;
}
-#ifndef DISABLE_JOURNAL
- if (manager->journal_check && is_first_time_index) {
- TrackerDBJournalReader *journal_reader;
-
- /* Call may fail without notice (it's handled) */
- journal_reader = tracker_db_journal_reader_new (manager->data_location, &internal_error);
-
- if (journal_reader) {
- if (tracker_db_journal_reader_next (journal_reader, NULL)) {
- /* journal with at least one valid transaction
- is required to trigger journal replay */
- read_journal = TRUE;
- }
-
- tracker_db_journal_reader_free (journal_reader);
- } else if (internal_error) {
- if (!g_error_matches (internal_error,
- TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_BEGIN_OF_JOURNAL)) {
- g_propagate_error (error, internal_error);
- return FALSE;
- } else {
- g_clear_error (&internal_error);
- }
- }
- }
-#endif /* DISABLE_JOURNAL */
-
if (g_file_query_file_type (manager->ontology_location, G_FILE_QUERY_INFO_NONE, NULL) != G_FILE_TYPE_DIRECTORY) {
gchar *uri;
@@ -4289,38 +4487,6 @@ tracker_data_manager_initable_init (GInitable *initable,
return FALSE;
}
-#ifndef DISABLE_JOURNAL
- if (read_journal) {
- TrackerDBJournalReader *journal_reader;
-
- manager->in_journal_replay = TRUE;
- journal_reader = tracker_db_journal_reader_ontology_new (manager->data_location, &internal_error);
-
- if (journal_reader) {
- /* Load ontology IDs from journal into memory */
- load_ontology_ids_from_journal (journal_reader, &uri_id_map, &max_id);
- tracker_db_journal_reader_free (journal_reader);
- } else {
- if (internal_error) {
- if (!g_error_matches (internal_error,
- TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_BEGIN_OF_JOURNAL)) {
- g_propagate_error (error, internal_error);
- return FALSE;
- } else {
- g_clear_error (&internal_error);
- }
- }
-
- /* do not trigger journal replay if ontology journal
- does not exist or is not valid,
- same as with regular journal further above */
- manager->in_journal_replay = FALSE;
- read_journal = FALSE;
- }
- }
-#endif /* DISABLE_JOURNAL */
-
if (is_first_time_index && !read_only) {
sorted = get_ontologies (manager, manager->ontology_location, &internal_error);
@@ -4329,15 +4495,6 @@ tracker_data_manager_initable_init (GInitable *initable,
return FALSE;
}
-#ifndef DISABLE_JOURNAL
- manager->ontology_writer = tracker_db_journal_ontology_new (manager->data_location, &internal_error);
-
- if (internal_error) {
- g_propagate_error (error, internal_error);
- return FALSE;
- }
-#endif /* DISABLE_JOURNAL */
-
/* load ontology from files into memory (max_id starts at zero: first-time) */
for (l = sorted; l; l = l->next) {
@@ -4368,37 +4525,37 @@ tracker_data_manager_initable_init (GInitable *initable,
return FALSE;
}
- tracker_data_ontology_import_into_db (manager, FALSE,
- &internal_error);
+ if (!create_base_tables (manager, iface, error)) {
+ return FALSE;
+ }
+
+ tracker_data_ontology_setup_db (manager, iface, "main", FALSE,
+ &internal_error);
+
+ if (!internal_error) {
+ tracker_data_ontology_import_into_db (manager, iface,
+ FALSE,
+ &internal_error);
+ }
if (internal_error) {
g_propagate_error (error, internal_error);
return FALSE;
}
-#ifndef DISABLE_JOURNAL
- if (uri_id_map) {
- /* restore all IDs from ontology journal */
- GHashTableIter iter;
- gpointer key, value;
-
- g_hash_table_iter_init (&iter, uri_id_map);
- while (g_hash_table_iter_next (&iter, &key, &value)) {
- insert_uri_in_resource_table (manager, iface,
- key,
- GPOINTER_TO_INT (value),
- &internal_error);
- if (internal_error) {
- g_propagate_error (error, internal_error);
- return FALSE;
- }
- }
+#if HAVE_TRACKER_FTS
+ tracker_data_manager_init_fts (manager, iface, "main", TRUE);
+#endif
+
+ tracker_data_manager_initialize_iface (manager, iface, &internal_error);
+ if (internal_error) {
+ g_propagate_error (error, internal_error);
+ return FALSE;
}
-#endif /* DISABLE_JOURNAL */
/* store ontology in database */
for (l = sorted; l; l = l->next) {
- import_ontology_file (manager, l->data, FALSE, !manager->journal_check);
+ import_ontology_file (manager, l->data, FALSE);
}
tracker_data_commit_transaction (manager->data_update, &internal_error);
@@ -4417,16 +4574,6 @@ tracker_data_manager_initable_init (GInitable *initable,
check_ontology = FALSE;
} else {
if (!read_only) {
-
-#ifndef DISABLE_JOURNAL
- manager->ontology_writer = tracker_db_journal_ontology_new (manager->data_location, &internal_error);
-
- if (internal_error) {
- g_propagate_error (error, internal_error);
- return FALSE;
- }
-#endif /* DISABLE_JOURNAL */
-
/* Load ontology from database into memory */
db_get_static_data (iface, manager, &internal_error);
check_ontology = (manager->flags & TRACKER_DB_MANAGER_DO_NOT_CHECK_ONTOLOGY) == 0;
@@ -4437,10 +4584,6 @@ tracker_data_manager_initable_init (GInitable *initable,
}
write_ontologies_gvdb (manager, FALSE /* overwrite */, NULL);
-
- /* Skipped in the read-only case as it can't work with direct access and
- it reduces initialization time */
- clean_decomposed_transient_metadata (manager, iface);
} else {
GError *gvdb_error = NULL;
@@ -4459,9 +4602,11 @@ tracker_data_manager_initable_init (GInitable *initable,
}
}
-#if HAVE_TRACKER_FTS
- tracker_data_manager_init_fts (iface, FALSE);
-#endif
+ tracker_data_manager_initialize_iface (manager, iface, &internal_error);
+ if (internal_error) {
+ g_propagate_error (error, internal_error);
+ return FALSE;
+ }
}
if (!read_only) {
@@ -4741,8 +4886,61 @@ tracker_data_manager_initable_init (GInitable *initable,
if (!ontology_error) {
/* Perform ALTER-TABLE and CREATE-TABLE calls for all that are is_new */
- tracker_data_ontology_import_into_db (manager, TRUE,
- &ontology_error);
+#if HAVE_TRACKER_FTS
+ gboolean update_fts;
+
+ update_fts = tracker_data_manager_fts_changed (manager);
+
+ if (update_fts)
+ tracker_db_interface_sqlite_fts_delete_table (iface, "main");
+#endif
+
+ tracker_data_ontology_setup_db (manager, iface, "main", TRUE,
+ &ontology_error);
+
+ graphs = tracker_data_manager_ensure_graphs (manager, iface, &ontology_error);
+
+ if (graphs) {
+ GHashTableIter iter;
+ gpointer value;
+
+ g_hash_table_iter_init (&iter, graphs);
+
+ while (g_hash_table_iter_next (&iter, &value, NULL)) {
+#if HAVE_TRACKER_FTS
+ if (update_fts)
+ tracker_db_interface_sqlite_fts_delete_table (iface, value);
+#endif
+
+ tracker_data_ontology_setup_db (manager, iface, value, TRUE,
+ &ontology_error);
+ if (ontology_error)
+ break;
+
+#if HAVE_TRACKER_FTS
+ if (update_fts) {
+ tracker_data_manager_update_fts (manager, iface, value);
+ } else {
+ tracker_data_manager_init_fts (manager, iface, value, FALSE);
+ }
+#endif
+ }
+ }
+
+ if (!ontology_error) {
+#if HAVE_TRACKER_FTS
+ if (update_fts) {
+ tracker_data_manager_update_fts (manager, iface, "main");
+ } else {
+ tracker_data_manager_init_fts (manager, iface, "main", FALSE);
+ }
+#endif
+ }
+
+ if (!ontology_error) {
+ tracker_data_ontology_import_into_db (manager, iface, TRUE,
+ &ontology_error);
+ }
if (!ontology_error) {
tracker_data_ontology_process_changes_post_db (manager,
@@ -4787,7 +4985,7 @@ tracker_data_manager_initable_init (GInitable *initable,
for (l = to_reload; l; l = l->next) {
GFile *ontology_file = l->data;
/* store ontology in database */
- import_ontology_file (manager, ontology_file, TRUE, !manager->journal_check);
+ import_ontology_file (manager, ontology_file, TRUE);
}
g_list_free (to_reload);
@@ -4808,11 +5006,6 @@ tracker_data_manager_initable_init (GInitable *initable,
g_propagate_error (error, internal_error);
return FALSE;
}
-
-#ifndef DISABLE_JOURNAL
- tracker_db_journal_free (manager->ontology_writer, NULL);
- manager->ontology_writer = NULL;
-#endif /* DISABLE_JOURNAL */
}
g_hash_table_unref (ontos_table);
@@ -4835,52 +5028,16 @@ tracker_data_manager_initable_init (GInitable *initable,
}
}
-skip_ontology_check:
-
-#ifndef DISABLE_JOURNAL
- if (read_journal) {
- /* Start replay */
- tracker_data_replay_journal (manager->data_update,
- busy_callback,
- manager,
- "Replaying journal",
- &internal_error);
-
- if (internal_error) {
- if (g_error_matches (internal_error, TRACKER_DB_INTERFACE_ERROR, TRACKER_DB_NO_SPACE)) {
- GError *n_error = NULL;
- tracker_db_manager_remove_all (manager->db_manager);
- g_clear_pointer (&manager->db_manager, tracker_db_manager_free);
- /* Call may fail without notice, we're in error handling already.
- * When fails it means that close() of journal file failed. */
- if (n_error) {
- g_warning ("Error closing journal: %s",
- n_error->message ? n_error->message : "No error given");
- g_error_free (n_error);
- }
- }
-
- g_hash_table_unref (uri_id_map);
- g_propagate_error (error, internal_error);
- return FALSE;
- }
-
- manager->in_journal_replay = FALSE;
- g_hash_table_unref (uri_id_map);
- }
-
- /* open journal for writing */
- manager->journal_writer = tracker_db_journal_new (manager->data_location, FALSE, &internal_error);
-
- if (internal_error) {
- g_propagate_error (error, internal_error);
+ if (!tracker_data_manager_update_union_views (manager, iface, NULL, error))
return FALSE;
- }
-#endif /* DISABLE_JOURNAL */
- /* If locale changed, re-create indexes */
- if (!read_only && tracker_db_manager_locale_changed (manager->db_manager, NULL)) {
- /* No need to reset the collator in the db interface,
+skip_ontology_check:
+ if (!read_only && is_first_time_index) {
+ tracker_db_manager_set_current_locale (manager->db_manager);
+ tracker_db_manager_tokenizer_update (manager->db_manager);
+ } else if (!read_only && tracker_db_manager_locale_changed (manager->db_manager, NULL)) {
+ /* If locale changed, re-create indexes.
+ * No need to reset the collator in the db interface,
* as this is only executed during startup, which should
* already have the proper locale set in the collator */
tracker_data_manager_recreate_indexes (manager, &internal_error);
@@ -4899,13 +5056,6 @@ skip_ontology_check:
#endif
}
-#ifndef DISABLE_JOURNAL
- if (manager->ontology_writer) {
- tracker_db_journal_free (manager->ontology_writer, NULL);
- manager->ontology_writer = NULL;
- }
-#endif /* DISABLE_JOURNAL */
-
if (!read_only) {
tracker_ontologies_sort (manager->ontologies);
}
@@ -4951,8 +5101,10 @@ data_manager_check_perform_cleanup (TrackerDataManager *manager)
count = 0;
stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE, NULL,
- "SELECT COUNT(*) FROM Resource WHERE Refcount <= 0 "
- "AND Resource.ID > %d AND Resource.ID NOT IN (SELECT ID FROM Graph)",
+ "SELECT COUNT(*) FROM Resource "
+ "WHERE Resource.ID > %d "
+ "AND Resource.ID NOT IN (SELECT ROWID FROM unionGraph_Refcount) "
+ "AND Resource.ID NOT IN (SELECT ID FROM Graph)",
TRACKER_ONTOLOGIES_MAX_ID);
if (stmt) {
cursor = tracker_db_statement_start_cursor (stmt, NULL);
@@ -4986,8 +5138,10 @@ tracker_data_manager_dispose (GObject *object)
iface = tracker_db_manager_get_writable_db_interface (manager->db_manager);
stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE,
&error,
- "DELETE FROM Resource WHERE Refcount <= 0 "
- "AND Resource.ID > %d AND Resource.ID NOT IN (SELECT ID FROM Graph)",
+ "DELETE FROM Resource "
+ "WHERE Resource.ID > %d "
+ "AND Resource.ID NOT IN (SELECT ROWID FROM unionGraph_Refcount) "
+ "AND Resource.ID NOT IN (SELECT ID FROM Graph)",
TRACKER_ONTOLOGIES_MAX_ID);
if (stmt) {
@@ -5004,7 +5158,7 @@ tracker_data_manager_dispose (GObject *object)
tracker_db_manager_check_perform_vacuum (manager->db_manager);
}
- g_clear_pointer (&manager->db_manager, tracker_db_manager_free);
+ g_clear_object (&manager->db_manager);
}
G_OBJECT_CLASS (tracker_data_manager_parent_class)->dispose (object);
@@ -5014,31 +5168,10 @@ void
tracker_data_manager_finalize (GObject *object)
{
TrackerDataManager *manager = TRACKER_DATA_MANAGER (object);
-#ifndef DISABLE_JOURNAL
- GError *error = NULL;
-
- /* Make sure we shutdown all other modules we depend on */
- if (manager->journal_writer) {
- tracker_db_journal_free (manager->journal_writer, &error);
- manager->journal_writer = NULL;
- if (error) {
- g_warning ("While shutting down journal %s", error->message);
- g_clear_error (&error);
- }
- }
-
- if (manager->ontology_writer) {
- tracker_db_journal_free (manager->ontology_writer, &error);
- manager->ontology_writer = NULL;
- if (error) {
- g_warning ("While shutting down ontology journal %s", error->message);
- g_clear_error (&error);
- }
- }
-#endif /* DISABLE_JOURNAL */
g_clear_object (&manager->ontologies);
g_clear_object (&manager->data_update);
+ g_clear_pointer (&manager->graphs, g_hash_table_unref);
g_free (manager->status);
G_OBJECT_CLASS (tracker_data_manager_parent_class)->finalize (object);
@@ -5086,20 +5219,6 @@ tracker_data_manager_class_init (TrackerDataManagerClass *klass)
G_PARAM_READABLE));
}
-#ifndef DISABLE_JOURNAL
-TrackerDBJournal *
-tracker_data_manager_get_journal_writer (TrackerDataManager *manager)
-{
- return manager->journal_writer;
-}
-
-TrackerDBJournal *
-tracker_data_manager_get_ontology_writer (TrackerDataManager *manager)
-{
- return manager->ontology_writer;
-}
-#endif
-
TrackerOntologies *
tracker_data_manager_get_ontologies (TrackerDataManager *manager)
{
@@ -5154,3 +5273,256 @@ tracker_data_manager_get_namespaces (TrackerDataManager *manager)
return ht;
}
+
+gboolean
+tracker_data_manager_create_graph (TrackerDataManager *manager,
+ const gchar *name,
+ GError **error)
+{
+ TrackerDBInterface *iface;
+ gint id;
+
+
+ iface = tracker_db_manager_get_writable_db_interface (manager->db_manager);
+
+ if (!tracker_db_manager_attach_database (manager->db_manager, iface,
+ name, TRUE, error))
+ return FALSE;
+
+ if (!tracker_data_ontology_setup_db (manager, iface, name,
+ FALSE, error))
+ goto detach;
+
+#if HAVE_TRACKER_FTS
+ tracker_data_manager_init_fts (manager, iface, name, TRUE);
+#endif
+
+ id = tracker_data_ensure_graph (manager->data_update, name, error);
+ if (id == 0)
+ goto detach;
+
+ g_hash_table_insert (manager->graphs, g_strdup (name), GINT_TO_POINTER (id));
+
+ manager->generation++;
+
+ if (!tracker_data_manager_update_union_views (manager, iface, NULL, error))
+ goto detach;
+
+ return TRUE;
+
+detach:
+ tracker_db_manager_detach_database (manager->db_manager, iface, name, NULL);
+ return FALSE;
+}
+
+gboolean
+tracker_data_manager_drop_graph (TrackerDataManager *manager,
+ const gchar *name,
+ GError **error)
+{
+ TrackerDBInterface *iface;
+
+ iface = tracker_db_manager_get_writable_db_interface (manager->db_manager);
+
+ /* Silently refuse to drop the main graph, clear it instead */
+ if (!name)
+ return tracker_data_manager_clear_graph (manager, name, error);
+
+ /* Ensure the current transaction doesn't keep tables in this database locked */
+ tracker_data_commit_transaction (manager->data_update, NULL);
+ tracker_data_begin_transaction (manager->data_update, NULL);
+
+ if (!tracker_db_manager_detach_database (manager->db_manager, iface,
+ name, error))
+ return FALSE;
+
+ if (!tracker_data_delete_graph (manager->data_update, name, error))
+ return FALSE;
+
+ manager->generation++;
+
+ if (!tracker_data_manager_update_union_views (manager, iface, NULL, error))
+ return FALSE;
+
+ if (manager->graphs)
+ g_hash_table_remove (manager->graphs, name);
+
+ return TRUE;
+}
+
+gint
+tracker_data_manager_find_graph (TrackerDataManager *manager,
+ const gchar *name)
+{
+ TrackerDBInterface *iface;
+ GHashTable *graphs;
+
+ iface = tracker_db_manager_get_writable_db_interface (manager->db_manager);
+ graphs = tracker_data_manager_ensure_graphs (manager, iface, NULL);
+
+ if (!graphs)
+ return 0;
+
+ return GPOINTER_TO_UINT (g_hash_table_lookup (graphs, name));
+}
+
+const gchar *
+tracker_data_manager_find_graph_by_id (TrackerDataManager *manager,
+ gint id)
+{
+ TrackerDBInterface *iface;
+ GHashTableIter iter;
+ GHashTable *graphs;
+ gpointer key, value;
+
+ iface = tracker_db_manager_get_writable_db_interface (manager->db_manager);
+ graphs = tracker_data_manager_ensure_graphs (manager, iface, NULL);
+
+ if (!graphs)
+ return NULL;
+
+ g_hash_table_iter_init (&iter, graphs);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ if (id == GPOINTER_TO_INT (value))
+ return key;
+ }
+
+ return NULL;
+}
+
+gboolean
+tracker_data_manager_clear_graph (TrackerDataManager *manager,
+ const gchar *graph,
+ GError **error)
+{
+ TrackerOntologies *ontologies = manager->ontologies;
+ TrackerClass **classes;
+ TrackerProperty **properties;
+ TrackerDBStatement *stmt;
+ guint i, n_classes, n_properties;
+ GError *inner_error = NULL;
+ TrackerDBInterface *iface;
+
+ if (!graph)
+ graph = "main";
+
+ iface = tracker_db_manager_get_writable_db_interface (manager->db_manager);
+ classes = tracker_ontologies_get_classes (ontologies, &n_classes);
+ properties = tracker_ontologies_get_properties (ontologies, &n_properties);
+
+ for (i = 0; !inner_error && i < n_classes; i++) {
+ if (g_str_has_prefix (tracker_class_get_name (classes[i]), "xsd:"))
+ continue;
+
+ stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE, &inner_error,
+ "DELETE FROM \"%s\".\"%s\" WHERE ID > 100000",
+ graph,
+ tracker_class_get_name (classes[i]));
+ if (!stmt)
+ break;
+
+ tracker_db_statement_execute (stmt, &inner_error);
+ g_object_unref (stmt);
+ }
+
+ for (i = 0; !inner_error && i < n_properties; i++) {
+ TrackerClass *service;
+
+ if (!tracker_property_get_multiple_values (properties[i]))
+ continue;
+
+ service = tracker_property_get_domain (properties[i]);
+ stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE, &inner_error,
+ "DELETE FROM \"%s\".\"%s_%s\" WHERE ID > 100000",
+ graph,
+ tracker_class_get_name (service),
+ tracker_property_get_name (properties[i]));
+ if (!stmt)
+ break;
+
+ tracker_db_statement_execute (stmt, &inner_error);
+ g_object_unref (stmt);
+ }
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+tracker_data_manager_copy_graph (TrackerDataManager *manager,
+ const gchar *source,
+ const gchar *destination,
+ GError **error)
+{
+ TrackerOntologies *ontologies = manager->ontologies;
+ TrackerClass **classes;
+ TrackerProperty **properties;
+ TrackerDBStatement *stmt;
+ guint i, n_classes, n_properties;
+ GError *inner_error = NULL;
+ TrackerDBInterface *iface;
+
+ if (!source)
+ source = "main";
+ if (!destination)
+ destination = "main";
+
+ if (strcmp (source, destination) == 0)
+ return TRUE;
+
+ iface = tracker_db_manager_get_writable_db_interface (manager->db_manager);
+ classes = tracker_ontologies_get_classes (ontologies, &n_classes);
+ properties = tracker_ontologies_get_properties (ontologies, &n_properties);
+
+ for (i = 0; !inner_error && i < n_classes; i++) {
+ if (g_str_has_prefix (tracker_class_get_name (classes[i]), "xsd:"))
+ continue;
+
+ stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE, &inner_error,
+ "INSERT OR REPLACE INTO \"%s\".\"%s\" "
+ "SELECT * from \"%s\".\"%s\" WHERE ID > 100000",
+ destination,
+ tracker_class_get_name (classes[i]),
+ source,
+ tracker_class_get_name (classes[i]));
+ if (!stmt)
+ break;
+
+ tracker_db_statement_execute (stmt, &inner_error);
+ g_object_unref (stmt);
+ }
+
+ for (i = 0; !inner_error && i < n_properties; i++) {
+ TrackerClass *service;
+
+ if (!tracker_property_get_multiple_values (properties[i]))
+ continue;
+
+ service = tracker_property_get_domain (properties[i]);
+ stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE, &inner_error,
+ "INSERT OR REPLACE INTO \"%s\".\"%s_%s\" "
+ "SELECT * from \"%s\".\"%s_%s\" WHERE ID > 100000",
+ destination,
+ tracker_class_get_name (service),
+ tracker_property_get_name (properties[i]),
+ source,
+ tracker_class_get_name (service),
+ tracker_property_get_name (properties[i]));
+ if (!stmt)
+ break;
+
+ tracker_db_statement_execute (stmt, &inner_error);
+ g_object_unref (stmt);
+ }
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/src/libtracker-data/tracker-data-manager.h b/src/libtracker-data/tracker-data-manager.h
index 5c6fb0e05..7170e01eb 100644
--- a/src/libtracker-data/tracker-data-manager.h
+++ b/src/libtracker-data/tracker-data-manager.h
@@ -40,7 +40,6 @@ typedef struct _TrackerDataManagerClass TrackerDataManagerClass;
#include <libtracker-data/tracker-data-update.h>
#include <libtracker-data/tracker-db-interface.h>
#include <libtracker-data/tracker-db-manager.h>
-#include <libtracker-data/tracker-db-journal.h>
#define TRACKER_DATA_ONTOLOGY_ERROR (tracker_data_ontology_error_quark ())
@@ -64,7 +63,6 @@ TrackerDataManager * tracker_data_manager_new (TrackerDBManagerFlags fl
GFile *cache_location,
GFile *data_location,
GFile *ontology_location,
- gboolean journal_check,
gboolean restoring_backup,
guint select_cache_size,
guint update_cache_size);
@@ -73,8 +71,6 @@ void tracker_data_manager_shutdown (TrackerDataManage
GFile * tracker_data_manager_get_cache_location (TrackerDataManager *manager);
GFile * tracker_data_manager_get_data_location (TrackerDataManager *manager);
-TrackerDBJournal * tracker_data_manager_get_journal_writer (TrackerDataManager *manager);
-TrackerDBJournal * tracker_data_manager_get_ontology_writer (TrackerDataManager *manager);
TrackerOntologies * tracker_data_manager_get_ontologies (TrackerDataManager *manager);
TrackerDBManager * tracker_data_manager_get_db_manager (TrackerDataManager *manager);
@@ -82,11 +78,36 @@ TrackerDBInterface * tracker_data_manager_get_db_interface (TrackerDataManage
TrackerDBInterface * tracker_data_manager_get_writable_db_interface (TrackerDataManager *manager);
TrackerData * tracker_data_manager_get_data (TrackerDataManager *manager);
-gboolean tracker_data_manager_init_fts (TrackerDBInterface *interface,
- gboolean create);
-
GHashTable * tracker_data_manager_get_namespaces (TrackerDataManager *manager);
+gboolean tracker_data_manager_create_graph (TrackerDataManager *manager,
+ const gchar *name,
+ GError **error);
+
+gboolean tracker_data_manager_drop_graph (TrackerDataManager *manager,
+ const gchar *name,
+ GError **error);
+
+gboolean tracker_data_manager_clear_graph (TrackerDataManager *manager,
+ const gchar *graph,
+ GError **error);
+gboolean tracker_data_manager_copy_graph (TrackerDataManager *manager,
+ const gchar *source,
+ const gchar *destination,
+ GError **error);
+
+GHashTable * tracker_data_manager_get_graphs (TrackerDataManager *manager);
+
+gint tracker_data_manager_find_graph (TrackerDataManager *manager,
+ const gchar *name);
+const gchar * tracker_data_manager_find_graph_by_id (TrackerDataManager *manager,
+ gint id);
+
+gboolean tracker_data_manager_update_union_views (TrackerDataManager *manager,
+ TrackerDBInterface *iface,
+ GHashTable *tables,
+ GError **error);
+
G_END_DECLS
#endif /* __LIBTRACKER_DATA_MANAGER_H__ */
diff --git a/src/libtracker-data/tracker-data-query.c b/src/libtracker-data/tracker-data-query.c
index 2694d6d0b..ddde295e8 100644
--- a/src/libtracker-data/tracker-data-query.c
+++ b/src/libtracker-data/tracker-data-query.c
@@ -35,6 +35,7 @@
GPtrArray*
tracker_data_query_rdf_type (TrackerDataManager *manager,
+ const gchar *graph,
gint id)
{
TrackerDBCursor *cursor = NULL;
@@ -49,8 +50,9 @@ tracker_data_query_rdf_type (TrackerDataManager *manager,
stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT, &error,
"SELECT (SELECT Uri FROM Resource WHERE ID = \"rdf:type\") "
- "FROM \"rdfs:Resource_rdf:type\" "
- "WHERE ID = ?");
+ "FROM \"%s\".\"rdfs:Resource_rdf:type\" "
+ "WHERE ID = ?",
+ graph ? graph : "main");
if (stmt) {
tracker_db_statement_bind_int (stmt, 0, id);
@@ -140,7 +142,7 @@ tracker_data_query_unused_uuid (TrackerDataManager *manager,
gchar *uuid = NULL;
stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT, &error,
- "SELECT SparqlUUID()");
+ "SELECT SparqlBNODE()");
if (stmt) {
cursor = tracker_db_statement_start_cursor (stmt, &error);
diff --git a/src/libtracker-data/tracker-data-query.h b/src/libtracker-data/tracker-data-query.h
index 3b2df21fd..928483169 100644
--- a/src/libtracker-data/tracker-data-query.h
+++ b/src/libtracker-data/tracker-data-query.h
@@ -45,6 +45,7 @@ TrackerDBCursor *tracker_data_query_sparql_cursor (TrackerDataManager *mana
GError **error);
GPtrArray* tracker_data_query_rdf_type (TrackerDataManager *manager,
+ const gchar *graph,
gint id);
G_END_DECLS
diff --git a/src/libtracker-data/tracker-data-update.c b/src/libtracker-data/tracker-data-update.c
index c5d2941fc..65e9d8306 100644
--- a/src/libtracker-data/tracker-data-update.c
+++ b/src/libtracker-data/tracker-data-update.c
@@ -34,15 +34,14 @@
#include "tracker-data-query.h"
#include "tracker-db-interface-sqlite.h"
#include "tracker-db-manager.h"
-#include "tracker-db-journal.h"
#include "tracker-ontologies.h"
#include "tracker-property.h"
#include "tracker-sparql-query.h"
#include "tracker-sparql.h"
typedef struct _TrackerDataUpdateBuffer TrackerDataUpdateBuffer;
+typedef struct _TrackerDataUpdateBufferGraph TrackerDataUpdateBufferGraph;
typedef struct _TrackerDataUpdateBufferResource TrackerDataUpdateBufferResource;
-typedef struct _TrackerDataUpdateBufferPredicate TrackerDataUpdateBufferPredicate;
typedef struct _TrackerDataUpdateBufferProperty TrackerDataUpdateBufferProperty;
typedef struct _TrackerDataUpdateBufferTable TrackerDataUpdateBufferTable;
typedef struct _TrackerDataBlankBuffer TrackerDataBlankBuffer;
@@ -52,21 +51,24 @@ typedef struct _TrackerCommitDelegate TrackerCommitDelegate;
struct _TrackerDataUpdateBuffer {
/* string -> integer */
GHashTable *resource_cache;
- /* string -> TrackerDataUpdateBufferResource */
- GHashTable *resources;
- /* integer -> TrackerDataUpdateBufferResource */
- GHashTable *resources_by_id;
-
- /* the following two fields are valid per sqlite transaction, not just for same subject */
- /* TrackerClass -> integer */
- GHashTable *class_counts;
+ /* string -> TrackerDataUpdateBufferGraph */
+ GPtrArray *graphs;
#if HAVE_TRACKER_FTS
gboolean fts_ever_updated;
#endif
};
+struct _TrackerDataUpdateBufferGraph {
+ gchar *graph;
+ gint id;
+
+ /* string -> TrackerDataUpdateBufferResource */
+ GHashTable *resources;
+};
+
struct _TrackerDataUpdateBufferResource {
+ const TrackerDataUpdateBufferGraph *graph;
const gchar *subject;
gint id;
gboolean create;
@@ -86,11 +88,10 @@ struct _TrackerDataUpdateBufferResource {
struct _TrackerDataUpdateBufferProperty {
const gchar *name;
GValue value;
- gint graph;
- gboolean date_time : 1;
+ guint date_time : 1;
#if HAVE_TRACKER_FTS
- gboolean fts : 1;
+ guint fts : 1;
#endif
};
@@ -104,19 +105,6 @@ struct _TrackerDataUpdateBufferTable {
GArray *properties;
};
-/* buffer for anonymous blank nodes
- * that are not yet in the database */
-struct _TrackerDataBlankBuffer {
- GHashTable *table;
- gchar *subject;
- /* string */
- GArray *predicates;
- /* string */
- GArray *objects;
- /* string */
- GArray *graphs;
-};
-
struct _TrackerStatementDelegate {
TrackerStatementCallback callback;
gpointer user_data;
@@ -127,14 +115,6 @@ struct _TrackerCommitDelegate {
gpointer user_data;
};
-typedef struct {
- gchar *graph;
- gchar *subject;
- gchar *predicate;
- gchar *object;
- gboolean is_uri;
-} QueuedStatement;
-
struct _TrackerData {
GObject parent_instance;
@@ -142,12 +122,10 @@ struct _TrackerData {
gboolean in_transaction;
gboolean in_ontology_transaction;
- gboolean in_journal_replay;
TrackerDataUpdateBuffer update_buffer;
/* current resource */
TrackerDataUpdateBufferResource *resource_buffer;
- TrackerDataBlankBuffer blank_buffer;
time_t resource_time;
gint transaction_modseq;
gboolean has_persistent;
@@ -158,8 +136,6 @@ struct _TrackerData {
GPtrArray *rollback_callbacks;
gint max_service_id;
gint max_ontology_id;
-
- TrackerDBJournal *journal_writer;
};
struct _TrackerDataClass {
@@ -179,21 +155,16 @@ static gint ensure_resource_id (TrackerData *data,
static void cache_insert_value (TrackerData *data,
const gchar *table_name,
const gchar *field_name,
- gboolean transient,
GValue *value,
- gint graph,
gboolean multiple_values,
gboolean fts,
gboolean date_time);
static GArray *get_old_property_values (TrackerData *data,
TrackerProperty *property,
GError **error);
-static gchar* gvalue_to_string (TrackerPropertyType type,
- GValue *gvalue);
static gboolean delete_metadata_decomposed (TrackerData *data,
TrackerProperty *property,
- const gchar *value,
- gint value_id,
+ GValue *gvalue,
GError **error);
static void resource_buffer_switch (TrackerData *data,
const gchar *graph,
@@ -571,12 +542,11 @@ cache_table_free (TrackerDataUpdateBufferTable *table)
static TrackerDataUpdateBufferTable *
cache_ensure_table (TrackerData *data,
const gchar *table_name,
- gboolean multiple_values,
- gboolean transient)
+ gboolean multiple_values)
{
TrackerDataUpdateBufferTable *table;
- if (!data->resource_buffer->modified && !transient) {
+ if (!data->resource_buffer->modified) {
/* first modification of this particular resource, update tracker:modified */
GValue gvalue = { 0 };
@@ -586,8 +556,7 @@ cache_ensure_table (TrackerData *data,
g_value_init (&gvalue, G_TYPE_INT64);
g_value_set_int64 (&gvalue, get_transaction_modseq (data));
cache_insert_value (data, "rdfs:Resource", "tracker:modified",
- TRUE, &gvalue, 0,
- FALSE, FALSE, FALSE);
+ &gvalue, FALSE, FALSE, FALSE);
}
table = g_hash_table_lookup (data->resource_buffer->tables, table_name);
@@ -606,7 +575,7 @@ cache_insert_row (TrackerData *data,
{
TrackerDataUpdateBufferTable *table;
- table = cache_ensure_table (data, tracker_class_get_name (class), FALSE, FALSE);
+ table = cache_ensure_table (data, tracker_class_get_name (class), FALSE);
table->class = class;
table->insert = TRUE;
}
@@ -615,28 +584,26 @@ static void
cache_insert_value (TrackerData *data,
const gchar *table_name,
const gchar *field_name,
- gboolean transient,
GValue *value,
- gint graph,
gboolean multiple_values,
gboolean fts,
gboolean date_time)
{
TrackerDataUpdateBufferTable *table;
- TrackerDataUpdateBufferProperty property;
+ TrackerDataUpdateBufferProperty property = { 0 };
/* No need to strdup here, the incoming string is either always static, or
* long-standing as tracker_property_get_name return value. */
property.name = field_name;
- property.value = *value;
- property.graph = graph;
+ g_value_init (&property.value, G_VALUE_TYPE (value));
+ g_value_copy (value, &property.value);
#if HAVE_TRACKER_FTS
property.fts = fts;
#endif
property.date_time = date_time;
- table = cache_ensure_table (data, table_name, multiple_values, transient);
+ table = cache_ensure_table (data, table_name, multiple_values);
g_array_append_val (table->properties, property);
}
@@ -646,7 +613,7 @@ cache_delete_row (TrackerData *data,
{
TrackerDataUpdateBufferTable *table;
- table = cache_ensure_table (data, tracker_class_get_name (class), FALSE, FALSE);
+ table = cache_ensure_table (data, tracker_class_get_name (class), FALSE);
table->class = class;
table->delete_row = TRUE;
}
@@ -655,24 +622,24 @@ static void
cache_delete_value (TrackerData *data,
const gchar *table_name,
const gchar *field_name,
- gboolean transient,
GValue *value,
gboolean multiple_values,
gboolean fts,
gboolean date_time)
{
TrackerDataUpdateBufferTable *table;
- TrackerDataUpdateBufferProperty property;
+ TrackerDataUpdateBufferProperty property = { 0 };
property.name = field_name;
- property.value = *value;
- property.graph = 0;
+
+ g_value_init (&property.value, G_VALUE_TYPE (value));
+ g_value_copy (value, &property.value);
#if HAVE_TRACKER_FTS
property.fts = fts;
#endif
property.date_time = date_time;
- table = cache_ensure_table (data, table_name, multiple_values, transient);
+ table = cache_ensure_table (data, table_name, multiple_values);
table->delete_value = TRUE;
g_array_append_val (table->properties, property);
}
@@ -719,11 +686,12 @@ ensure_resource_id (TrackerData *data,
id = tracker_data_update_get_new_service_id (data);
stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, &error,
- "INSERT INTO Resource (ID, Uri) VALUES (?, ?)");
+ "INSERT INTO Resource (ID, Uri, BlankNode) VALUES (?, ?, ?)");
if (stmt) {
tracker_db_statement_bind_int (stmt, 0, id);
tracker_db_statement_bind_text (stmt, 1, uri);
+ tracker_db_statement_bind_int (stmt, 2, g_str_has_prefix (uri, "urn:bnode:"));
tracker_db_statement_execute (stmt, &error);
g_object_unref (stmt);
}
@@ -733,51 +701,12 @@ ensure_resource_id (TrackerData *data,
g_error_free (error);
}
-#ifndef DISABLE_JOURNAL
- if (!data->in_journal_replay) {
- tracker_db_journal_append_resource (data->journal_writer, id, uri);
- }
-#endif /* DISABLE_JOURNAL */
-
g_hash_table_insert (data->update_buffer.resource_cache, g_strdup (uri), GINT_TO_POINTER (id));
}
return id;
}
-static gint
-ensure_graph_id (TrackerData *data,
- const gchar *uri,
- gboolean *create)
-{
- TrackerDBInterface *iface;
- TrackerDBStatement *stmt;
- GError *error = NULL;
- gint id;
-
- id = GPOINTER_TO_INT (g_hash_table_lookup (data->update_buffer.resource_cache, uri));
- if (id != 0)
- return id;
-
- id = ensure_resource_id (data, uri, create);
- iface = tracker_data_manager_get_writable_db_interface (data->manager);
- stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, &error,
- "INSERT OR IGNORE INTO Graph (ID) VALUES (?)");
-
- if (stmt) {
- tracker_db_statement_bind_int (stmt, 0, id);
- tracker_db_statement_execute (stmt, &error);
- g_object_unref (stmt);
- }
-
- if (error) {
- g_critical ("Could not ensure graph existence: %s", error->message);
- g_error_free (error);
- }
-
- return id;
-}
-
static void
statement_bind_gvalue (TrackerDBStatement *stmt,
gint *idx,
@@ -798,9 +727,36 @@ statement_bind_gvalue (TrackerDBStatement *stmt,
break;
default:
if (type == TRACKER_TYPE_DATE_TIME) {
- tracker_db_statement_bind_double (stmt, (*idx)++, tracker_date_time_get_time (value));
- tracker_db_statement_bind_int (stmt, (*idx)++, tracker_date_time_get_local_date (value));
- tracker_db_statement_bind_int (stmt, (*idx)++, tracker_date_time_get_local_time (value));
+ gdouble time = tracker_date_time_get_time (value);
+ int offset = tracker_date_time_get_offset (value);
+
+ /* If we have anything that prevents a unix timestamp to be
+ * lossless, we use the ISO8601 string.
+ */
+ if (offset != 0 || floor (time) != time) {
+ gchar *str;
+
+ str = tracker_date_to_string (time, offset);
+ tracker_db_statement_bind_text (stmt, (*idx)++, str);
+ g_free (str);
+ } else {
+ tracker_db_statement_bind_int (stmt, (*idx)++, round (time));
+ }
+ } else if (type == G_TYPE_BYTES) {
+ GBytes *bytes;
+ gconstpointer data;
+ gsize len;
+
+ bytes = g_value_get_boxed (value);
+ data = g_bytes_get_data (bytes, &len);
+
+ if (len == strlen (data) + 1) {
+ /* No ancillary data */
+ tracker_db_statement_bind_text (stmt, (*idx)++, data);
+ } else {
+ /* String with langtag */
+ tracker_db_statement_bind_bytes (stmt, (*idx)++, bytes);
+ }
} else {
g_warning ("Unknown type for binding: %s\n", G_VALUE_TYPE_NAME (value));
}
@@ -809,40 +765,23 @@ statement_bind_gvalue (TrackerDBStatement *stmt,
}
static void
-add_class_count (TrackerData *data,
- TrackerClass *class,
- gint count)
-{
- gint old_count_entry;
-
- tracker_class_set_count (class, tracker_class_get_count (class) + count);
-
- /* update class_counts table so that the count change can be reverted in case of rollback */
- if (!data->update_buffer.class_counts) {
- data->update_buffer.class_counts = g_hash_table_new (g_direct_hash, g_direct_equal);
- }
-
- old_count_entry = GPOINTER_TO_INT (g_hash_table_lookup (data->update_buffer.class_counts, class));
- g_hash_table_insert (data->update_buffer.class_counts, class,
- GINT_TO_POINTER (old_count_entry + count));
-}
-
-static void
-tracker_data_resource_buffer_flush (TrackerData *data,
- GError **error)
+tracker_data_resource_buffer_flush (TrackerData *data,
+ TrackerDataUpdateBufferResource *resource,
+ GError **error)
{
TrackerDBInterface *iface;
TrackerDBStatement *stmt;
TrackerDataUpdateBufferTable *table;
TrackerDataUpdateBufferProperty *property;
GHashTableIter iter;
- const gchar *table_name;
+ const gchar *table_name, *database;
gint i, param;
GError *actual_error = NULL;
iface = tracker_data_manager_get_writable_db_interface (data->manager);
+ database = resource->graph->graph ? resource->graph->graph : "main";
- g_hash_table_iter_init (&iter, data->resource_buffer->tables);
+ g_hash_table_iter_init (&iter, resource->tables);
while (g_hash_table_iter_next (&iter, (gpointer*) &table_name, (gpointer*) &table)) {
if (table->multiple_values) {
for (i = 0; i < table->properties->len; i++) {
@@ -851,22 +790,15 @@ tracker_data_resource_buffer_flush (TrackerData *data,
if (table->delete_value) {
/* delete rows for multiple value properties */
stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, &actual_error,
- "DELETE FROM \"%s\" WHERE ID = ? AND \"%s\" = ?",
+ "DELETE FROM \"%s\".\"%s\" WHERE ID = ? AND \"%s\" = ?",
+ database,
table_name,
property->name);
- } else if (property->date_time) {
- stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, &actual_error,
- "INSERT OR IGNORE INTO \"%s\" (ID, \"%s\", \"%s:localDate\", \"%s:localTime\", \"%s:graph\") VALUES (?, ?, ?, ?, ?)",
- table_name,
- property->name,
- property->name,
- property->name,
- property->name);
} else {
stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, &actual_error,
- "INSERT OR IGNORE INTO \"%s\" (ID, \"%s\", \"%s:graph\") VALUES (?, ?, ?)",
+ "INSERT OR IGNORE INTO \"%s\".\"%s\" (ID, \"%s\") VALUES (?, ?)",
+ database,
table_name,
- property->name,
property->name);
}
@@ -877,11 +809,11 @@ tracker_data_resource_buffer_flush (TrackerData *data,
param = 0;
- tracker_db_statement_bind_int (stmt, param++, data->resource_buffer->id);
+ tracker_db_statement_bind_int (stmt, param++, resource->id);
statement_bind_gvalue (stmt, &param, &property->value);
- if (property->graph != 0) {
- tracker_db_statement_bind_int (stmt, param++, property->graph);
+ if (resource->graph->id != 0) {
+ tracker_db_statement_bind_int (stmt, param++, resource->graph->id);
} else {
tracker_db_statement_bind_null (stmt, param++);
}
@@ -900,10 +832,11 @@ tracker_data_resource_buffer_flush (TrackerData *data,
if (table->delete_row) {
/* remove entry from rdf:type table */
stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, &actual_error,
- "DELETE FROM \"rdfs:Resource_rdf:type\" WHERE ID = ? AND \"rdf:type\" = ?");
+ "DELETE FROM \"%s\".\"rdfs:Resource_rdf:type\" WHERE ID = ? AND \"rdf:type\" = ?",
+ database);
if (stmt) {
- tracker_db_statement_bind_int (stmt, 0, data->resource_buffer->id);
+ tracker_db_statement_bind_int (stmt, 0, resource->id);
tracker_db_statement_bind_int (stmt, 1, ensure_resource_id (data, tracker_class_get_uri (table->class), NULL));
tracker_db_statement_execute (stmt, &actual_error);
g_object_unref (stmt);
@@ -914,16 +847,13 @@ tracker_data_resource_buffer_flush (TrackerData *data,
return;
}
- if (table->class) {
- add_class_count (data, table->class, -1);
- }
-
/* remove row from class table */
stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, &actual_error,
- "DELETE FROM \"%s\" WHERE ID = ?", table_name);
+ "DELETE FROM \"%s\".\"%s\" WHERE ID = ?",
+ database, table_name);
if (stmt) {
- tracker_db_statement_bind_int (stmt, 0, data->resource_buffer->id);
+ tracker_db_statement_bind_int (stmt, 0, resource->id);
tracker_db_statement_execute (stmt, &actual_error);
g_object_unref (stmt);
}
@@ -937,17 +867,18 @@ tracker_data_resource_buffer_flush (TrackerData *data,
}
if (table->insert) {
- sql = g_string_new ("INSERT INTO \"");
+ sql = g_string_new ("INSERT INTO ");
values_sql = g_string_new ("VALUES (?");
} else {
- sql = g_string_new ("UPDATE \"");
+ sql = g_string_new ("UPDATE ");
values_sql = NULL;
}
- g_string_append (sql, table_name);
+ g_string_append_printf (sql, "\"%s\".\"%s\"",
+ database, table_name);
if (table->insert) {
- g_string_append (sql, "\" (ID");
+ g_string_append (sql, " (ID");
if (strcmp (table_name, "rdfs:Resource") == 0) {
g_string_append (sql, ", \"tracker:added\", \"tracker:modified\"");
@@ -955,7 +886,7 @@ tracker_data_resource_buffer_flush (TrackerData *data,
} else {
}
} else {
- g_string_append (sql, "\" SET ");
+ g_string_append (sql, " SET ");
}
for (i = 0; i < table->properties->len; i++) {
@@ -963,27 +894,11 @@ tracker_data_resource_buffer_flush (TrackerData *data,
if (table->insert) {
g_string_append_printf (sql, ", \"%s\"", property->name);
g_string_append (values_sql, ", ?");
-
- if (property->date_time) {
- g_string_append_printf (sql, ", \"%s:localDate\"", property->name);
- g_string_append_printf (sql, ", \"%s:localTime\"", property->name);
- g_string_append (values_sql, ", ?, ?");
- }
-
- g_string_append_printf (sql, ", \"%s:graph\"", property->name);
- g_string_append (values_sql, ", ?");
} else {
if (i > 0) {
g_string_append (sql, ", ");
}
g_string_append_printf (sql, "\"%s\" = ?", property->name);
-
- if (property->date_time) {
- g_string_append_printf (sql, ", \"%s:localDate\" = ?", property->name);
- g_string_append_printf (sql, ", \"%s:localTime\" = ?", property->name);
- }
-
- g_string_append_printf (sql, ", \"%s:graph\" = ?", property->name);
}
}
@@ -1009,7 +924,7 @@ tracker_data_resource_buffer_flush (TrackerData *data,
}
if (table->insert) {
- tracker_db_statement_bind_int (stmt, 0, data->resource_buffer->id);
+ tracker_db_statement_bind_int (stmt, 0, resource->id);
if (strcmp (table_name, "rdfs:Resource") == 0) {
g_warn_if_fail (data->resource_time != 0);
@@ -1028,23 +943,13 @@ tracker_data_resource_buffer_flush (TrackerData *data,
if (table->delete_value) {
/* just set value to NULL for single value properties */
tracker_db_statement_bind_null (stmt, param++);
- if (property->date_time) {
- /* also set localDate and localTime to NULL */
- tracker_db_statement_bind_null (stmt, param++);
- tracker_db_statement_bind_null (stmt, param++);
- }
} else {
statement_bind_gvalue (stmt, &param, &property->value);
}
- if (property->graph != 0) {
- tracker_db_statement_bind_int (stmt, param++, property->graph);
- } else {
- tracker_db_statement_bind_null (stmt, param++);
- }
}
if (!table->insert) {
- tracker_db_statement_bind_int (stmt, param++, data->resource_buffer->id);
+ tracker_db_statement_bind_int (stmt, param++, resource->id);
}
tracker_db_statement_execute (stmt, &actual_error);
@@ -1058,13 +963,13 @@ tracker_data_resource_buffer_flush (TrackerData *data,
}
#if HAVE_TRACKER_FTS
- if (data->resource_buffer->fts_updated) {
+ if (resource->fts_updated) {
TrackerProperty *prop;
GArray *values;
GPtrArray *properties, *text;
properties = text = NULL;
- g_hash_table_iter_init (&iter, data->resource_buffer->predicates);
+ g_hash_table_iter_init (&iter, resource->predicates);
while (g_hash_table_iter_next (&iter, (gpointer*) &prop, (gpointer*) &values)) {
if (tracker_property_get_fulltext_indexed (prop)) {
GString *fts;
@@ -1091,7 +996,8 @@ tracker_data_resource_buffer_flush (TrackerData *data,
g_ptr_array_add (text, NULL);
tracker_db_interface_sqlite_fts_update_text (iface,
- data->resource_buffer->id,
+ database,
+ resource->id,
(const gchar **) properties->pdata,
(const gchar **) text->pdata);
data->update_buffer.fts_ever_updated = TRUE;
@@ -1102,6 +1008,14 @@ tracker_data_resource_buffer_flush (TrackerData *data,
#endif
}
+static void
+graph_buffer_free (TrackerDataUpdateBufferGraph *graph)
+{
+ g_hash_table_unref (graph->resources);
+ g_free (graph->graph);
+ g_slice_free (TrackerDataUpdateBufferGraph, graph);
+}
+
static void resource_buffer_free (TrackerDataUpdateBufferResource *resource)
{
g_hash_table_unref (resource->predicates);
@@ -1118,32 +1032,26 @@ void
tracker_data_update_buffer_flush (TrackerData *data,
GError **error)
{
+ TrackerDataUpdateBufferGraph *graph;
+ TrackerDataUpdateBufferResource *resource;
GHashTableIter iter;
GError *actual_error = NULL;
+ gint i;
- if (data->in_journal_replay) {
- g_hash_table_iter_init (&iter, data->update_buffer.resources_by_id);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &data->resource_buffer)) {
- tracker_data_resource_buffer_flush (data, &actual_error);
- if (actual_error) {
- g_propagate_error (error, actual_error);
- break;
- }
- }
+ for (i = 0; i < data->update_buffer.graphs->len; i++) {
+ graph = g_ptr_array_index (data->update_buffer.graphs, i);
+ g_hash_table_iter_init (&iter, graph->resources);
- g_hash_table_remove_all (data->update_buffer.resources_by_id);
- } else {
- g_hash_table_iter_init (&iter, data->update_buffer.resources);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &data->resource_buffer)) {
- tracker_data_resource_buffer_flush (data, &actual_error);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &resource)) {
+ tracker_data_resource_buffer_flush (data, resource, &actual_error);
if (actual_error) {
g_propagate_error (error, actual_error);
break;
}
}
-
- g_hash_table_remove_all (data->update_buffer.resources);
}
+
+ g_ptr_array_set_size (data->update_buffer.graphs, 0);
data->resource_buffer = NULL;
}
@@ -1151,136 +1059,46 @@ void
tracker_data_update_buffer_might_flush (TrackerData *data,
GError **error)
{
- /* avoid high memory usage by update buffer */
- if (g_hash_table_size (data->update_buffer.resources) +
- g_hash_table_size (data->update_buffer.resources_by_id) >= 1000) {
- tracker_data_update_buffer_flush (data, error);
+ TrackerDataUpdateBufferGraph *graph;
+ gint i, count = 0;
+
+ for (i = 0; i < data->update_buffer.graphs->len; i++) {
+ graph = g_ptr_array_index (data->update_buffer.graphs, i);
+ count += g_hash_table_size (graph->resources);
+
+ if (count >= 1000) {
+ tracker_data_update_buffer_flush (data, error);
+ break;
+ }
}
}
static void
tracker_data_update_buffer_clear (TrackerData *data)
{
- g_hash_table_remove_all (data->update_buffer.resources);
- g_hash_table_remove_all (data->update_buffer.resources_by_id);
+ g_ptr_array_set_size (data->update_buffer.graphs, 0);
g_hash_table_remove_all (data->update_buffer.resource_cache);
data->resource_buffer = NULL;
#if HAVE_TRACKER_FTS
data->update_buffer.fts_ever_updated = FALSE;
#endif
-
- if (data->update_buffer.class_counts) {
- /* revert class count changes */
-
- GHashTableIter iter;
- TrackerClass *class;
- gpointer count_ptr;
-
- g_hash_table_iter_init (&iter, data->update_buffer.class_counts);
- while (g_hash_table_iter_next (&iter, (gpointer*) &class, &count_ptr)) {
- gint count;
-
- count = GPOINTER_TO_INT (count_ptr);
- tracker_class_set_count (class, tracker_class_get_count (class) - count);
- }
-
- g_hash_table_remove_all (data->update_buffer.class_counts);
- }
-}
-
-static void
-tracker_data_blank_buffer_flush (TrackerData *data,
- GError **error)
-{
- /* end of blank node */
- gint i;
- gint id;
- gchar *subject;
- gchar *blank_uri;
- const gchar *sha1;
- GChecksum *checksum;
- GError *actual_error = NULL;
- TrackerDBInterface *iface;
-
- subject = data->blank_buffer.subject;
- data->blank_buffer.subject = NULL;
-
- /* we share anonymous blank nodes with identical properties
- to avoid blowing up the database with duplicates */
-
- checksum = g_checksum_new (G_CHECKSUM_SHA1);
-
- /* generate hash uri from data to find resource
- assumes no collisions due to generally little contents of anonymous nodes */
- for (i = 0; i < data->blank_buffer.predicates->len; i++) {
- if (g_array_index (data->blank_buffer.graphs, guchar *, i) != NULL) {
- g_checksum_update (checksum, g_array_index (data->blank_buffer.graphs, guchar *, i), -1);
- }
-
- g_checksum_update (checksum, g_array_index (data->blank_buffer.predicates, guchar *, i), -1);
- g_checksum_update (checksum, g_array_index (data->blank_buffer.objects, guchar *, i), -1);
- }
-
- sha1 = g_checksum_get_string (checksum);
-
- /* generate name based uuid */
- blank_uri = g_strdup_printf ("urn:uuid:%.8s-%.4s-%.4s-%.4s-%.12s",
- sha1, sha1 + 8, sha1 + 12, sha1 + 16, sha1 + 20);
-
- iface = tracker_data_manager_get_writable_db_interface (data->manager);
- id = tracker_data_query_resource_id (data->manager, iface, blank_uri);
-
- if (id == 0) {
- /* uri not found
- replay piled up statements to create resource */
- for (i = 0; i < data->blank_buffer.predicates->len; i++) {
- tracker_data_insert_statement (data,
- g_array_index (data->blank_buffer.graphs, gchar *, i),
- blank_uri,
- g_array_index (data->blank_buffer.predicates, gchar *, i),
- g_array_index (data->blank_buffer.objects, gchar *, i),
- &actual_error);
- if (actual_error) {
- break;
- }
- }
- }
-
- /* free piled up statements */
- for (i = 0; i < data->blank_buffer.predicates->len; i++) {
- g_free (g_array_index (data->blank_buffer.graphs, gchar *, i));
- g_free (g_array_index (data->blank_buffer.predicates, gchar *, i));
- g_free (g_array_index (data->blank_buffer.objects, gchar *, i));
- }
- g_array_remove_range (data->blank_buffer.graphs, 0, data->blank_buffer.graphs->len);
- g_array_remove_range (data->blank_buffer.predicates, 0, data->blank_buffer.predicates->len);
- g_array_remove_range (data->blank_buffer.objects, 0, data->blank_buffer.objects->len);
-
- g_hash_table_insert (data->blank_buffer.table, subject, blank_uri);
- g_checksum_free (checksum);
-
- if (actual_error) {
- g_propagate_error (error, actual_error);
- }
}
static void
cache_create_service_decomposed (TrackerData *data,
- TrackerClass *cl,
- const gchar *graph,
- gint graph_id)
+ TrackerClass *cl)
{
TrackerClass **super_classes;
TrackerProperty **domain_indexes;
GValue gvalue = { 0 };
- gint i, final_graph_id, class_id;
+ gint i, class_id;
TrackerOntologies *ontologies;
/* also create instance of all super classes */
super_classes = tracker_class_get_super_classes (cl);
while (*super_classes) {
- cache_create_service_decomposed (data, *super_classes, graph, graph_id);
+ cache_create_service_decomposed (data, *super_classes);
super_classes++;
}
@@ -1297,8 +1115,6 @@ cache_create_service_decomposed (TrackerData *data,
cache_insert_row (data, cl);
- final_graph_id = (graph != NULL ? ensure_graph_id (data, graph, NULL) : graph_id);
-
/* This is the original, no idea why tracker_class_get_id wasn't used here:
* class_id = ensure_resource_id (tracker_class_get_uri (cl), NULL); */
@@ -1307,19 +1123,18 @@ cache_create_service_decomposed (TrackerData *data,
g_value_set_int64 (&gvalue, class_id);
cache_insert_value (data, "rdfs:Resource_rdf:type", "rdf:type",
- FALSE, &gvalue, final_graph_id,
- TRUE, FALSE, FALSE);
-
- add_class_count (data, cl, 1);
+ &gvalue, TRUE, FALSE, FALSE);
- if (!data->in_journal_replay && data->insert_callbacks) {
+ if (data->insert_callbacks) {
guint n;
for (n = 0; n < data->insert_callbacks->len; n++) {
TrackerStatementDelegate *delegate;
delegate = g_ptr_array_index (data->insert_callbacks, n);
- delegate->callback (final_graph_id, graph, data->resource_buffer->id, data->resource_buffer->subject,
+ delegate->callback (data->resource_buffer->graph->id,
+ data->resource_buffer->graph->graph,
+ data->resource_buffer->id, data->resource_buffer->subject,
tracker_property_get_id (tracker_ontologies_get_rdf_type (ontologies)),
class_id,
tracker_class_get_uri (cl),
@@ -1354,7 +1169,6 @@ cache_create_service_decomposed (TrackerData *data,
if (old_values &&
old_values->len > 0) {
GValue *v;
- GValue gvalue_copy = { 0 };
/* Don't expect several values for property which is a domain index */
g_assert_cmpint (old_values->len, ==, 1);
@@ -1365,15 +1179,11 @@ cache_create_service_decomposed (TrackerData *data,
tracker_class_get_name (cl));
v = &g_array_index (old_values, GValue, 0);
- g_value_init (&gvalue_copy, G_VALUE_TYPE (v));
- g_value_copy (v, &gvalue_copy);
cache_insert_value (data,
tracker_class_get_name (cl),
tracker_property_get_name (*domain_indexes),
- tracker_property_get_transient (*domain_indexes),
- &gvalue_copy,
- graph != NULL ? ensure_graph_id (data, graph, NULL) : graph_id,
+ v,
tracker_property_get_multiple_values (*domain_indexes),
tracker_property_get_fulltext_indexed (*domain_indexes),
tracker_property_get_data_type (*domain_indexes) == TRACKER_PROPERTY_TYPE_DATETIME);
@@ -1480,7 +1290,8 @@ static GArray *
get_property_values (TrackerData *data,
TrackerProperty *property)
{
- gboolean multiple_values;
+ gboolean multiple_values;
+ const gchar *database;
GArray *old_values;
multiple_values = tracker_property_get_multiple_values (property);
@@ -1489,6 +1300,9 @@ get_property_values (TrackerData *data,
g_array_set_clear_func (old_values, (GDestroyNotify) g_value_unset);
g_hash_table_insert (data->resource_buffer->predicates, g_object_ref (property), old_values);
+ database = data->resource_buffer->graph->graph ?
+ data->resource_buffer->graph->graph : "main";
+
if (!data->resource_buffer->create) {
TrackerDBInterface *iface;
TrackerDBStatement *stmt;
@@ -1503,8 +1317,8 @@ get_property_values (TrackerData *data,
iface = tracker_data_manager_get_writable_db_interface (data->manager);
stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT, &error,
- "SELECT \"%s\" FROM \"%s\" WHERE ID = ?",
- field_name, table_name);
+ "SELECT \"%s\" FROM \"%s\".\"%s\" WHERE ID = ?",
+ field_name, database, table_name);
if (stmt) {
tracker_db_statement_bind_int (stmt, 0, data->resource_buffer->id);
@@ -1554,6 +1368,12 @@ get_old_property_values (TrackerData *data,
GError **error)
{
GArray *old_values;
+#if HAVE_TRACKER_FTS
+ const gchar *database;
+
+ database = data->resource_buffer->graph->graph ?
+ data->resource_buffer->graph->graph : "main";
+#endif
/* read existing property values */
old_values = g_hash_table_lookup (data->resource_buffer->predicates, property);
@@ -1606,6 +1426,7 @@ get_old_property_values (TrackerData *data,
}
tracker_db_interface_sqlite_fts_delete_text (iface,
+ database,
data->resource_buffer->id,
property_name,
str->str);
@@ -1633,20 +1454,27 @@ get_old_property_values (TrackerData *data,
}
static void
-string_to_gvalue (const gchar *value,
- TrackerPropertyType type,
- GValue *gvalue,
- TrackerData *data,
- GError **error)
+bytes_to_gvalue (GBytes *bytes,
+ TrackerPropertyType type,
+ GValue *gvalue,
+ TrackerData *data,
+ GError **error)
{
gint object_id;
gchar *datetime;
+ const gchar *value;
+
+ value = g_bytes_get_data (bytes, NULL);
switch (type) {
case TRACKER_PROPERTY_TYPE_STRING:
g_value_init (gvalue, G_TYPE_STRING);
g_value_set_string (gvalue, value);
break;
+ case TRACKER_PROPERTY_TYPE_LANGSTRING:
+ g_value_init (gvalue, G_TYPE_BYTES);
+ g_value_set_boxed (gvalue, bytes);
+ break;
case TRACKER_PROPERTY_TYPE_INTEGER:
g_value_init (gvalue, G_TYPE_INT64);
g_value_set_int64 (gvalue, atoll (value));
@@ -1682,47 +1510,6 @@ string_to_gvalue (const gchar *value,
}
}
-static gchar*
-gvalue_to_string (TrackerPropertyType type,
- GValue *gvalue)
-{
- gchar *retval = NULL;
- gint64 datet;
-
- switch (type) {
- case TRACKER_PROPERTY_TYPE_STRING:
- retval = g_value_dup_string (gvalue);
- break;
- case TRACKER_PROPERTY_TYPE_INTEGER:
- retval = g_strdup_printf ("%" G_GINT64_FORMAT, g_value_get_int64 (gvalue));
- break;
- case TRACKER_PROPERTY_TYPE_BOOLEAN:
- retval = g_value_get_int64 (gvalue) == 0 ? g_strdup ("false") : g_strdup ("true");
- break;
- case TRACKER_PROPERTY_TYPE_DOUBLE:
- retval = g_new0 (char, G_ASCII_DTOSTR_BUF_SIZE);
- g_ascii_dtostr (retval, G_ASCII_DTOSTR_BUF_SIZE,
- g_value_get_double (gvalue));
- break;
- case TRACKER_PROPERTY_TYPE_DATE:
- datet = g_value_get_int64 (gvalue);
- retval = tracker_date_to_string (datet);
- /* it's a date-only, cut off the time */
- retval[10] = '\0';
- break;
- case TRACKER_PROPERTY_TYPE_DATETIME:
- datet = tracker_date_time_get_time (gvalue);
- retval = tracker_date_to_string (datet);
- break;
- case TRACKER_PROPERTY_TYPE_RESOURCE:
- default:
- g_warn_if_reached ();
- break;
- }
-
- return retval;
-}
-
static gboolean
resource_in_domain_index_class (TrackerData *data,
TrackerClass *domain_index_class)
@@ -1740,26 +1527,17 @@ static void
process_domain_indexes (TrackerData *data,
TrackerProperty *property,
GValue *gvalue,
- const gchar *field_name,
- const gchar *graph,
- gint graph_id)
+ const gchar *field_name)
{
TrackerClass **domain_index_classes;
domain_index_classes = tracker_property_get_domain_indexes (property);
while (*domain_index_classes) {
if (resource_in_domain_index_class (data, *domain_index_classes)) {
- GValue gvalue_copy = { 0 };
-
- g_value_init (&gvalue_copy, G_VALUE_TYPE (gvalue));
- g_value_copy (gvalue, &gvalue_copy);
-
cache_insert_value (data,
tracker_class_get_name (*domain_index_classes),
field_name,
- tracker_property_get_transient (property),
- &gvalue_copy,
- graph != NULL ? ensure_graph_id (data, graph, NULL) : graph_id,
+ gvalue,
FALSE,
tracker_property_get_fulltext_indexed (property),
tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME);
@@ -1771,17 +1549,13 @@ process_domain_indexes (TrackerData *data,
static gboolean
cache_insert_metadata_decomposed (TrackerData *data,
TrackerProperty *property,
- const gchar *value,
- gint value_id,
- const gchar *graph,
- gint graph_id,
+ GValue *value,
GError **error)
{
gboolean multiple_values;
const gchar *table_name;
const gchar *field_name;
TrackerProperty **super_properties;
- GValue gvalue = { 0 };
GArray *old_values;
GError *new_error = NULL;
gboolean change = FALSE;
@@ -1803,8 +1577,8 @@ cache_insert_metadata_decomposed (TrackerData *data,
super_is_multi = tracker_property_get_multiple_values (*super_properties);
if (super_is_multi || old_values->len == 0) {
- change |= cache_insert_metadata_decomposed (data, *super_properties, value, value_id,
- graph, graph_id, &new_error);
+ change |= cache_insert_metadata_decomposed (data, *super_properties, value,
+ &new_error);
if (new_error) {
g_propagate_error (error, new_error);
return FALSE;
@@ -1816,20 +1590,8 @@ cache_insert_metadata_decomposed (TrackerData *data,
table_name = tracker_property_get_table_name (property);
field_name = tracker_property_get_name (property);
- if (value) {
- string_to_gvalue (value, tracker_property_get_data_type (property), &gvalue, data, &new_error);
- if (new_error) {
- g_propagate_error (error, new_error);
- return FALSE;
- }
- } else {
- g_value_init (&gvalue, G_TYPE_INT64);
- g_value_set_int64 (&gvalue, value_id);
- }
-
- if (!value_set_add_value (old_values, &gvalue)) {
+ if (!value_set_add_value (old_values, value)) {
/* value already inserted */
- g_value_unset (&gvalue);
} else if (!multiple_values && old_values->len > 1) {
/* trying to add second value to single valued property */
GValue old_value = { 0 };
@@ -1865,19 +1627,15 @@ cache_insert_metadata_decomposed (TrackerData *data,
g_free (new_value_str);
g_value_unset (&old_value);
g_value_unset (&new_value);
- g_value_unset (&gvalue);
-
} else {
cache_insert_value (data, table_name, field_name,
- tracker_property_get_transient (property),
- &gvalue,
- graph != NULL ? ensure_graph_id (data, graph, NULL) : graph_id,
+ value,
multiple_values,
tracker_property_get_fulltext_indexed (property),
tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME);
if (!multiple_values) {
- process_domain_indexes (data, property, &gvalue, field_name, graph, graph_id);
+ process_domain_indexes (data, property, value, field_name);
}
change = TRUE;
@@ -1890,10 +1648,9 @@ static gboolean
delete_first_object (TrackerData *data,
TrackerProperty *field,
GArray *old_values,
- const gchar *graph,
GError **error)
{
- gint pred_id = 0, graph_id = 0;
+ gint pred_id = 0;
gint object_id = 0;
gboolean change = FALSE;
@@ -1902,93 +1659,56 @@ delete_first_object (TrackerData *data,
}
pred_id = tracker_property_get_id (field);
- graph_id = (graph != NULL ? query_resource_id (data, graph) : 0);
if (tracker_property_get_data_type (field) == TRACKER_PROPERTY_TYPE_RESOURCE) {
GError *new_error = NULL;
- GValue *v;
+ GValue *v, copy = G_VALUE_INIT;
v = &g_array_index (old_values, GValue, 0);
- object_id = (gint) g_value_get_int64 (v);
+ g_value_init (&copy, G_VALUE_TYPE (v));
+ g_value_copy (v, &copy);
/* This influences old_values, which is a reference, not a copy */
- change = delete_metadata_decomposed (data, field, NULL, object_id, &new_error);
+ change = delete_metadata_decomposed (data, field, &copy, &new_error);
+ g_value_unset (&copy);
if (new_error) {
g_propagate_error (error, new_error);
return change;
}
-
-#ifndef DISABLE_JOURNAL
- if (!data->in_journal_replay && change && !tracker_property_get_transient (field)) {
- tracker_db_journal_append_delete_statement_id (data->journal_writer,
- graph_id,
- data->resource_buffer->id,
- pred_id,
- object_id);
- }
-#endif /* DISABLE_JOURNAL */
} else {
- GValue *v;
+ GValue *v, copy = G_VALUE_INIT;
GError *new_error = NULL;
- gchar *object_str = NULL;
object_id = 0;
v = &g_array_index (old_values, GValue, 0);
- object_str = gvalue_to_string (tracker_property_get_data_type (field), v);
+ g_value_init (&copy, G_VALUE_TYPE (v));
+ g_value_copy (v, &copy);
/* This influences old_values, which is a reference, not a copy */
- change = delete_metadata_decomposed (data, field, object_str, 0, &new_error);
+ change = delete_metadata_decomposed (data, field, &copy, &new_error);
+ g_value_unset (&copy);
if (new_error) {
g_propagate_error (error, new_error);
return change;
}
-#ifndef DISABLE_JOURNAL
- if (!data->in_journal_replay && change && !tracker_property_get_transient (field)) {
- if (!tracker_property_get_force_journal (field) &&
- g_strcmp0 (graph, TRACKER_OWN_GRAPH_URN) == 0) {
- /* do not journal this statement extracted from filesystem */
- TrackerProperty *damaged;
- TrackerOntologies *ontologies;
-
- ontologies = tracker_data_manager_get_ontologies (data->manager);
- damaged = tracker_ontologies_get_property_by_uri (ontologies, TRACKER_PREFIX_TRACKER "damaged");
-
- tracker_db_journal_append_insert_statement (data->journal_writer,
- graph_id,
- data->resource_buffer->id,
- tracker_property_get_id (damaged),
- "true");
- } else {
- tracker_db_journal_append_delete_statement (data->journal_writer,
- graph_id,
- data->resource_buffer->id,
- pred_id,
- object_str);
- }
- }
-
-#endif /* DISABLE_JOURNAL */
-
if (data->delete_callbacks && change) {
guint n;
for (n = 0; n < data->delete_callbacks->len; n++) {
TrackerStatementDelegate *delegate;
delegate = g_ptr_array_index (data->delete_callbacks, n);
- delegate->callback (graph_id, graph,
+ delegate->callback (data->resource_buffer->graph->id,
+ data->resource_buffer->graph->graph,
data->resource_buffer->id,
data->resource_buffer->subject,
- pred_id, object_id,
- object_str,
+ pred_id, object_id, NULL, /* FIXME */
data->resource_buffer->types,
delegate->user_data);
}
}
-
- g_free (object_str);
}
return change;
@@ -1997,17 +1717,14 @@ delete_first_object (TrackerData *data,
static gboolean
cache_update_metadata_decomposed (TrackerData *data,
TrackerProperty *property,
- const gchar *value,
- gint value_id,
+ GValue *value,
const gchar *graph,
- gint graph_id,
GError **error)
{
gboolean multiple_values;
const gchar *table_name;
const gchar *field_name;
TrackerProperty **super_properties;
- GValue gvalue = { 0 };
GError *new_error = NULL;
gboolean change = FALSE;
@@ -2035,7 +1752,6 @@ cache_update_metadata_decomposed (TrackerData *data,
/* Delete old values from super */
change |= delete_first_object (data, *super_properties,
old_values,
- graph,
&new_error);
if (new_error) {
@@ -2060,8 +1776,8 @@ cache_update_metadata_decomposed (TrackerData *data,
g_free (subject);
}
- change |= cache_update_metadata_decomposed (data, *super_properties, value, value_id,
- graph, graph_id, &new_error);
+ change |= cache_update_metadata_decomposed (data, *super_properties, value,
+ graph, &new_error);
if (new_error) {
g_propagate_error (error, new_error);
return FALSE;
@@ -2072,27 +1788,14 @@ cache_update_metadata_decomposed (TrackerData *data,
table_name = tracker_property_get_table_name (property);
field_name = tracker_property_get_name (property);
- if (value) {
- string_to_gvalue (value, tracker_property_get_data_type (property), &gvalue, data, &new_error);
- if (new_error) {
- g_propagate_error (error, new_error);
- return FALSE;
- }
- } else {
- g_value_init (&gvalue, G_TYPE_INT64);
- g_value_set_int64 (&gvalue, value_id);
- }
-
cache_insert_value (data, table_name, field_name,
- tracker_property_get_transient (property),
- &gvalue,
- graph != NULL ? ensure_graph_id (data, graph, NULL) : graph_id,
+ value,
multiple_values,
tracker_property_get_fulltext_indexed (property),
tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME);
if (!multiple_values) {
- process_domain_indexes (data, property, &gvalue, field_name, graph, graph_id);
+ process_domain_indexes (data, property, value, field_name);
}
return TRUE;
@@ -2101,15 +1804,13 @@ cache_update_metadata_decomposed (TrackerData *data,
static gboolean
delete_metadata_decomposed (TrackerData *data,
TrackerProperty *property,
- const gchar *value,
- gint value_id,
+ GValue *value,
GError **error)
{
gboolean multiple_values;
const gchar *table_name;
const gchar *field_name;
TrackerProperty **super_properties;
- GValue gvalue = { 0 };
GArray *old_values;
GError *new_error = NULL;
gboolean change = FALSE;
@@ -2126,24 +1827,11 @@ delete_metadata_decomposed (TrackerData *data,
return FALSE;
}
- if (value) {
- string_to_gvalue (value, tracker_property_get_data_type (property), &gvalue, data, &new_error);
- if (new_error) {
- g_propagate_error (error, new_error);
- return FALSE;
- }
- } else {
- g_value_init (&gvalue, G_TYPE_INT64);
- g_value_set_int64 (&gvalue, value_id);
- }
-
- if (!value_set_remove_value (old_values, &gvalue)) {
+ if (!value_set_remove_value (old_values, value)) {
/* value not found */
- g_value_unset (&gvalue);
} else {
cache_delete_value (data, table_name, field_name,
- tracker_property_get_transient (property),
- &gvalue, multiple_values,
+ value, multiple_values,
tracker_property_get_fulltext_indexed (property),
tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME);
@@ -2154,14 +1842,10 @@ delete_metadata_decomposed (TrackerData *data,
while (*domain_index_classes) {
if (resource_in_domain_index_class (data, *domain_index_classes)) {
- GValue gvalue_copy = { 0 };
- g_value_init (&gvalue_copy, G_VALUE_TYPE (&gvalue));
- g_value_copy (&gvalue, &gvalue_copy);
cache_delete_value (data,
tracker_class_get_name (*domain_index_classes),
field_name,
- tracker_property_get_transient (property),
- &gvalue_copy, multiple_values,
+ value, multiple_values,
tracker_property_get_fulltext_indexed (property),
tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME);
}
@@ -2175,7 +1859,7 @@ delete_metadata_decomposed (TrackerData *data,
/* also delete super property values */
super_properties = tracker_property_get_super_properties (property);
while (*super_properties) {
- change |= delete_metadata_decomposed (data, *super_properties, value, value_id, error);
+ change |= delete_metadata_decomposed (data, *super_properties, value, error);
super_properties++;
}
@@ -2184,6 +1868,7 @@ delete_metadata_decomposed (TrackerData *data,
static void
db_delete_row (TrackerDBInterface *iface,
+ const gchar *database,
const gchar *table_name,
gint id)
{
@@ -2191,8 +1876,8 @@ db_delete_row (TrackerDBInterface *iface,
GError *error = NULL;
stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, &error,
- "DELETE FROM \"%s\" WHERE ID = ?",
- table_name);
+ "DELETE FROM \"%s\".\"%s\" WHERE ID = ?",
+ database, table_name);
if (stmt) {
tracker_db_statement_bind_int (stmt, 0, id);
@@ -2210,8 +1895,6 @@ db_delete_row (TrackerDBInterface *iface,
static void
cache_delete_resource_type_full (TrackerData *data,
TrackerClass *class,
- const gchar *graph,
- gint graph_id,
gboolean single_type)
{
TrackerDBInterface *iface;
@@ -2223,15 +1906,18 @@ cache_delete_resource_type_full (TrackerData *data,
guint p, n_props;
GError *error = NULL;
TrackerOntologies *ontologies;
+ const gchar *database;
iface = tracker_data_manager_get_writable_db_interface (data->manager);
ontologies = tracker_data_manager_get_ontologies (data->manager);
+ database = data->resource_buffer->graph->graph ?
+ data->resource_buffer->graph->graph : "main";
if (!single_type) {
if (strcmp (tracker_class_get_uri (class), TRACKER_PREFIX_RDFS "Resource") == 0 &&
g_hash_table_size (data->resource_buffer->tables) == 0) {
#if HAVE_TRACKER_FTS
- tracker_db_interface_sqlite_fts_delete_id (iface, data->resource_buffer->id);
+ tracker_db_interface_sqlite_fts_delete_id (iface, database, data->resource_buffer->id);
#endif
/* skip subclass query when deleting whole resource
to improve performance */
@@ -2241,10 +1927,7 @@ cache_delete_resource_type_full (TrackerData *data,
type = g_ptr_array_index (data->resource_buffer->types,
data->resource_buffer->types->len - 1);
- cache_delete_resource_type_full (data, type,
- graph,
- graph_id,
- TRUE);
+ cache_delete_resource_type_full (data, type, TRUE);
}
return;
@@ -2266,9 +1949,10 @@ cache_delete_resource_type_full (TrackerData *data,
/* retrieve all subclasses we need to remove from the subject
* before we can remove the class specified as object of the statement */
stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT, &error,
- "SELECT (SELECT Uri FROM Resource WHERE ID = \"rdfs:Class_rdfs:subClassOf\".ID) "
- "FROM \"rdfs:Resource_rdf:type\" INNER JOIN \"rdfs:Class_rdfs:subClassOf\" ON (\"rdf:type\" = \"rdfs:Class_rdfs:subClassOf\".ID) "
- "WHERE \"rdfs:Resource_rdf:type\".ID = ? AND \"rdfs:subClassOf\" = (SELECT ID FROM Resource WHERE Uri = ?)");
+ "SELECT (SELECT Uri FROM Resource WHERE ID = subclass.ID) "
+ "FROM \"%s\".\"rdfs:Resource_rdf:type\" AS type INNER JOIN \"%s\".\"rdfs:Class_rdfs:subClassOf\" AS subclass ON (type.\"rdf:type\" = subclass.ID) "
+ "WHERE type.ID = ? AND subclass.\"rdfs:subClassOf\" = (SELECT ID FROM Resource WHERE Uri = ?)",
+ database, database);
if (stmt) {
tracker_db_statement_bind_int (stmt, 0, data->resource_buffer->id);
@@ -2283,7 +1967,7 @@ cache_delete_resource_type_full (TrackerData *data,
class_uri = tracker_db_cursor_get_string (cursor, 0, NULL);
cache_delete_resource_type_full (data, tracker_ontologies_get_class_by_uri (ontologies, class_uri),
- graph, graph_id, FALSE);
+ FALSE);
}
g_object_unref (cursor);
@@ -2322,7 +2006,7 @@ cache_delete_resource_type_full (TrackerData *data,
if (direct_delete) {
if (multiple_values) {
- db_delete_row (iface, table_name, data->resource_buffer->id);
+ db_delete_row (iface, database, table_name, data->resource_buffer->id);
}
/* single-valued property values are deleted right after the loop by deleting the row in the class table */
continue;
@@ -2332,16 +2016,12 @@ cache_delete_resource_type_full (TrackerData *data,
for (y = old_values->len - 1; y >= 0 ; y--) {
GValue *old_gvalue;
- GValue gvalue = { 0 };
old_gvalue = &g_array_index (old_values, GValue, y);
- g_value_init (&gvalue, G_VALUE_TYPE (old_gvalue));
- g_value_copy (old_gvalue, &gvalue);
- value_set_remove_value (old_values, &gvalue);
+ value_set_remove_value (old_values, old_gvalue);
cache_delete_value (data, table_name, field_name,
- tracker_property_get_transient (prop),
- &gvalue, multiple_values,
+ old_gvalue, multiple_values,
tracker_property_get_fulltext_indexed (prop),
tracker_property_get_data_type (prop) == TRACKER_PROPERTY_TYPE_DATETIME);
@@ -2352,14 +2032,10 @@ cache_delete_resource_type_full (TrackerData *data,
domain_index_classes = tracker_property_get_domain_indexes (prop);
while (*domain_index_classes) {
if (resource_in_domain_index_class (data, *domain_index_classes)) {
- GValue gvalue_copy = { 0 };
- g_value_init (&gvalue_copy, G_VALUE_TYPE (&gvalue));
- g_value_copy (&gvalue, &gvalue_copy);
cache_delete_value (data,
tracker_class_get_name (*domain_index_classes),
field_name,
- tracker_property_get_transient (prop),
- &gvalue_copy, multiple_values,
+ old_gvalue, multiple_values,
tracker_property_get_fulltext_indexed (prop),
tracker_property_get_data_type (prop) == TRACKER_PROPERTY_TYPE_DATETIME);
}
@@ -2372,7 +2048,7 @@ cache_delete_resource_type_full (TrackerData *data,
if (direct_delete) {
/* delete row from class table */
- db_delete_row (iface, tracker_class_get_name (class), data->resource_buffer->id);
+ db_delete_row (iface, database, tracker_class_get_name (class), data->resource_buffer->id);
if (!single_type) {
/* delete row from rdfs:Resource_rdf:type table */
@@ -2394,23 +2070,20 @@ cache_delete_resource_type_full (TrackerData *data,
error = NULL;
}
}
-
- add_class_count (data, class, -1);
} else {
cache_delete_row (data, class);
}
- if (!data->in_journal_replay && data->delete_callbacks) {
+ if (data->delete_callbacks) {
guint n;
- gint final_graph_id;
-
- final_graph_id = (graph != NULL ? ensure_graph_id (data, graph, NULL) : graph_id);
for (n = 0; n < data->delete_callbacks->len; n++) {
TrackerStatementDelegate *delegate;
delegate = g_ptr_array_index (data->delete_callbacks, n);
- delegate->callback (final_graph_id, graph, data->resource_buffer->id, data->resource_buffer->subject,
+ delegate->callback (data->resource_buffer->graph->id,
+ data->resource_buffer->graph->graph,
+ data->resource_buffer->id, data->resource_buffer->subject,
tracker_property_get_id (tracker_ontologies_get_rdf_type (ontologies)),
tracker_class_get_id (class),
tracker_class_get_uri (class),
@@ -2424,11 +2097,42 @@ cache_delete_resource_type_full (TrackerData *data,
static void
cache_delete_resource_type (TrackerData *data,
- TrackerClass *class,
- const gchar *graph,
- gint graph_id)
+ TrackerClass *class)
{
- cache_delete_resource_type_full (data, class, graph, graph_id, FALSE);
+ cache_delete_resource_type_full (data, class, FALSE);
+}
+
+static TrackerDataUpdateBufferGraph *
+ensure_graph_buffer (TrackerDataUpdateBuffer *buffer,
+ TrackerData *data,
+ const gchar *name)
+{
+ TrackerDataUpdateBufferGraph *graph_buffer;
+ gint i;
+
+ for (i = 0; i < buffer->graphs->len; i++) {
+ graph_buffer = g_ptr_array_index (buffer->graphs, i);
+ if (g_strcmp0 (graph_buffer->graph, name) == 0)
+ return graph_buffer;
+ }
+
+ if (name && !tracker_data_manager_find_graph (data->manager, name)) {
+ tracker_data_manager_create_graph (data->manager, name, NULL);
+ }
+
+ graph_buffer = g_slice_new0 (TrackerDataUpdateBufferGraph);
+ graph_buffer->graph = g_strdup (name);
+ if (graph_buffer->graph) {
+ graph_buffer->id = tracker_data_manager_find_graph (data->manager,
+ graph_buffer->graph);
+ }
+
+ graph_buffer->resources =
+ g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+ (GDestroyNotify) resource_buffer_free);
+ g_ptr_array_add (buffer->graphs, graph_buffer);
+
+ return graph_buffer;
}
static void
@@ -2437,30 +2141,30 @@ resource_buffer_switch (TrackerData *data,
const gchar *subject,
gint subject_id)
{
- if (data->in_journal_replay) {
- /* journal replay only provides subject id
- resource_buffer->subject is only used in error messages and callbacks
- both should never occur when in journal replay */
- if (data->resource_buffer == NULL || data->resource_buffer->id != subject_id) {
- /* switch subject */
- data->resource_buffer = g_hash_table_lookup (data->update_buffer.resources_by_id, GINT_TO_POINTER (subject_id));
- }
- } else {
- if (data->resource_buffer == NULL || strcmp (data->resource_buffer->subject, subject) != 0) {
- /* switch subject */
- data->resource_buffer = g_hash_table_lookup (data->update_buffer.resources, subject);
- }
+ TrackerDataUpdateBufferGraph *graph_buffer;
+
+ if (data->resource_buffer != NULL &&
+ g_strcmp0 (data->resource_buffer->graph->graph, graph) == 0 &&
+ strcmp (data->resource_buffer->subject, subject) == 0) {
+ /* Resource buffer stays the same */
+ return;
}
+ /* large INSERTs with thousands of resources could lead to
+ high peak memory usage due to the update buffer
+ flush the buffer if it already contains 1000 resources */
+ tracker_data_update_buffer_might_flush (data, NULL);
+
+ data->resource_buffer = NULL;
+
+ graph_buffer = ensure_graph_buffer (&data->update_buffer, data, graph);
+ data->resource_buffer =
+ g_hash_table_lookup (graph_buffer->resources, subject);
+
if (data->resource_buffer == NULL) {
TrackerDataUpdateBufferResource *resource_buffer;
gchar *subject_dup = NULL;
- /* large INSERTs with thousands of resources could lead to
- high peak memory usage due to the update buffer
- flush the buffer if it already contains 1000 resources */
- tracker_data_update_buffer_might_flush (data, NULL);
-
/* subject not yet in cache, retrieve or create ID */
resource_buffer = g_slice_new0 (TrackerDataUpdateBufferResource);
if (subject != NULL) {
@@ -2478,21 +2182,13 @@ resource_buffer_switch (TrackerData *data,
if (resource_buffer->create) {
resource_buffer->types = g_ptr_array_new ();
} else {
- resource_buffer->types = tracker_data_query_rdf_type (data->manager, resource_buffer->id);
+ resource_buffer->types = tracker_data_query_rdf_type (data->manager, graph, resource_buffer->id);
}
resource_buffer->predicates = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, (GDestroyNotify) g_array_unref);
resource_buffer->tables = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) cache_table_free);
+ resource_buffer->graph = graph_buffer;
- if (data->in_journal_replay) {
- g_hash_table_insert (data->update_buffer.resources_by_id, GINT_TO_POINTER (subject_id), resource_buffer);
- } else {
- g_hash_table_insert (data->update_buffer.resources, subject_dup, resource_buffer);
-
- /* Ensure the graph gets an ID */
- if (graph != NULL) {
- ensure_graph_id (data, graph, NULL);
- }
- }
+ g_hash_table_insert (graph_buffer->resources, subject_dup, resource_buffer);
data->resource_buffer = resource_buffer;
}
@@ -2503,14 +2199,14 @@ tracker_data_delete_statement (TrackerData *data,
const gchar *graph,
const gchar *subject,
const gchar *predicate,
- const gchar *object,
+ GBytes *object,
GError **error)
{
TrackerClass *class;
- gint subject_id = 0;
+ gint subject_id = 0, graph_id = 0;
gboolean change = FALSE;
TrackerOntologies *ontologies;
- TrackerDBInterface *iface;
+ const gchar *object_str;
g_return_if_fail (subject != NULL);
g_return_if_fail (predicate != NULL);
@@ -2526,98 +2222,45 @@ tracker_data_delete_statement (TrackerData *data,
resource_buffer_switch (data, graph, subject, subject_id);
ontologies = tracker_data_manager_get_ontologies (data->manager);
- iface = tracker_data_manager_get_writable_db_interface (data->manager);
+
+ if (graph)
+ graph_id = tracker_data_manager_find_graph (data->manager, graph);
+
+ object_str = g_bytes_get_data (object, NULL);
if (object && g_strcmp0 (predicate, TRACKER_PREFIX_RDF "type") == 0) {
- class = tracker_ontologies_get_class_by_uri (ontologies, object);
+ class = tracker_ontologies_get_class_by_uri (ontologies, object_str);
if (class != NULL) {
data->has_persistent = TRUE;
-
-#ifndef DISABLE_JOURNAL
- if (!data->in_journal_replay) {
- tracker_db_journal_append_delete_statement_id (
- data->journal_writer,
- (graph != NULL ? query_resource_id (data, graph) : 0),
- data->resource_buffer->id,
- tracker_data_query_resource_id (data->manager, iface, predicate),
- tracker_class_get_id (class));
- }
-#endif /* DISABLE_JOURNAL */
-
- cache_delete_resource_type (data, class, graph, 0);
+ cache_delete_resource_type (data, class);
} else {
g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_UNKNOWN_CLASS,
- "Class '%s' not found in the ontology", object);
+ "Class '%s' not found in the ontology", object_str);
}
} else {
- gint pred_id = 0, graph_id = 0, object_id = 0;
- gboolean tried = FALSE;
+ gint pred_id = 0, object_id = 0;
TrackerProperty *field;
field = tracker_ontologies_get_property_by_uri (ontologies, predicate);
if (field != NULL) {
- if (!tracker_property_get_transient (field)) {
- data->has_persistent = TRUE;
- }
+ GError *inner_error = NULL;
+ GValue object_value = G_VALUE_INIT;
- change = delete_metadata_decomposed (data, field, object, 0, error);
- if (!data->in_journal_replay && change && !tracker_property_get_transient (field)) {
- if (tracker_property_get_data_type (field) == TRACKER_PROPERTY_TYPE_RESOURCE) {
-
- graph_id = (graph != NULL ? query_resource_id (data, graph) : 0);
- pred_id = tracker_property_get_id (field);
- object_id = query_resource_id (data, object);
- tried = TRUE;
-
-#ifndef DISABLE_JOURNAL
- tracker_db_journal_append_delete_statement_id (data->journal_writer,
- graph_id,
- data->resource_buffer->id,
- pred_id,
- object_id);
-#endif /* DISABLE_JOURNAL */
- } else {
- pred_id = tracker_property_get_id (field);
- graph_id = (graph != NULL ? query_resource_id (data, graph) : 0);
- object_id = 0;
- tried = TRUE;
-
-#ifndef DISABLE_JOURNAL
- if (!tracker_property_get_force_journal (field) &&
- g_strcmp0 (graph, TRACKER_OWN_GRAPH_URN) == 0) {
- /* do not journal this statement extracted from filesystem */
- TrackerProperty *damaged;
-
- damaged = tracker_ontologies_get_property_by_uri (ontologies, TRACKER_PREFIX_TRACKER "damaged");
-
- tracker_db_journal_append_insert_statement (data->journal_writer,
- graph_id,
- data->resource_buffer->id,
- tracker_property_get_id (damaged),
- "true");
- } else {
- tracker_db_journal_append_delete_statement (data->journal_writer,
- graph_id,
- data->resource_buffer->id,
- pred_id,
- object);
- }
-#endif /* DISABLE_JOURNAL */
- }
+ pred_id = tracker_property_get_id (field);
+ data->has_persistent = TRUE;
+
+ bytes_to_gvalue (object, tracker_property_get_data_type (field), &object_value, data, &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return;
}
+
+ change = delete_metadata_decomposed (data, field, &object_value, error);
+ g_value_unset (&object_value);
} else {
- /* I wonder why in case of error the delete_callbacks are still executed */
g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_UNKNOWN_PROPERTY,
"Property '%s' not found in the ontology", predicate);
- }
-
- if (!tried) {
- graph_id = (graph != NULL ? query_resource_id (data, graph) : 0);
- if (field == NULL) {
- pred_id = tracker_data_query_resource_id (data->manager, iface, predicate);
- } else {
- pred_id = tracker_property_get_id (field);
- }
+ return;
}
if (data->delete_callbacks && change) {
@@ -2628,7 +2271,7 @@ tracker_data_delete_statement (TrackerData *data,
delegate = g_ptr_array_index (data->delete_callbacks, n);
delegate->callback (graph_id, graph, subject_id, subject,
pred_id, object_id,
- object,
+ object_str,
data->resource_buffer->types,
delegate->user_data);
}
@@ -2667,10 +2310,7 @@ delete_all_objects (TrackerData *data,
if (field != NULL) {
GArray *old_values;
- if (!tracker_property_get_transient (field)) {
- data->has_persistent = TRUE;
- }
-
+ data->has_persistent = TRUE;
old_values = get_old_property_values (data, field, &new_error);
if (new_error) {
g_propagate_error (error, new_error);
@@ -2680,7 +2320,7 @@ delete_all_objects (TrackerData *data,
while (old_values->len > 0) {
GError *new_error = NULL;
- change |= delete_first_object (data, field, old_values, graph, &new_error);
+ change |= delete_first_object (data, field, old_values, &new_error);
if (new_error) {
g_propagate_error (error, new_error);
@@ -2693,63 +2333,12 @@ delete_all_objects (TrackerData *data,
}
}
-static gboolean
-tracker_data_insert_statement_common (TrackerData *data,
- const gchar *graph,
- const gchar *subject,
- const gchar *predicate,
- const gchar *object,
- GError **error)
-{
- if (g_str_has_prefix (subject, ":")) {
- /* blank node definition
- pile up statements until the end of the blank node */
- gchar *value;
- GError *actual_error = NULL;
-
- if (data->blank_buffer.subject != NULL) {
- /* active subject in buffer */
- if (strcmp (data->blank_buffer.subject, subject) != 0) {
- /* subject changed, need to flush buffer */
- tracker_data_blank_buffer_flush (data, &actual_error);
-
- if (actual_error) {
- g_propagate_error (error, actual_error);
- return FALSE;
- }
- }
- }
-
- if (data->blank_buffer.subject == NULL) {
- data->blank_buffer.subject = g_strdup (subject);
- if (data->blank_buffer.graphs == NULL) {
- data->blank_buffer.graphs = g_array_sized_new (FALSE, FALSE, sizeof (char*), 4);
- data->blank_buffer.predicates = g_array_sized_new (FALSE, FALSE, sizeof (char*), 4);
- data->blank_buffer.objects = g_array_sized_new (FALSE, FALSE, sizeof (char*), 4);
- }
- }
-
- value = g_strdup (graph);
- g_array_append_val (data->blank_buffer.graphs, value);
- value = g_strdup (predicate);
- g_array_append_val (data->blank_buffer.predicates, value);
- value = g_strdup (object);
- g_array_append_val (data->blank_buffer.objects, value);
-
- return FALSE;
- }
-
- resource_buffer_switch (data, graph, subject, 0);
-
- return TRUE;
-}
-
void
tracker_data_insert_statement (TrackerData *data,
const gchar *graph,
const gchar *subject,
const gchar *predicate,
- const gchar *object,
+ GBytes *object,
GError **error)
{
TrackerProperty *property;
@@ -2775,63 +2364,12 @@ tracker_data_insert_statement (TrackerData *data,
}
}
-
-static gboolean
-handle_blank_node (TrackerData *data,
- const gchar *subject,
- const gchar *predicate,
- const gchar *object,
- const gchar *graph,
- gboolean update,
- GError **error)
-{
- GError *actual_error = NULL;
- /* anonymous blank node used as object in a statement */
- const gchar *blank_uri;
-
- if (data->blank_buffer.subject != NULL) {
- if (strcmp (data->blank_buffer.subject, object) == 0) {
- /* object still in blank buffer, need to flush buffer */
- tracker_data_blank_buffer_flush (data, &actual_error);
-
- if (actual_error) {
- g_propagate_error (error, actual_error);
- return FALSE;
- }
- }
- }
-
- blank_uri = g_hash_table_lookup (data->blank_buffer.table, object);
-
- if (blank_uri != NULL) {
- /* now insert statement referring to blank node */
- if (update) {
- tracker_data_update_statement (data, graph, subject, predicate, blank_uri, &actual_error);
- } else {
- tracker_data_insert_statement (data, graph, subject, predicate, blank_uri, &actual_error);
- }
-
- g_hash_table_remove (data->blank_buffer.table, object);
-
- if (actual_error) {
- g_propagate_error (error, actual_error);
- return FALSE;
- }
-
- return TRUE;
- } else {
- g_critical ("Blank node '%s' not found", object);
-
- return FALSE;
- }
-}
-
void
tracker_data_insert_statement_with_uri (TrackerData *data,
const gchar *graph,
const gchar *subject,
const gchar *predicate,
- const gchar *object,
+ GBytes *object,
GError **error)
{
GError *actual_error = NULL;
@@ -2842,6 +2380,7 @@ tracker_data_insert_statement_with_uri (TrackerData *data,
gboolean change = FALSE;
TrackerOntologies *ontologies;
TrackerDBInterface *iface;
+ const gchar *object_str;
g_return_if_fail (subject != NULL);
g_return_if_fail (predicate != NULL);
@@ -2865,62 +2404,52 @@ tracker_data_insert_statement_with_uri (TrackerData *data,
prop_id = tracker_property_get_id (property);
}
- if (!tracker_property_get_transient (property)) {
- data->has_persistent = TRUE;
- }
+ data->has_persistent = TRUE;
- /* subjects and objects starting with `:' are anonymous blank nodes */
- if (g_str_has_prefix (object, ":")) {
- if (handle_blank_node (data, subject, predicate, object, graph, FALSE, &actual_error)) {
- return;
- }
+ resource_buffer_switch (data, graph, subject, 0);
- if (actual_error) {
- g_propagate_error (error, actual_error);
- return;
- }
- }
+ if (graph)
+ graph_id = tracker_data_manager_find_graph (data->manager, graph);
- if (!tracker_data_insert_statement_common (data, graph, subject, predicate, object, &actual_error)) {
- if (actual_error) {
- g_propagate_error (error, actual_error);
- return;
- }
-
- return;
- }
+ object_str = g_bytes_get_data (object, NULL);
if (property == tracker_ontologies_get_rdf_type (ontologies)) {
/* handle rdf:type statements specially to
cope with inference and insert blank rows */
- class = tracker_ontologies_get_class_by_uri (ontologies, object);
+ class = tracker_ontologies_get_class_by_uri (ontologies, object_str);
if (class != NULL) {
- cache_create_service_decomposed (data, class, graph, 0);
+ cache_create_service_decomposed (data, class);
} else {
g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_UNKNOWN_CLASS,
- "Class '%s' not found in the ontology", object);
+ "Class '%s' not found in the ontology", object_str);
return;
}
- if (!data->in_journal_replay && !tracker_property_get_transient (property)) {
- graph_id = (graph != NULL ? query_resource_id (data, graph) : 0);
- final_prop_id = (prop_id != 0) ? prop_id : tracker_data_query_resource_id (data->manager, iface, predicate);
- object_id = query_resource_id (data, object);
- }
+ final_prop_id = (prop_id != 0) ? prop_id : tracker_data_query_resource_id (data->manager, iface, predicate);
+ object_id = query_resource_id (data, object_str);
change = TRUE;
} else {
+ GValue object_value = G_VALUE_INIT;
+
+ bytes_to_gvalue (object, tracker_property_get_data_type (property), &object_value, data, &actual_error);
+ if (actual_error) {
+ g_propagate_error (error, actual_error);
+ return;
+ }
+
/* add value to metadata database */
- change = cache_insert_metadata_decomposed (data, property, object, 0, graph, 0, &actual_error);
+ change = cache_insert_metadata_decomposed (data, property, &object_value, &actual_error);
+ g_value_unset (&object_value);
+
if (actual_error) {
g_propagate_error (error, actual_error);
return;
}
if (change) {
- graph_id = (graph != NULL ? query_resource_id (data, graph) : 0);
final_prop_id = (prop_id != 0) ? prop_id : tracker_data_query_resource_id (data->manager, iface, predicate);
- object_id = query_resource_id (data, object);
+ object_id = query_resource_id (data, object_str);
if (data->insert_callbacks) {
guint n;
@@ -2930,25 +2459,13 @@ tracker_data_insert_statement_with_uri (TrackerData *data,
delegate = g_ptr_array_index (data->insert_callbacks, n);
delegate->callback (graph_id, graph, data->resource_buffer->id, subject,
final_prop_id, object_id,
- object,
+ object_str,
data->resource_buffer->types,
delegate->user_data);
}
}
}
}
-
-#ifndef DISABLE_JOURNAL
- if (!data->in_journal_replay && change && !tracker_property_get_transient (property)) {
- tracker_db_journal_append_insert_statement_id (
- data->journal_writer,
- (graph != NULL ? query_resource_id (data, graph) : 0),
- data->resource_buffer->id,
- final_prop_id,
- object_id);
- }
-#endif /* DISABLE_JOURNAL */
-
}
void
@@ -2956,7 +2473,7 @@ tracker_data_insert_statement_with_string (TrackerData *data,
const gchar *graph,
const gchar *subject,
const gchar *predicate,
- const gchar *object,
+ GBytes *object,
GError **error)
{
GError *actual_error = NULL;
@@ -2965,9 +2482,8 @@ tracker_data_insert_statement_with_string (TrackerData *data,
gint graph_id = 0, pred_id = 0;
TrackerOntologies *ontologies;
TrackerDBInterface *iface;
-#ifndef DISABLE_JOURNAL
- gboolean tried = FALSE;
-#endif
+ GValue object_value = G_VALUE_INIT;
+ const gchar *object_str;
g_return_if_fail (subject != NULL);
g_return_if_fail (predicate != NULL);
@@ -2991,21 +2507,23 @@ tracker_data_insert_statement_with_string (TrackerData *data,
pred_id = tracker_property_get_id (property);
}
- if (!tracker_property_get_transient (property)) {
- data->has_persistent = TRUE;
- }
+ data->has_persistent = TRUE;
- if (!tracker_data_insert_statement_common (data, graph, subject, predicate, object, &actual_error)) {
- if (actual_error) {
- g_propagate_error (error, actual_error);
- return;
- }
+ resource_buffer_switch (data, graph, subject, 0);
+
+ if (graph)
+ graph_id = tracker_data_manager_find_graph (data->manager, graph);
+ bytes_to_gvalue (object, tracker_property_get_data_type (property), &object_value, data, &actual_error);
+ if (actual_error) {
+ g_propagate_error (error, actual_error);
return;
}
/* add value to metadata database */
- change = cache_insert_metadata_decomposed (data, property, object, 0, graph, 0, &actual_error);
+ change = cache_insert_metadata_decomposed (data, property, &object_value, &actual_error);
+ g_value_unset (&object_value);
+
if (actual_error) {
g_propagate_error (error, actual_error);
return;
@@ -3014,11 +2532,8 @@ tracker_data_insert_statement_with_string (TrackerData *data,
if (data->insert_callbacks && change) {
guint n;
- graph_id = (graph != NULL ? query_resource_id (data, graph) : 0);
pred_id = (pred_id != 0) ? pred_id : tracker_data_query_resource_id (data->manager, iface, predicate);
-#ifndef DISABLE_JOURNAL
- tried = TRUE;
-#endif
+ object_str = g_bytes_get_data (object, NULL);
for (n = 0; n < data->insert_callbacks->len; n++) {
TrackerStatementDelegate *delegate;
@@ -3026,38 +2541,11 @@ tracker_data_insert_statement_with_string (TrackerData *data,
delegate = g_ptr_array_index (data->insert_callbacks, n);
delegate->callback (graph_id, graph, data->resource_buffer->id, subject,
pred_id, 0 /* Always a literal */,
- object,
+ object_str,
data->resource_buffer->types,
delegate->user_data);
}
}
-
-#ifndef DISABLE_JOURNAL
- if (!data->in_journal_replay && change && !tracker_property_get_transient (property)) {
- if (!tried) {
- graph_id = (graph != NULL ? query_resource_id (data, graph) : 0);
- pred_id = (pred_id != 0) ? pred_id : tracker_data_query_resource_id (data->manager, iface, predicate);
- }
- if (!tracker_property_get_force_journal (property) &&
- g_strcmp0 (graph, TRACKER_OWN_GRAPH_URN) == 0) {
- /* do not journal this statement extracted from filesystem */
- TrackerProperty *damaged;
-
- damaged = tracker_ontologies_get_property_by_uri (ontologies, TRACKER_PREFIX_TRACKER "damaged");
- tracker_db_journal_append_insert_statement (data->journal_writer,
- graph_id,
- data->resource_buffer->id,
- tracker_property_get_id (damaged),
- "true");
- } else {
- tracker_db_journal_append_insert_statement (data->journal_writer,
- graph_id,
- data->resource_buffer->id,
- pred_id,
- object);
- }
- }
-#endif /* DISABLE_JOURNAL */
}
static void
@@ -3065,7 +2553,7 @@ tracker_data_update_statement_with_uri (TrackerData *data,
const gchar *graph,
const gchar *subject,
const gchar *predicate,
- const gchar *object,
+ GBytes *object,
GError **error)
{
GError *actual_error = NULL;
@@ -3076,6 +2564,7 @@ tracker_data_update_statement_with_uri (TrackerData *data,
gboolean change = FALSE;
TrackerOntologies *ontologies;
TrackerDBInterface *iface;
+ const gchar *object_str;
g_return_if_fail (subject != NULL);
g_return_if_fail (predicate != NULL);
@@ -3098,50 +2587,30 @@ tracker_data_update_statement_with_uri (TrackerData *data,
prop_id = tracker_property_get_id (property);
}
- if (!tracker_property_get_transient (property)) {
- data->has_persistent = TRUE;
- }
+ data->has_persistent = TRUE;
- /* subjects and objects starting with `:' are anonymous blank nodes */
- if (g_str_has_prefix (object, ":")) {
- if (handle_blank_node (data, subject, predicate, object, graph, TRUE, &actual_error)) {
- return;
- }
-
- if (actual_error) {
- g_propagate_error (error, actual_error);
- return;
- }
- }
+ resource_buffer_switch (data, graph, subject, 0);
- /* Update and insert share the exact same code here */
- if (!tracker_data_insert_statement_common (data, graph, subject, predicate, object, &actual_error)) {
- if (actual_error) {
- g_propagate_error (error, actual_error);
- return;
- }
+ if (graph)
+ graph_id = tracker_data_manager_find_graph (data->manager, graph);
- return;
- }
+ object_str = g_bytes_get_data (object, NULL);
if (property == tracker_ontologies_get_rdf_type (ontologies)) {
/* handle rdf:type statements specially to
cope with inference and insert blank rows */
- class = tracker_ontologies_get_class_by_uri (ontologies, object);
+ class = tracker_ontologies_get_class_by_uri (ontologies, object_str);
if (class != NULL) {
/* Create here is fine for Update too */
- cache_create_service_decomposed (data, class, graph, 0);
+ cache_create_service_decomposed (data, class);
} else {
g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_UNKNOWN_CLASS,
- "Class '%s' not found in the ontology", object);
+ "Class '%s' not found in the ontology", object_str);
return;
}
- if (!data->in_journal_replay && !tracker_property_get_transient (property)) {
- graph_id = (graph != NULL ? query_resource_id (data, graph) : 0);
- final_prop_id = (prop_id != 0) ? prop_id : tracker_data_query_resource_id (data->manager, iface, predicate);
- object_id = query_resource_id (data, object);
- }
+ final_prop_id = (prop_id != 0) ? prop_id : tracker_data_query_resource_id (data->manager, iface, predicate);
+ object_id = query_resource_id (data, object_str);
change = TRUE;
} else {
@@ -3150,6 +2619,7 @@ tracker_data_update_statement_with_uri (TrackerData *data,
gboolean multiple_values;
GError *new_error = NULL;
gboolean domain_unchecked = TRUE;
+ GValue object_value = G_VALUE_INIT;
multiple_values = tracker_property_get_multiple_values (property);
@@ -3213,17 +2683,24 @@ tracker_data_update_statement_with_uri (TrackerData *data,
return;
}
+ bytes_to_gvalue (object, tracker_property_get_data_type (property), &object_value, data, &new_error);
+ if (new_error) {
+ g_propagate_error (error, new_error);
+ return;
+ }
+
/* update or add value to metadata database */
- change = cache_update_metadata_decomposed (data, property, object, 0, graph, 0, &actual_error);
+ change = cache_update_metadata_decomposed (data, property, &object_value, graph, &actual_error);
+ g_value_unset (&object_value);
+
if (actual_error) {
g_propagate_error (error, actual_error);
return;
}
if (change) {
- graph_id = (graph != NULL ? query_resource_id (data, graph) : 0);
final_prop_id = (prop_id != 0) ? prop_id : tracker_data_query_resource_id (data->manager, iface, predicate);
- object_id = query_resource_id (data, object);
+ object_id = query_resource_id (data, object_str);
if (!multiple_values && data->delete_callbacks) {
guint n;
@@ -3250,24 +2727,13 @@ tracker_data_update_statement_with_uri (TrackerData *data,
delegate = g_ptr_array_index (data->insert_callbacks, n);
delegate->callback (graph_id, graph, data->resource_buffer->id, subject,
final_prop_id, object_id,
- object,
+ object_str,
data->resource_buffer->types,
delegate->user_data);
}
}
}
}
-
-#ifndef DISABLE_JOURNAL
- if (!data->in_journal_replay && change && !tracker_property_get_transient (property)) {
- tracker_db_journal_append_update_statement_id (
- data->journal_writer,
- (graph != NULL ? query_resource_id (data, graph) : 0),
- data->resource_buffer->id,
- final_prop_id,
- object_id);
- }
-#endif /* DISABLE_JOURNAL */
}
static void
@@ -3275,7 +2741,7 @@ tracker_data_update_statement_with_string (TrackerData *data,
const gchar *graph,
const gchar *subject,
const gchar *predicate,
- const gchar *object,
+ GBytes *object,
GError **error)
{
GError *actual_error = NULL;
@@ -3285,12 +2751,9 @@ tracker_data_update_statement_with_string (TrackerData *data,
gboolean multiple_values;
TrackerOntologies *ontologies;
TrackerDBInterface *iface;
-#ifndef DISABLE_JOURNAL
- gboolean tried = FALSE;
-#endif
-#if HAVE_TRACKER_FTS
+ GValue object_value = G_VALUE_INIT;
+ const gchar *object_str;
GError *new_error = NULL;
-#endif /* HAVE_TRACKER_FTS */
g_return_if_fail (subject != NULL);
g_return_if_fail (predicate != NULL);
@@ -3315,19 +2778,9 @@ tracker_data_update_statement_with_string (TrackerData *data,
multiple_values = tracker_property_get_multiple_values (property);
- if (!tracker_property_get_transient (property)) {
- data->has_persistent = TRUE;
- }
-
- /* Update and insert share the exact same code here */
- if (!tracker_data_insert_statement_common (data, graph, subject, predicate, object, &actual_error)) {
- if (actual_error) {
- g_propagate_error (error, actual_error);
- return;
- }
+ data->has_persistent = TRUE;
- return;
- }
+ resource_buffer_switch (data, graph, subject, 0);
#if HAVE_TRACKER_FTS
/* This is unavoidable with FTS */
@@ -3347,19 +2800,28 @@ tracker_data_update_statement_with_string (TrackerData *data,
}
#endif /* HAVE_TRACKER_FTS */
+ bytes_to_gvalue (object, tracker_property_get_data_type (property), &object_value, data, &new_error);
+ if (new_error) {
+ g_propagate_error (error, new_error);
+ return;
+ }
+
/* add or update value to metadata database */
- change = cache_update_metadata_decomposed (data, property, object, 0, graph, 0, &actual_error);
+ change = cache_update_metadata_decomposed (data, property, &object_value, graph, &actual_error);
+ g_value_unset (&object_value);
+
if (actual_error) {
g_propagate_error (error, actual_error);
return;
}
+ if (graph)
+ graph_id = tracker_data_manager_find_graph (data->manager, graph);
+
+ object_str = g_bytes_get_data (object, NULL);
+
if (((!multiple_values && data->delete_callbacks) || data->insert_callbacks) && change) {
- graph_id = (graph != NULL ? query_resource_id (data, graph) : 0);
pred_id = (pred_id != 0) ? pred_id : tracker_data_query_resource_id (data->manager, iface, predicate);
-#ifndef DISABLE_JOURNAL
- tried = TRUE;
-#endif
}
if ((!multiple_values && data->delete_callbacks) && change) {
@@ -3386,38 +2848,11 @@ tracker_data_update_statement_with_string (TrackerData *data,
delegate = g_ptr_array_index (data->insert_callbacks, n);
delegate->callback (graph_id, graph, data->resource_buffer->id, subject,
pred_id, 0 /* Always a literal */,
- object,
+ object_str,
data->resource_buffer->types,
delegate->user_data);
}
}
-
-#ifndef DISABLE_JOURNAL
- if (!data->in_journal_replay && change && !tracker_property_get_transient (property)) {
- if (!tried) {
- graph_id = (graph != NULL ? query_resource_id (data, graph) : 0);
- pred_id = (pred_id != 0) ? pred_id : tracker_data_query_resource_id (data->manager, iface, predicate);
- }
- if (!tracker_property_get_force_journal (property) &&
- g_strcmp0 (graph, TRACKER_OWN_GRAPH_URN) == 0) {
- /* do not journal this statement extracted from filesystem */
- TrackerProperty *damaged;
-
- damaged = tracker_ontologies_get_property_by_uri (ontologies, TRACKER_PREFIX_TRACKER "damaged");
- tracker_db_journal_append_update_statement (data->journal_writer,
- graph_id,
- data->resource_buffer->id,
- tracker_property_get_id (damaged),
- "true");
- } else {
- tracker_db_journal_append_update_statement (data->journal_writer,
- graph_id,
- data->resource_buffer->id,
- pred_id,
- object);
- }
- }
-#endif /* DISABLE_JOURNAL */
}
void
@@ -3425,7 +2860,7 @@ tracker_data_update_statement (TrackerData *data,
const gchar *graph,
const gchar *subject,
const gchar *predicate,
- const gchar *object,
+ GBytes *object,
GError **error)
{
TrackerProperty *property;
@@ -3502,15 +2937,10 @@ tracker_data_begin_transaction (TrackerData *data,
if (data->update_buffer.resource_cache == NULL) {
data->update_buffer.resource_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
/* used for normal transactions */
- data->update_buffer.resources = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) resource_buffer_free);
- /* used for journal replay */
- data->update_buffer.resources_by_id = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) resource_buffer_free);
+ data->update_buffer.graphs = g_ptr_array_new_with_free_func ((GDestroyNotify) graph_buffer_free);
}
data->resource_buffer = NULL;
- if (data->blank_buffer.table == NULL) {
- data->blank_buffer.table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
- }
iface = tracker_data_manager_get_writable_db_interface (data->manager);
@@ -3518,18 +2948,6 @@ tracker_data_begin_transaction (TrackerData *data,
tracker_db_interface_start_transaction (iface);
-#ifndef DISABLE_JOURNAL
- if (!data->in_journal_replay) {
- g_assert (data->journal_writer == NULL);
- /* Pick the right journal writer for this transaction */
- data->journal_writer = data->in_ontology_transaction ?
- tracker_data_manager_get_ontology_writer (data->manager) :
- tracker_data_manager_get_journal_writer (data->manager);
-
- tracker_db_journal_start_transaction (data->journal_writer, data->resource_time);
- }
-#endif /* DISABLE_JOURNAL */
-
data->in_transaction = TRUE;
}
@@ -3542,16 +2960,6 @@ tracker_data_begin_ontology_transaction (TrackerData *data,
}
void
-tracker_data_begin_transaction_for_replay (TrackerData *data,
- time_t time,
- GError **error)
-{
- data->in_journal_replay = TRUE;
- tracker_data_begin_transaction (data, error);
- data->resource_time = time;
-}
-
-void
tracker_data_commit_transaction (TrackerData *data,
GError **error)
{
@@ -3578,28 +2986,6 @@ tracker_data_commit_transaction (TrackerData *data,
return;
}
-#ifndef DISABLE_JOURNAL
- if (!data->in_journal_replay) {
- g_assert (data->journal_writer != NULL);
- if (data->has_persistent || data->in_ontology_transaction) {
- tracker_db_journal_commit_db_transaction (data->journal_writer, &actual_error);
- } else {
- /* If we only had transient properties, then we must not write
- * anything to the journal. So we roll it back, but only the
- * journal's part. */
- tracker_db_journal_rollback_transaction (data->journal_writer);
- }
-
- data->journal_writer = NULL;
-
- if (actual_error) {
- /* Can't write in journal anymore; quite a serious problem */
- g_propagate_error (error, actual_error);
- /* Don't return, remainder of the function cleans things up */
- }
- }
-#endif /* DISABLE_JOURNAL */
-
get_transaction_modseq (data);
if (data->has_persistent && !data->in_ontology_transaction) {
data->transaction_modseq++;
@@ -3609,12 +2995,6 @@ tracker_data_commit_transaction (TrackerData *data,
data->in_transaction = FALSE;
data->in_ontology_transaction = FALSE;
- if (data->update_buffer.class_counts) {
- /* successful transaction, no need to rollback class counts,
- so remove them */
- g_hash_table_remove_all (data->update_buffer.class_counts);
- }
-
#if HAVE_TRACKER_FTS
if (data->update_buffer.fts_ever_updated) {
data->update_buffer.fts_ever_updated = FALSE;
@@ -3623,11 +3003,10 @@ tracker_data_commit_transaction (TrackerData *data,
tracker_db_interface_execute_query (iface, NULL, "PRAGMA cache_size = %d", TRACKER_DB_CACHE_SIZE_DEFAULT);
- g_hash_table_remove_all (data->update_buffer.resources);
- g_hash_table_remove_all (data->update_buffer.resources_by_id);
+ g_ptr_array_set_size (data->update_buffer.graphs, 0);
g_hash_table_remove_all (data->update_buffer.resource_cache);
- if (!data->in_journal_replay && data->commit_callbacks) {
+ if (data->commit_callbacks) {
guint n;
for (n = 0; n < data->commit_callbacks->len; n++) {
TrackerCommitDelegate *delegate;
@@ -3635,8 +3014,6 @@ tracker_data_commit_transaction (TrackerData *data,
delegate->callback (delegate->user_data);
}
}
-
- data->in_journal_replay = FALSE;
}
void
@@ -3663,22 +3040,12 @@ tracker_data_rollback_transaction (TrackerData *data)
tracker_db_interface_execute_query (iface, NULL, "PRAGMA cache_size = %d", TRACKER_DB_CACHE_SIZE_DEFAULT);
- /* Runtime false in case of DISABLE_JOURNAL */
- if (!data->in_journal_replay) {
-
-#ifndef DISABLE_JOURNAL
- g_assert (data->journal_writer != NULL);
- tracker_db_journal_rollback_transaction (data->journal_writer);
- data->journal_writer = NULL;
-#endif /* DISABLE_JOURNAL */
-
- if (data->rollback_callbacks) {
- guint n;
- for (n = 0; n < data->rollback_callbacks->len; n++) {
- TrackerCommitDelegate *delegate;
- delegate = g_ptr_array_index (data->rollback_callbacks, n);
- delegate->callback (delegate->user_data);
- }
+ if (data->rollback_callbacks) {
+ guint n;
+ for (n = 0; n < data->rollback_callbacks->len; n++) {
+ TrackerCommitDelegate *delegate;
+ delegate = g_ptr_array_index (data->rollback_callbacks, n);
+ delegate->callback (delegate->user_data);
}
}
}
@@ -3739,358 +3106,116 @@ tracker_data_update_sparql_blank (TrackerData *data,
void
tracker_data_load_turtle_file (TrackerData *data,
GFile *file,
+ const gchar *graph,
GError **error)
{
- g_return_if_fail (G_IS_FILE (file));
+ TrackerTurtleReader *reader = NULL;
+ GError *inner_error = NULL;
+ gboolean in_transaction = FALSE;
- tracker_turtle_reader_load (file, data, error);
-}
+ tracker_data_begin_transaction (data, &inner_error);
+ if (inner_error)
+ goto failed;
-void
-tracker_data_sync (TrackerData *data)
-{
-#ifndef DISABLE_JOURNAL
- TrackerDBJournal *writer;
+ in_transaction = TRUE;
+ reader = tracker_turtle_reader_new (file, &inner_error);
+ if (inner_error)
+ goto failed;
- writer = tracker_data_manager_get_journal_writer (data->manager);
- if (writer)
- tracker_db_journal_fsync (writer);
+ while (tracker_turtle_reader_next (reader, &inner_error)) {
+ const gchar *object_str;
+ GBytes *object;
- writer = tracker_data_manager_get_ontology_writer (data->manager);
- if (writer)
- tracker_db_journal_fsync (writer);
-#endif
-}
+ object_str = tracker_turtle_reader_get_object (reader);
+ object = g_bytes_new (object_str, strlen (object_str) + 1);
-#ifndef DISABLE_JOURNAL
+ if (tracker_turtle_reader_get_object_is_uri (reader)) {
+ tracker_data_insert_statement_with_uri (data, graph,
+ tracker_turtle_reader_get_subject (reader),
+ tracker_turtle_reader_get_predicate (reader),
+ object,
+ &inner_error);
+ } else {
+ tracker_data_insert_statement_with_string (data, graph,
+ tracker_turtle_reader_get_subject (reader),
+ tracker_turtle_reader_get_predicate (reader),
+ object,
+ &inner_error);
+ }
-void
-tracker_data_replay_journal (TrackerData *data,
- TrackerBusyCallback busy_callback,
- gpointer busy_user_data,
- const gchar *busy_status,
- GError **error)
-{
- GError *journal_error = NULL;
- TrackerProperty *rdf_type = NULL;
- gint last_operation_type = 0;
- const gchar *uri;
- GError *n_error = NULL;
- GFile *data_location;
- TrackerDBJournalReader *reader;
- TrackerOntologies *ontologies;
+ g_bytes_unref (object);
- ontologies = tracker_data_manager_get_ontologies (data->manager);
- rdf_type = tracker_ontologies_get_rdf_type (ontologies);
+ if (inner_error)
+ goto failed;
- data_location = tracker_data_manager_get_data_location (data->manager);
- reader = tracker_db_journal_reader_new (data_location, &n_error);
- g_object_unref (data_location);
+ tracker_data_update_buffer_might_flush (data, &inner_error);
- if (!reader) {
- /* This is fatal (doesn't happen when file doesn't exist, does happen
- * when for some other reason the reader can't be created) */
- g_propagate_error (error, n_error);
- return;
+ if (inner_error)
+ goto failed;
}
- while (tracker_db_journal_reader_next (reader, &journal_error)) {
- TrackerDBJournalEntryType type;
- const gchar *object;
- gint graph_id, subject_id, predicate_id, object_id;
-
- type = tracker_db_journal_reader_get_entry_type (reader);
- if (type == TRACKER_DB_JOURNAL_RESOURCE) {
- GError *new_error = NULL;
- TrackerDBInterface *iface;
- TrackerDBStatement *stmt;
- gint id;
-
- tracker_db_journal_reader_get_resource (reader, &id, &uri);
-
- iface = tracker_data_manager_get_writable_db_interface (data->manager);
-
- stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, &new_error,
- "INSERT INTO Resource (ID, Uri) VALUES (?, ?)");
-
- if (stmt) {
- tracker_db_statement_bind_int (stmt, 0, id);
- tracker_db_statement_bind_text (stmt, 1, uri);
- tracker_db_statement_execute (stmt, &new_error);
- g_object_unref (stmt);
- }
-
- if (new_error) {
- g_warning ("Journal replay error: '%s'", new_error->message);
- g_error_free (new_error);
- }
-
- } else if (type == TRACKER_DB_JOURNAL_START_TRANSACTION) {
- tracker_data_begin_transaction_for_replay (data, tracker_db_journal_reader_get_time (reader), NULL);
- } else if (type == TRACKER_DB_JOURNAL_END_TRANSACTION) {
- GError *new_error = NULL;
- tracker_data_update_buffer_might_flush (data, &new_error);
-
- tracker_data_commit_transaction (data, &new_error);
- if (new_error) {
- /* Out of disk is an unrecoverable fatal error */
- if (g_error_matches (new_error, TRACKER_DB_INTERFACE_ERROR, TRACKER_DB_NO_SPACE)) {
- g_propagate_error (error, new_error);
- return;
- } else {
- g_warning ("Journal replay error: '%s'", new_error->message);
- g_clear_error (&new_error);
- }
- }
- } else if (type == TRACKER_DB_JOURNAL_INSERT_STATEMENT ||
- type == TRACKER_DB_JOURNAL_UPDATE_STATEMENT) {
- GError *new_error = NULL;
- TrackerProperty *property = NULL;
-
- tracker_db_journal_reader_get_statement (reader, &graph_id, &subject_id, &predicate_id, &object);
-
- if (last_operation_type == -1) {
- tracker_data_update_buffer_flush (data, &new_error);
- if (new_error) {
- g_warning ("Journal replay error: '%s'", new_error->message);
- g_clear_error (&new_error);
- }
- }
- last_operation_type = 1;
-
- uri = tracker_ontologies_get_uri_by_id (ontologies, predicate_id);
- if (uri) {
- property = tracker_ontologies_get_property_by_uri (ontologies, uri);
- }
-
- if (property) {
- resource_buffer_switch (data, NULL, NULL, subject_id);
-
- if (type == TRACKER_DB_JOURNAL_UPDATE_STATEMENT) {
- cache_update_metadata_decomposed (data, property, object, 0, NULL, graph_id, &new_error);
- } else {
- cache_insert_metadata_decomposed (data, property, object, 0, NULL, graph_id, &new_error);
- }
- if (new_error) {
- g_warning ("Journal replay error: '%s'", new_error->message);
- g_clear_error (&new_error);
- }
-
- } else {
- g_warning ("Journal replay error: 'property with ID %d doesn't exist'", predicate_id);
- }
-
- } else if (type == TRACKER_DB_JOURNAL_INSERT_STATEMENT_ID ||
- type == TRACKER_DB_JOURNAL_UPDATE_STATEMENT_ID) {
- GError *new_error = NULL;
- TrackerClass *class = NULL;
- TrackerProperty *property = NULL;
-
- tracker_db_journal_reader_get_statement_id (reader, &graph_id, &subject_id, &predicate_id, &object_id);
-
- if (last_operation_type == -1) {
- tracker_data_update_buffer_flush (data, &new_error);
- if (new_error) {
- g_warning ("Journal replay error: '%s'", new_error->message);
- g_clear_error (&new_error);
- }
- }
- last_operation_type = 1;
-
- uri = tracker_ontologies_get_uri_by_id (ontologies, predicate_id);
- if (uri) {
- property = tracker_ontologies_get_property_by_uri (ontologies, uri);
- }
-
- if (property) {
- if (tracker_property_get_data_type (property) != TRACKER_PROPERTY_TYPE_RESOURCE) {
- g_warning ("Journal replay error: 'property with ID %d does not account URIs'", predicate_id);
- } else {
- resource_buffer_switch (data, NULL, NULL, subject_id);
-
- if (property == rdf_type) {
- uri = tracker_ontologies_get_uri_by_id (ontologies, object_id);
- if (uri) {
- class = tracker_ontologies_get_class_by_uri (ontologies, uri);
- }
- if (class) {
- cache_create_service_decomposed (data, class, NULL, graph_id);
- } else {
- g_warning ("Journal replay error: 'class with ID %d not found in the ontology'", object_id);
- }
- } else {
- GError *new_error = NULL;
-
- /* add value to metadata database */
- if (type == TRACKER_DB_JOURNAL_UPDATE_STATEMENT_ID) {
- cache_update_metadata_decomposed (data, property, NULL, object_id, NULL, graph_id, &new_error);
- } else {
- cache_insert_metadata_decomposed (data, property, NULL, object_id, NULL, graph_id, &new_error);
- }
-
- if (new_error) {
- g_warning ("Journal replay error: '%s'", new_error->message);
- g_error_free (new_error);
- }
- }
- }
- } else {
- g_warning ("Journal replay error: 'property with ID %d doesn't exist'", predicate_id);
- }
-
- } else if (type == TRACKER_DB_JOURNAL_DELETE_STATEMENT) {
- GError *new_error = NULL;
- TrackerProperty *property = NULL;
-
- tracker_db_journal_reader_get_statement (reader, &graph_id, &subject_id, &predicate_id, &object);
-
- if (last_operation_type == 1) {
- tracker_data_update_buffer_flush (data, &new_error);
- if (new_error) {
- g_warning ("Journal replay error: '%s'", new_error->message);
- g_clear_error (&new_error);
- }
- }
- last_operation_type = -1;
-
- resource_buffer_switch (data, NULL, NULL, subject_id);
-
- uri = tracker_ontologies_get_uri_by_id (ontologies, predicate_id);
- if (uri) {
- property = tracker_ontologies_get_property_by_uri (ontologies, uri);
- }
-
- if (property) {
- GError *new_error = NULL;
-
- if (object && rdf_type == property) {
- TrackerClass *class;
-
- class = tracker_ontologies_get_class_by_uri (ontologies, object);
- if (class != NULL) {
- cache_delete_resource_type (data, class, NULL, graph_id);
- } else {
- g_warning ("Journal replay error: 'class with '%s' not found in the ontology'", object);
- }
- } else {
- delete_metadata_decomposed (data, property, object, 0, &new_error);
- }
+ if (inner_error)
+ goto failed;
- if (new_error) {
- g_warning ("Journal replay error: '%s'", new_error->message);
- g_error_free (new_error);
- }
-
- } else {
- g_warning ("Journal replay error: 'property with ID %d doesn't exist'", predicate_id);
- }
-
- } else if (type == TRACKER_DB_JOURNAL_DELETE_STATEMENT_ID) {
- GError *new_error = NULL;
- TrackerClass *class = NULL;
- TrackerProperty *property = NULL;
-
- tracker_db_journal_reader_get_statement_id (reader, &graph_id, &subject_id, &predicate_id, &object_id);
-
- if (last_operation_type == 1) {
- tracker_data_update_buffer_flush (data, &new_error);
- if (new_error) {
- g_warning ("Journal replay error: '%s'", new_error->message);
- g_clear_error (&new_error);
- }
- }
- last_operation_type = -1;
-
- uri = tracker_ontologies_get_uri_by_id (ontologies, predicate_id);
- if (uri) {
- property = tracker_ontologies_get_property_by_uri (ontologies, uri);
- }
-
- if (property) {
-
- resource_buffer_switch (data, NULL, NULL, subject_id);
-
- if (property == rdf_type) {
- uri = tracker_ontologies_get_uri_by_id (ontologies, object_id);
- if (uri) {
- class = tracker_ontologies_get_class_by_uri (ontologies, uri);
- }
- if (class) {
- cache_delete_resource_type (data, class, NULL, graph_id);
- } else {
- g_warning ("Journal replay error: 'class with ID %d not found in the ontology'", object_id);
- }
- } else {
- GError *new_error = NULL;
+ tracker_data_commit_transaction (data, &inner_error);
+ if (inner_error)
+ goto failed;
- delete_metadata_decomposed (data, property, NULL, object_id, &new_error);
+ g_clear_object (&reader);
- if (new_error) {
- g_warning ("Journal replay error: '%s'", new_error->message);
- g_error_free (new_error);
- }
- }
- } else {
- g_warning ("Journal replay error: 'property with ID %d doesn't exist'", predicate_id);
- }
- }
+ return;
- if (busy_callback) {
- busy_callback (busy_status,
- tracker_db_journal_reader_get_progress (reader),
- busy_user_data);
- }
- }
+failed:
+ if (in_transaction)
+ tracker_data_rollback_transaction (data);
+ g_clear_object (&reader);
- if (journal_error) {
- GError *n_error = NULL;
- gsize size;
- GFile *cache_location, *data_location;
- TrackerDBJournal *writer;
+ g_propagate_error (error, inner_error);
+}
- size = tracker_db_journal_reader_get_size_of_correct (reader);
- tracker_db_journal_reader_free (reader);
+gint
+tracker_data_ensure_graph (TrackerData *data,
+ const gchar *uri,
+ GError **error)
+{
+ TrackerDBInterface *iface;
+ TrackerDBStatement *stmt;
+ gint id;
- cache_location = tracker_data_manager_get_cache_location(data->manager);
- data_location = tracker_data_manager_get_data_location (data->manager);
+ id = ensure_resource_id (data, uri, NULL);
+ iface = tracker_data_manager_get_writable_db_interface (data->manager);
+ stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, error,
+ "INSERT OR IGNORE INTO Graph (ID) VALUES (?)");
+ if (!stmt)
+ return 0;
- writer = tracker_db_journal_new (data_location, FALSE, &n_error);
- g_object_unref (cache_location);
- g_object_unref (data_location);
+ tracker_db_statement_bind_int (stmt, 0, id);
+ tracker_db_statement_execute (stmt, error);
+ g_object_unref (stmt);
- if (n_error) {
- g_clear_error (&journal_error);
- /* This is fatal (journal file not writable, etc) */
- g_propagate_error (error, n_error);
- return;
- }
- tracker_db_journal_truncate (writer, size);
- tracker_db_journal_free (writer, &n_error);
+ return id;
+}
- if (n_error) {
- g_clear_error (&journal_error);
- /* This is fatal (close of journal file failed after truncate) */
- g_propagate_error (error, n_error);
- return;
- }
+gboolean
+tracker_data_delete_graph (TrackerData *data,
+ const gchar *uri,
+ GError **error)
+{
+ TrackerDBInterface *iface;
+ TrackerDBStatement *stmt;
+ gint id;
- g_clear_error (&journal_error);
- } else {
- tracker_db_journal_reader_free (reader);
- }
-}
+ id = query_resource_id (data, uri);
+ iface = tracker_data_manager_get_writable_db_interface (data->manager);
+ stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, error,
+ "DELETE FROM Graph WHERE ID = ?");
+ if (!stmt)
+ return FALSE;
-#else
+ tracker_db_statement_bind_int (stmt, 0, id);
+ tracker_db_statement_execute (stmt, error);
+ g_object_unref (stmt);
-void
-tracker_data_replay_journal (TrackerData *data,
- TrackerBusyCallback busy_callback,
- gpointer busy_user_data,
- const gchar *busy_status,
- GError **error)
-{
- g_critical ("Not good. We disabled the journal and yet replaying it got called");
+ return TRUE;
}
-
-#endif /* DISABLE_JOURNAL */
diff --git a/src/libtracker-data/tracker-data-update.h b/src/libtracker-data/tracker-data-update.h
index 97fdcd6a9..a7f60eaa5 100644
--- a/src/libtracker-data/tracker-data-update.h
+++ b/src/libtracker-data/tracker-data-update.h
@@ -68,39 +68,36 @@ void tracker_data_delete_statement (TrackerData *
const gchar *graph,
const gchar *subject,
const gchar *predicate,
- const gchar *object,
+ GBytes *object,
GError **error);
void tracker_data_insert_statement (TrackerData *data,
const gchar *graph,
const gchar *subject,
const gchar *predicate,
- const gchar *object,
+ GBytes *object,
GError **error);
void tracker_data_insert_statement_with_uri (TrackerData *data,
const gchar *graph,
const gchar *subject,
const gchar *predicate,
- const gchar *object,
+ GBytes *object,
GError **error);
void tracker_data_insert_statement_with_string (TrackerData *data,
const gchar *graph,
const gchar *subject,
const gchar *predicate,
- const gchar *object,
+ GBytes *object,
GError **error);
void tracker_data_update_statement (TrackerData *data,
const gchar *graph,
const gchar *subject,
const gchar *predicate,
- const gchar *object,
+ GBytes *object,
GError **error);
void tracker_data_begin_transaction (TrackerData *data,
GError **error);
void tracker_data_begin_ontology_transaction (TrackerData *data,
GError **error);
-void tracker_data_begin_transaction_for_replay (TrackerData *data,
- time_t time,
- GError **error);
void tracker_data_commit_transaction (TrackerData *data,
GError **error);
void tracker_data_rollback_transaction (TrackerData *data);
@@ -117,13 +114,16 @@ void tracker_data_update_buffer_might_flush (TrackerData *
GError **error);
void tracker_data_load_turtle_file (TrackerData *data,
GFile *file,
+ const gchar *graph,
GError **error);
void tracker_data_sync (TrackerData *data);
-void tracker_data_replay_journal (TrackerData *data,
- TrackerBusyCallback busy_callback,
- gpointer busy_user_data,
- const gchar *busy_status,
+
+gint tracker_data_ensure_graph (TrackerData *data,
+ const gchar *name,
+ GError **error);
+gboolean tracker_data_delete_graph (TrackerData *data,
+ const gchar *uri,
GError **error);
/* Calling back */
diff --git a/src/libtracker-data/tracker-data.h b/src/libtracker-data/tracker-data.h
index 7188774e0..dd443ffe4 100644
--- a/src/libtracker-data/tracker-data.h
+++ b/src/libtracker-data/tracker-data.h
@@ -34,7 +34,6 @@
#include "tracker-db-config.h"
#include "tracker-db-interface.h"
#include "tracker-db-interface-sqlite.h"
-#include "tracker-db-journal.h"
#include "tracker-db-manager.h"
#include "tracker-namespace.h"
#include "tracker-ontology.h"
diff --git a/src/libtracker-data/tracker-db-interface-sqlite.c b/src/libtracker-data/tracker-db-interface-sqlite.c
index 16d201d10..9bbb7b64f 100644
--- a/src/libtracker-data/tracker-db-interface-sqlite.c
+++ b/src/libtracker-data/tracker-db-interface-sqlite.c
@@ -56,6 +56,7 @@
#include "tracker-db-manager.h"
#include "tracker-data-enum-types.h"
#include "tracker-uuid.h"
+#include "tracker-vtab-service.h"
#include "tracker-vtab-triples.h"
typedef struct {
@@ -414,9 +415,154 @@ function_sparql_format_time (sqlite3_context *context,
int argc,
sqlite3_value *argv[])
{
- gdouble seconds;
- gchar *str;
+ if (argc != 1) {
+ sqlite3_result_error (context, "Invalid argument count", -1);
+ return;
+ }
+
+ if (sqlite3_value_type (argv[0]) == SQLITE_NULL) {
+ sqlite3_result_null (context);
+ return;
+ } else if (sqlite3_value_numeric_type (argv[0]) == SQLITE_INTEGER) {
+ gdouble seconds;
+ gchar *str;
+
+ seconds = sqlite3_value_double (argv[0]);
+ str = tracker_date_to_string (seconds, 0);
+ sqlite3_result_text (context, str, -1, g_free);
+ } else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT) {
+ const gchar *str;
+
+ str = sqlite3_value_text (argv[0]);
+ sqlite3_result_text (context, g_strdup (str), -1, g_free);
+ } else {
+ sqlite3_result_error (context, "Invalid argument type", -1);
+ }
+}
+
+static void
+function_sparql_timestamp (sqlite3_context *context,
+ int argc,
+ sqlite3_value *argv[])
+{
+ if (argc != 1) {
+ sqlite3_result_error (context, "Invalid argument count", -1);
+ return;
+ }
+
+ if (sqlite3_value_type (argv[0]) == SQLITE_NULL) {
+ sqlite3_result_null (context);
+ return;
+ } else if (sqlite3_value_numeric_type (argv[0]) == SQLITE_INTEGER) {
+ gdouble seconds;
+
+ seconds = sqlite3_value_double (argv[0]);
+ sqlite3_result_double (context, seconds);
+ } else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT) {
+ GError *error = NULL;
+ const gchar *str;
+ gdouble time;
+ gint offset;
+
+ str = sqlite3_value_text (argv[0]);
+ time = tracker_string_to_date (str, &offset, &error);
+
+ if (error) {
+ sqlite3_result_error (context, "Failed time string conversion", -1);
+ g_error_free (error);
+ return;
+ }
+
+ sqlite3_result_double (context, time + offset);
+ } else {
+ sqlite3_result_error (context, "Invalid argument type", -1);
+ }
+}
+
+static void
+function_sparql_time_sort (sqlite3_context *context,
+ int argc,
+ sqlite3_value *argv[])
+{
+ gint64 sort_key;
+
+ if (argc != 1) {
+ sqlite3_result_error (context, "Invalid argument count", -1);
+ return;
+ }
+
+ if (sqlite3_value_type (argv[0]) == SQLITE_NULL) {
+ sqlite3_result_null (context);
+ return;
+ } else if (sqlite3_value_numeric_type (argv[0]) == SQLITE_INTEGER ||
+ sqlite3_value_numeric_type (argv[0]) == SQLITE_FLOAT) {
+ gdouble value;
+
+ value = sqlite3_value_double (argv[0]);
+ sort_key = (gint64) (value * G_USEC_PER_SEC);
+ } else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT) {
+ const gchar *value;
+ gdouble time;
+ GError *error = NULL;
+
+ value = sqlite3_value_text (argv[0]);
+ time = tracker_string_to_date (value, NULL, &error);
+
+ if (error) {
+ sqlite3_result_error (context, "Failed time string conversion", -1);
+ g_error_free (error);
+ return;
+ }
+
+ sort_key = (gint64) (time * G_USEC_PER_SEC);
+ } else {
+ sqlite3_result_error (context, "Invalid argument type", -1);
+ return;
+ }
+
+ sqlite3_result_int64 (context, sort_key);
+}
+
+static void
+function_sparql_time_zone_duration (sqlite3_context *context,
+ int argc,
+ sqlite3_value *argv[])
+{
+ if (argc != 1) {
+ sqlite3_result_error (context, "Invalid argument count", -1);
+ return;
+ }
+
+ if (sqlite3_value_type (argv[0]) == SQLITE_NULL) {
+ sqlite3_result_null (context);
+ return;
+ } else if (sqlite3_value_numeric_type (argv[0]) == SQLITE_INTEGER) {
+ sqlite3_result_int (context, 0);
+ } else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT) {
+ GError *error = NULL;
+ const gchar *str;
+ gint offset;
+
+ str = sqlite3_value_text (argv[0]);
+ tracker_string_to_date (str, &offset, &error);
+
+ if (error) {
+ sqlite3_result_error (context, "Invalid date", -1);
+ g_error_free (error);
+ return;
+ }
+
+ sqlite3_result_int (context, offset);
+ } else {
+ sqlite3_result_error (context, "Invalid argument type", -1);
+ }
+}
+static void
+function_sparql_time_zone_substr (sqlite3_context *context,
+ int argc,
+ sqlite3_value *argv[])
+{
if (argc != 1) {
sqlite3_result_error (context, "Invalid argument count", -1);
return;
@@ -425,12 +571,97 @@ function_sparql_format_time (sqlite3_context *context,
if (sqlite3_value_type (argv[0]) == SQLITE_NULL) {
sqlite3_result_null (context);
return;
+ } else if (sqlite3_value_numeric_type (argv[0]) == SQLITE_INTEGER) {
+ sqlite3_result_text (context, "", -1, NULL);
+ } else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT) {
+ const gchar *str;
+ gint len;
+
+ str = sqlite3_value_text (argv[0]);
+ len = strlen (str);
+
+ if (g_str_has_suffix (str, "Z")) {
+ sqlite3_result_text (context, "Z", -1, NULL);
+ } else if (len > strlen ("0000-00-00T00:00:00Z")) {
+ const gchar *tz = "";
+
+ /* [+-]HHMM */
+ if (str[len - 5] == '+' || str[len - 5] == '-')
+ tz = &str[len - 5];
+ /* [+-]HH:MM */
+ else if (str[len - 6] == '+' || str[len - 6] == '-')
+ tz = &str[len - 6];
+
+ sqlite3_result_text (context, g_strdup (tz), -1, g_free);
+ } else {
+ sqlite3_result_text (context, "", -1, NULL);
+ }
+ } else {
+ sqlite3_result_error (context, "Invalid argument type", -1);
}
+}
+
+static gchar *
+offset_to_duration (gint offset)
+{
+ GString *str = g_string_new (NULL);
+ gint hours, minutes, seconds;
- seconds = sqlite3_value_double (argv[0]);
- str = tracker_date_to_string (seconds);
+ if (offset > 0)
+ g_string_append (str, "+PT");
+ else
+ g_string_append (str, "-PT");
- sqlite3_result_text (context, str, -1, g_free);
+ offset = ABS (offset);
+ hours = offset / 3600;
+ minutes = offset % 3600 / 60;
+ seconds = offset % 60;
+
+ if (hours > 0)
+ g_string_append_printf (str, "%dH", hours);
+ if (minutes > 0)
+ g_string_append_printf (str, "%dM", minutes);
+ if (seconds > 0)
+ g_string_append_printf (str, "%dS", seconds);
+
+ return g_string_free (str, FALSE);
+}
+
+static void
+function_sparql_time_zone (sqlite3_context *context,
+ int argc,
+ sqlite3_value *argv[])
+{
+ if (argc != 1) {
+ sqlite3_result_error (context, "Invalid argument count", -1);
+ return;
+ }
+
+ if (sqlite3_value_type (argv[0]) == SQLITE_NULL) {
+ sqlite3_result_null (context);
+ return;
+ } else if (sqlite3_value_numeric_type (argv[0]) == SQLITE_INTEGER) {
+ sqlite3_result_text (context, "PT0S", -1, NULL);
+ } else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT) {
+ GError *error = NULL;
+ const gchar *str;
+ gchar *duration;
+ gint offset;
+
+ str = sqlite3_value_text (argv[0]);
+ tracker_string_to_date (str, &offset, &error);
+
+ if (error) {
+ sqlite3_result_error (context, "Invalid date", -1);
+ g_error_free (error);
+ return;
+ }
+
+ duration = offset_to_duration (offset);
+ sqlite3_result_text (context, g_strdup (duration), -1, g_free);
+ } else {
+ sqlite3_result_error (context, "Invalid argument type", -1);
+ }
}
static void
@@ -1179,6 +1410,26 @@ function_sparql_encode_for_uri (sqlite3_context *context,
}
static void
+function_sparql_uri (sqlite3_context *context,
+ int argc,
+ sqlite3_value *argv[])
+{
+ const gchar *str;
+ gchar *encoded;
+
+ if (argc != 1) {
+ sqlite3_result_error (context, "Invalid argument count", -1);
+ return;
+ }
+
+ str = (gchar *)sqlite3_value_text (argv[0]);
+ encoded = g_uri_escape_string (str,
+ G_URI_RESERVED_CHARS_ALLOWED_IN_PATH,
+ FALSE);
+ sqlite3_result_text (context, encoded, -1, g_free);
+}
+
+static void
function_sparql_string_before (sqlite3_context *context,
int argc,
sqlite3_value *argv[])
@@ -1287,6 +1538,56 @@ function_sparql_floor (sqlite3_context *context,
}
static void
+function_sparql_data_type (sqlite3_context *context,
+ int argc,
+ sqlite3_value *argv[])
+{
+ TrackerPropertyType prop_type;
+ const gchar *type = NULL;
+
+ if (argc != 1) {
+ sqlite3_result_error (context, "Invalid argument count", -1);
+ return;
+ }
+
+ prop_type = sqlite3_value_int (argv[0]);
+
+ switch (prop_type) {
+ case TRACKER_PROPERTY_TYPE_UNKNOWN:
+ break;
+ case TRACKER_PROPERTY_TYPE_STRING:
+ type = "http://www.w3.org/2001/XMLSchema#string";
+ break;
+ case TRACKER_PROPERTY_TYPE_BOOLEAN:
+ type = "http://www.w3.org/2001/XMLSchema#boolean";
+ break;
+ case TRACKER_PROPERTY_TYPE_INTEGER:
+ type = "http://www.w3.org/2001/XMLSchema#integer";
+ break;
+ case TRACKER_PROPERTY_TYPE_DOUBLE:
+ type = "http://www.w3.org/2001/XMLSchema#double";
+ break;
+ case TRACKER_PROPERTY_TYPE_DATE:
+ type = "http://www.w3.org/2001/XMLSchema#date";
+ break;
+ case TRACKER_PROPERTY_TYPE_DATETIME:
+ type = "http://www.w3.org/2001/XMLSchema#dateType";
+ break;
+ case TRACKER_PROPERTY_TYPE_RESOURCE:
+ type = "http://www.w3.org/2000/01/rdf-schema#Resource";
+ break;
+ case TRACKER_PROPERTY_TYPE_LANGSTRING:
+ type = "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString";
+ break;
+ }
+
+ if (type)
+ sqlite3_result_text (context, type, -1, NULL);
+ else
+ sqlite3_result_null (context);
+}
+
+static void
function_sparql_rand (sqlite3_context *context,
int argc,
sqlite3_value *argv[])
@@ -1342,6 +1643,68 @@ function_sparql_checksum (sqlite3_context *context,
sqlite3_result_text (context, result, -1, g_free);
}
+static void
+function_sparql_langmatches (sqlite3_context *context,
+ int argc,
+ sqlite3_value *argv[])
+{
+ const gchar *str, *langtag;
+ gint type;
+
+ if (argc != 2) {
+ sqlite3_result_error (context, "Invalid argument count", -1);
+ return;
+ }
+
+ type = sqlite3_value_type (argv[0]);
+
+ if (type == SQLITE_TEXT) {
+ /* text arguments don't contain any language information */
+ sqlite3_result_int (context, FALSE);
+ } else if (type == SQLITE_BLOB) {
+ gint str_len, len;
+
+ str = sqlite3_value_blob (argv[0]);
+ len = sqlite3_value_bytes (argv[0]);
+ langtag = sqlite3_value_text (argv[1]);
+ str_len = strlen (str) + 1;
+
+ if (str_len + strlen (langtag) != len ||
+ g_strcmp0 (&str[str_len], langtag) != 0) {
+ sqlite3_result_int (context, FALSE);
+ } else {
+ sqlite3_result_int (context, TRUE);
+ }
+ } else {
+ sqlite3_result_null (context);
+ }
+}
+
+static void
+function_sparql_strlang (sqlite3_context *context,
+ int argc,
+ sqlite3_value *argv[])
+{
+ const gchar *str, *langtag;
+ GString *langstr;
+
+ if (argc != 2) {
+ sqlite3_result_error (context, "Invalid argument count", -1);
+ return;
+ }
+
+ str = sqlite3_value_text (argv[0]);
+ langtag = sqlite3_value_text (argv[1]);
+
+ langstr = g_string_new (str);
+ g_string_append_c (langstr, '\0');
+ g_string_append (langstr, langtag);
+
+ sqlite3_result_blob64 (context, langstr->str,
+ langstr->len, g_free);
+ g_string_free (langstr, FALSE);
+}
+
static inline int
stmt_step (sqlite3_stmt *stmt)
{
@@ -1369,20 +1732,14 @@ stmt_step (sqlite3_stmt *stmt)
}
static void
-function_sparql_uuid (sqlite3_context *context,
- int argc,
- sqlite3_value *argv[])
+generate_uuid (sqlite3_context *context,
+ const gchar *uri_prefix)
{
gchar *uuid = NULL;
sqlite3_stmt *stmt;
sqlite3 *db;
gint result;
- if (argc > 1) {
- sqlite3_result_error (context, "Invalid argument count", -1);
- return;
- }
-
db = sqlite3_context_db_handle (context);
result = sqlite3_prepare_v2 (db, "SELECT ID FROM Resource WHERE Uri=?",
@@ -1394,7 +1751,7 @@ function_sparql_uuid (sqlite3_context *context,
do {
g_clear_pointer (&uuid, g_free);
- uuid = tracker_generate_uuid ();
+ uuid = tracker_generate_uuid (uri_prefix);
sqlite3_reset (stmt);
sqlite3_bind_text (stmt, 1, uuid, -1, SQLITE_TRANSIENT);
@@ -1406,10 +1763,38 @@ function_sparql_uuid (sqlite3_context *context,
if (result != SQLITE_DONE) {
sqlite3_result_error (context, sqlite3_errstr (result), -1);
g_free (uuid);
+ } else {
+ sqlite3_result_text (context, uuid, -1, g_free);
+ }
+}
+
+static void
+function_sparql_uuid (sqlite3_context *context,
+ int argc,
+ sqlite3_value *argv[])
+{
+ const gchar *prefix;
+
+ if (argc > 1) {
+ sqlite3_result_error (context, "Invalid argument count", -1);
+ return;
+ }
+
+ prefix = sqlite3_value_text (argv[0]);
+ generate_uuid (context, prefix);
+}
+
+static void
+function_sparql_bnode (sqlite3_context *context,
+ int argc,
+ sqlite3_value *argv[])
+{
+ if (argc > 1) {
+ sqlite3_result_error (context, "Invalid argument count", -1);
return;
}
- sqlite3_result_text (context, uuid, -1, g_free);
+ generate_uuid (context, "urn:bnode");
}
static int
@@ -1438,6 +1823,16 @@ initialize_functions (TrackerDBInterface *db_interface)
/* Date/time */
{ "SparqlFormatTime", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
function_sparql_format_time },
+ { "SparqlTimestamp", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
+ function_sparql_timestamp },
+ { "SparqlTimeSort", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
+ function_sparql_time_sort },
+ { "SparqlTimezoneDuration", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
+ function_sparql_time_zone_duration },
+ { "SparqlTimezoneString", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
+ function_sparql_time_zone_substr },
+ { "SparqlTimezone", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
+ function_sparql_time_zone },
/* Paths and filenames */
{ "SparqlStringFromFilename", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
function_sparql_string_from_filename },
@@ -1447,6 +1842,8 @@ initialize_functions (TrackerDBInterface *db_interface)
function_sparql_uri_is_descendant },
{ "SparqlEncodeForUri", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
function_sparql_encode_for_uri },
+ { "SparqlUri", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
+ function_sparql_uri },
/* Strings */
{ "SparqlRegex", -1, SQLITE_ANY | SQLITE_DETERMINISTIC,
function_sparql_regex },
@@ -1470,14 +1867,22 @@ initialize_functions (TrackerDBInterface *db_interface)
function_sparql_replace },
{ "SparqlChecksum", 2, SQLITE_ANY | SQLITE_DETERMINISTIC,
function_sparql_checksum },
+ { "SparqlLangMatches", 2, SQLITE_ANY | SQLITE_DETERMINISTIC,
+ function_sparql_langmatches },
+ { "SparqlStrLang", 2, SQLITE_ANY | SQLITE_DETERMINISTIC,
+ function_sparql_strlang },
/* Numbers */
{ "SparqlCeil", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
function_sparql_ceil },
{ "SparqlFloor", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
function_sparql_floor },
{ "SparqlRand", 0, SQLITE_ANY, function_sparql_rand },
+ /* Types */
+ { "SparqlDataType", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
+ function_sparql_data_type },
/* UUID */
- { "SparqlUUID", 0, SQLITE_ANY, function_sparql_uuid },
+ { "SparqlUUID", 1, SQLITE_ANY, function_sparql_uuid },
+ { "SparqlBNODE", -1, SQLITE_ANY | SQLITE_DETERMINISTIC, function_sparql_bnode },
};
for (i = 0; i < G_N_ELEMENTS (functions); i++) {
@@ -1528,7 +1933,7 @@ open_database (TrackerDBInterface *db_interface,
"Could not open sqlite3 database:'%s': %s", db_interface->filename, str);
return;
} else {
- g_info ("Opened sqlite3 database:'%s'", db_interface->filename);
+ g_debug ("Opened sqlite3 database:'%s'", db_interface->filename);
}
/* Set our unicode collation function */
@@ -1675,6 +2080,7 @@ _fts_create_properties (GHashTable *properties)
void
tracker_db_interface_sqlite_fts_init (TrackerDBInterface *db_interface,
+ const gchar *database,
GHashTable *properties,
GHashTable *multivalued,
gboolean create)
@@ -1685,7 +2091,7 @@ tracker_db_interface_sqlite_fts_init (TrackerDBInterface *db_interface,
tracker_fts_init_db (db_interface->db, db_interface, properties);
if (create &&
- !tracker_fts_create_table (db_interface->db, "fts5",
+ !tracker_fts_create_table (db_interface->db, database, "fts5",
properties, multivalued)) {
g_warning ("FTS tables creation failed");
}
@@ -1714,32 +2120,36 @@ tracker_db_interface_sqlite_fts_init (TrackerDBInterface *db_interface,
#if HAVE_TRACKER_FTS
void
-tracker_db_interface_sqlite_fts_delete_table (TrackerDBInterface *db_interface)
+tracker_db_interface_sqlite_fts_delete_table (TrackerDBInterface *db_interface,
+ const gchar *database)
{
- if (!tracker_fts_delete_table (db_interface->db, "fts5")) {
+ if (!tracker_fts_delete_table (db_interface->db, database, "fts5")) {
g_critical ("Failed to delete FTS table");
}
}
void
tracker_db_interface_sqlite_fts_alter_table (TrackerDBInterface *db_interface,
- GHashTable *properties,
- GHashTable *multivalued)
+ const gchar *database,
+ GHashTable *properties,
+ GHashTable *multivalued)
{
- if (!tracker_fts_alter_table (db_interface->db, "fts5", properties, multivalued)) {
+ if (!tracker_fts_alter_table (db_interface->db, database, "fts5", properties, multivalued)) {
g_critical ("Failed to update FTS columns");
}
}
static gchar *
tracker_db_interface_sqlite_fts_create_query (TrackerDBInterface *db_interface,
+ const gchar *database,
gboolean delete,
const gchar **properties)
{
GString *insert_str, *values_str;
gint i;
- insert_str = g_string_new ("INSERT INTO fts5 (");
+ insert_str = g_string_new (NULL);
+ g_string_append_printf (insert_str, "INSERT INTO \"%s\".fts5 (", database);
values_str = g_string_new (NULL);
if (delete) {
@@ -1762,15 +2172,17 @@ tracker_db_interface_sqlite_fts_create_query (TrackerDBInterface *db_interface,
}
static gchar *
-tracker_db_interface_sqlite_fts_create_delete_all_query (TrackerDBInterface *db_interface)
+tracker_db_interface_sqlite_fts_create_delete_all_query (TrackerDBInterface *db_interface,
+ const gchar *database)
{
GString *insert_str;
insert_str = g_string_new (NULL);
g_string_append_printf (insert_str,
- "INSERT INTO fts5 (fts5, rowid %s) "
+ "INSERT INTO \"%s\".fts5 (fts5, rowid %s) "
"SELECT 'delete', rowid %s FROM fts_view "
"WHERE rowid = ?",
+ database,
db_interface->fts_properties,
db_interface->fts_properties);
return g_string_free (insert_str, FALSE);
@@ -1778,6 +2190,7 @@ tracker_db_interface_sqlite_fts_create_delete_all_query (TrackerDBInterface *db_
gboolean
tracker_db_interface_sqlite_fts_update_text (TrackerDBInterface *db_interface,
+ const gchar *database,
int id,
const gchar **properties,
const gchar **text)
@@ -1788,6 +2201,7 @@ tracker_db_interface_sqlite_fts_update_text (TrackerDBInterface *db_interface,
gint i;
query = tracker_db_interface_sqlite_fts_create_query (db_interface,
+ database,
FALSE, properties);
stmt = tracker_db_interface_create_statement (db_interface,
TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE,
@@ -1823,6 +2237,7 @@ tracker_db_interface_sqlite_fts_update_text (TrackerDBInterface *db_interface,
gboolean
tracker_db_interface_sqlite_fts_delete_text (TrackerDBInterface *db_interface,
+ const gchar *database,
int rowid,
const gchar *property,
const gchar *old_text)
@@ -1833,6 +2248,7 @@ tracker_db_interface_sqlite_fts_delete_text (TrackerDBInterface *db_interface,
gchar *query;
query = tracker_db_interface_sqlite_fts_create_query (db_interface,
+ database,
TRUE, properties);
stmt = tracker_db_interface_create_statement (db_interface,
TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE,
@@ -1863,13 +2279,14 @@ tracker_db_interface_sqlite_fts_delete_text (TrackerDBInterface *db_interface,
gboolean
tracker_db_interface_sqlite_fts_delete_id (TrackerDBInterface *db_interface,
+ const gchar *database,
int id)
{
TrackerDBStatement *stmt;
GError *error = NULL;
gchar *query;
- query = tracker_db_interface_sqlite_fts_create_delete_all_query (db_interface);
+ query = tracker_db_interface_sqlite_fts_create_delete_all_query (db_interface, database);
stmt = tracker_db_interface_create_statement (db_interface,
TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE,
&error,
@@ -1899,9 +2316,10 @@ tracker_db_interface_sqlite_fts_delete_id (TrackerDBInterface *db_interface,
}
void
-tracker_db_interface_sqlite_fts_rebuild_tokens (TrackerDBInterface *interface)
+tracker_db_interface_sqlite_fts_rebuild_tokens (TrackerDBInterface *interface,
+ const gchar *database)
{
- tracker_fts_rebuild_tokens (interface->db, "fts5");
+ tracker_fts_rebuild_tokens (interface->db, database, "fts5");
}
#endif
@@ -1962,6 +2380,11 @@ tracker_db_interface_sqlite_wal_checkpoint (TrackerDBInterface *interface,
GError **error)
{
int return_val;
+ GLogLevelFlags log_level;
+
+ log_level = blocking ? G_LOG_LEVEL_INFO : G_LOG_LEVEL_DEBUG;
+
+ g_log (G_LOG_DOMAIN, log_level, "Checkpointing database (%s)...", blocking ? "blocking" : "non-blocking");
return_val = sqlite3_wal_checkpoint_v2 (interface->db, NULL,
blocking ? SQLITE_CHECKPOINT_FULL : SQLITE_CHECKPOINT_PASSIVE,
@@ -1975,6 +2398,7 @@ tracker_db_interface_sqlite_wal_checkpoint (TrackerDBInterface *interface,
return FALSE;
}
+ g_log (G_LOG_DOMAIN, log_level, "Checkpointing complete");
return TRUE;
}
@@ -1988,7 +2412,7 @@ tracker_db_interface_sqlite_finalize (GObject *object)
close_database (db_interface);
g_free (db_interface->fts_properties);
- g_info ("Closed sqlite3 database:'%s'", db_interface->filename);
+ g_debug ("Closed sqlite3 database:'%s'", db_interface->filename);
g_free (db_interface->filename);
@@ -2290,7 +2714,7 @@ tracker_db_interface_create_statement (TrackerDBInterface *db_interfac
return g_object_ref_sink (stmt);
}
-static void
+static gboolean
execute_stmt (TrackerDBInterface *interface,
sqlite3_stmt *stmt,
GCancellable *cancellable,
@@ -2339,19 +2763,7 @@ execute_stmt (TrackerDBInterface *interface,
g_critical ("SQLite error: %s (errno: %s)",
sqlite3_errmsg (interface->db),
g_strerror (errno));
-
-#ifndef DISABLE_JOURNAL
- g_unlink (interface->filename);
-
- g_error ("SQLite experienced an error with file:'%s'. "
- "It is either NOT a SQLite database or it is "
- "corrupt or there was an IO error accessing the data. "
- "This file has now been removed and will be recreated on the next start. "
- "Shutting down now.",
- interface->filename);
-
- return;
-#endif /* DISABLE_JOURNAL */
+ return FALSE;
}
if (!error) {
@@ -2376,6 +2788,8 @@ execute_stmt (TrackerDBInterface *interface,
}
}
}
+
+ return result == SQLITE_DONE;
}
void
@@ -2722,6 +3136,25 @@ tracker_db_statement_bind_text (TrackerDBStatement *stmt,
}
void
+tracker_db_statement_bind_bytes (TrackerDBStatement *stmt,
+ int index,
+ GBytes *value)
+{
+ gconstpointer data;
+ gsize len;
+
+ g_return_if_fail (TRACKER_IS_DB_STATEMENT (stmt));
+
+ g_assert (!stmt->stmt_is_used);
+
+ data = g_bytes_get_data (value, &len);
+
+ tracker_db_interface_lock (stmt->db_interface);
+ sqlite3_bind_blob (stmt->stmt, index + 1, data, len - 1, SQLITE_TRANSIENT);
+ tracker_db_interface_unlock (stmt->db_interface);
+}
+
+void
tracker_db_statement_bind_value (TrackerDBStatement *stmt,
int index,
const GValue *value)
@@ -2747,6 +3180,15 @@ tracker_db_statement_bind_value (TrackerDBStatement *stmt,
} else if (type == G_TYPE_STRING) {
sqlite3_bind_text (stmt->stmt, index + 1,
g_value_get_string (value), -1, SQLITE_TRANSIENT);
+ } else if (type == G_TYPE_BYTES) {
+ GBytes *bytes;
+ gconstpointer data;
+ gsize len;
+
+ bytes = g_value_get_boxed (value);
+ data = g_bytes_get_data (bytes, &len);
+ sqlite3_bind_text (stmt->stmt, index + 1,
+ data, len, SQLITE_TRANSIENT);
} else {
GValue dest = G_VALUE_INIT;
@@ -3089,5 +3531,50 @@ tracker_db_interface_init_vtabs (TrackerDBInterface *db_interface,
TrackerOntologies *ontologies)
{
tracker_vtab_triples_init (db_interface->db, ontologies);
+ tracker_vtab_service_init (db_interface->db, ontologies);
return TRUE;
}
+
+gboolean
+tracker_db_interface_attach_database (TrackerDBInterface *db_interface,
+ GFile *file,
+ const gchar *name,
+ GError **error)
+{
+ gchar *sql, *path;
+ sqlite3_stmt *stmt;
+ gboolean retval;
+
+ path = g_file_get_path (file);
+ sql = g_strdup_printf ("ATTACH DATABASE \"%s\" AS \"%s\"", path, name);
+ g_free (path);
+
+ stmt = tracker_db_interface_prepare_stmt (db_interface, sql, error);
+ g_free (sql);
+ if (!stmt)
+ return FALSE;
+
+ retval = execute_stmt (db_interface, stmt, NULL, error);
+ sqlite3_finalize (stmt);
+ return retval;
+}
+
+gboolean
+tracker_db_interface_detach_database (TrackerDBInterface *db_interface,
+ const gchar *name,
+ GError **error)
+{
+ sqlite3_stmt *stmt;
+ gboolean retval;
+ gchar *sql;
+
+ sql = g_strdup_printf ("DETACH DATABASE \"%s\"", name);
+
+ stmt = tracker_db_interface_prepare_stmt (db_interface, sql, error);
+ if (!stmt)
+ return FALSE;
+
+ retval = execute_stmt (db_interface, stmt, NULL, error);
+ sqlite3_finalize (stmt);
+ return retval;
+}
diff --git a/src/libtracker-data/tracker-db-interface-sqlite.h b/src/libtracker-data/tracker-db-interface-sqlite.h
index 4608bac94..81127539a 100644
--- a/src/libtracker-data/tracker-db-interface-sqlite.h
+++ b/src/libtracker-data/tracker-db-interface-sqlite.h
@@ -48,6 +48,7 @@ TrackerDBInterface *tracker_db_interface_sqlite_new (const gc
gint64 tracker_db_interface_sqlite_get_last_insert_id (TrackerDBInterface *interface);
void tracker_db_interface_sqlite_enable_shared_cache (void);
void tracker_db_interface_sqlite_fts_init (TrackerDBInterface *interface,
+ const gchar *database,
GHashTable *properties,
GHashTable *multivalued,
gboolean create);
@@ -62,29 +63,43 @@ gboolean tracker_db_interface_init_vtabs (TrackerD
TrackerOntologies *ontologies);
#if HAVE_TRACKER_FTS
-void tracker_db_interface_sqlite_fts_delete_table (TrackerDBInterface *interface);
+void tracker_db_interface_sqlite_fts_delete_table (TrackerDBInterface *interface,
+ const gchar *database);
void tracker_db_interface_sqlite_fts_alter_table (TrackerDBInterface *interface,
+ const gchar *database,
GHashTable *properties,
GHashTable *multivalued);
gboolean tracker_db_interface_sqlite_fts_update_text (TrackerDBInterface *db_interface,
+ const gchar *database,
int id,
const gchar **properties,
const gchar **text);
gboolean tracker_db_interface_sqlite_fts_delete_text (TrackerDBInterface *interface,
+ const gchar *database,
int rowid,
const gchar *property,
const gchar *old_text);
gboolean tracker_db_interface_sqlite_fts_delete_id (TrackerDBInterface *interface,
+ const gchar *database,
int rowid);
void tracker_db_interface_sqlite_fts_update_commit (TrackerDBInterface *interface);
void tracker_db_interface_sqlite_fts_update_rollback (TrackerDBInterface *interface);
-void tracker_db_interface_sqlite_fts_rebuild_tokens (TrackerDBInterface *interface);
+void tracker_db_interface_sqlite_fts_rebuild_tokens (TrackerDBInterface *interface,
+ const gchar *database);
#endif
+gboolean tracker_db_interface_attach_database (TrackerDBInterface *db_interface,
+ GFile *file,
+ const gchar *name,
+ GError **error);
+gboolean tracker_db_interface_detach_database (TrackerDBInterface *db_interface,
+ const gchar *name,
+ GError **error);
+
G_END_DECLS
#endif /* __LIBTRACKER_DB_INTERFACE_SQLITE_H__ */
diff --git a/src/libtracker-data/tracker-db-interface.h b/src/libtracker-data/tracker-db-interface.h
index 287738afb..26d194e8e 100644
--- a/src/libtracker-data/tracker-db-interface.h
+++ b/src/libtracker-data/tracker-db-interface.h
@@ -128,6 +128,9 @@ void tracker_db_statement_bind_null (TrackerDBS
void tracker_db_statement_bind_text (TrackerDBStatement *stmt,
int index,
const gchar *value);
+void tracker_db_statement_bind_bytes (TrackerDBStatement *stmt,
+ int index,
+ GBytes *value);
void tracker_db_statement_bind_value (TrackerDBStatement *stmt,
int index,
const GValue *value);
diff --git a/src/libtracker-data/tracker-db-journal.c b/src/libtracker-data/tracker-db-journal.c
deleted file mode 100644
index 0c5711494..000000000
--- a/src/libtracker-data/tracker-db-journal.c
+++ /dev/null
@@ -1,2165 +0,0 @@
-/*
- * Copyright (C) 2009, Nokia <ivan.frade@nokia.com>
- *
- * 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.
- *
- * Author: Philip Van Hoof <philip@codeminded.be>
- */
-
-#include "config.h"
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-
-#include <glib/gstdio.h>
-
-#ifndef O_LARGEFILE
-# define O_LARGEFILE 0
-#endif
-
-#include "tracker-crc32.h"
-#include "tracker-db-journal.h"
-
-#ifndef DISABLE_JOURNAL
-
-#define MIN_BLOCK_SIZE 1024
-
-/*
- * data_format:
- * #... 0000 0000 (total size is 4 bytes)
- * | |||`- resource insert (all other bits must be 0 if 1)
- * | ||`-- object type (1 = id, 0 = cstring)
- * | |`--- operation type (0 = insert, 1 = delete)
- * | `---- graph (0 = default graph, 1 = named graph)
- * `------ update (0 = insert, 1 = update)
- */
-
-typedef enum {
- DATA_FORMAT_RESOURCE_INSERT = 1 << 0,
- DATA_FORMAT_OBJECT_ID = 1 << 1,
- DATA_FORMAT_OPERATION_DELETE = 1 << 2,
- DATA_FORMAT_GRAPH = 1 << 3,
- DATA_FORMAT_OPERATION_UPDATE = 1 << 4
-} DataFormat;
-
-typedef enum {
- TRANSACTION_FORMAT_NONE = 0,
- TRANSACTION_FORMAT_DATA = 1 << 0,
- TRANSACTION_FORMAT_ONTOLOGY = 1 << 1,
-} TransactionFormat;
-
-struct _TrackerDBJournalReader {
- gchar *filename;
- GFile *journal_location;
- GDataInputStream *stream;
- GInputStream *underlying_stream;
- GFileInfo *underlying_stream_info;
- GMappedFile *file;
- const gchar *current;
- const gchar *end;
- const gchar *entry_begin;
- const gchar *entry_end;
- const gchar *last_success;
- const gchar *start;
- guint32 amount_of_triples;
- gint64 time;
- TrackerDBJournalEntryType type;
- gchar *uri;
- gint g_id;
- gint s_id;
- gint p_id;
- gint o_id;
- gchar *object;
- guint current_file;
- guint total_chunks;
-};
-
-struct _TrackerDBJournal {
- gchar *journal_filename;
- GFile *journal_location;
- int journal;
- gsize cur_size;
- guint cur_block_len;
- guint cur_block_alloc;
- gchar *cur_block;
- guint cur_entry_amount;
- guint cur_pos;
-
- TransactionFormat transaction_format;
- gboolean in_transaction;
- gint cur_journal_file;
-};
-
-static struct {
- gsize chunk_size;
- gboolean do_rotating;
- gchar *rotate_to;
- gboolean rotate_progress_flag;
-} rotating_settings = {0};
-
-static gboolean tracker_db_journal_rotate (TrackerDBJournal *jwriter,
- GError **error);
-
-#ifndef HAVE_STRNLEN
-
-size_t
-strnlen (const char *str, size_t max)
-{
- const char *end = memchr (str, 0, max);
- return end ? (size_t)(end - str) : max;
-}
-
-#endif /* HAVE_STRNLEN */
-
-static gboolean
-journal_eof (TrackerDBJournalReader *jreader)
-{
- if (jreader->stream) {
- GBufferedInputStream *bstream;
-
- bstream = G_BUFFERED_INPUT_STREAM (jreader->stream);
-
- if (g_buffered_input_stream_get_available (bstream) == 0) {
- if (g_buffered_input_stream_fill (bstream, -1, NULL, NULL) == 0) {
- return TRUE;
- }
- }
- } else {
- if (jreader->current >= jreader->end) {
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-static guint32
-read_uint32 (const guint8 *data)
-{
- return data[0] << 24 |
- data[1] << 16 |
- data[2] << 8 |
- data[3];
-}
-
-static guint32
-journal_read_uint32 (TrackerDBJournalReader *jreader,
- GError **error)
-{
- guint32 result;
-
- if (jreader->stream) {
- result = g_data_input_stream_read_uint32 (jreader->stream, NULL, error);
- } else {
- if (jreader->end - jreader->current < sizeof (guint32)) {
- /* damaged journal entry */
- g_set_error (error, TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_DAMAGED_JOURNAL_ENTRY,
- "Damaged journal entry, %d < sizeof(guint32)",
- (gint) (jreader->end - jreader->current));
- return 0;
- }
-
- result = read_uint32 (jreader->current);
- jreader->current += 4;
- }
-
- return result;
-}
-
-/* based on GDataInputStream code */
-static gssize
-scan_for_nul (GBufferedInputStream *stream,
- gsize *checked_out)
-{
- const gchar *buffer;
- gsize start, end, peeked;
- gint i;
- gsize available, checked;
-
- checked = *checked_out;
-
- start = checked;
- buffer = (const gchar *) g_buffered_input_stream_peek_buffer (stream, &available) + start;
- end = available;
- peeked = end - start;
-
- for (i = 0; checked < available && i < peeked; i++) {
- if (buffer[i] == '\0') {
- return (start + i);
- }
- }
-
- checked = end;
-
- *checked_out = checked;
- return -1;
-}
-
-static gchar *
-journal_read_string (TrackerDBJournalReader *jreader,
- GError **error)
-{
- gchar *result;
-
- if (jreader->stream) {
- /* based on GDataInputStream code */
-
- GBufferedInputStream *bstream;
- gsize checked;
- gssize found_pos;
-
- bstream = G_BUFFERED_INPUT_STREAM (jreader->stream);
-
- checked = 0;
-
- while ((found_pos = scan_for_nul (bstream, &checked)) == -1) {
- if (g_buffered_input_stream_get_available (bstream) == g_buffered_input_stream_get_buffer_size (bstream)) {
- g_buffered_input_stream_set_buffer_size (bstream, 2 * g_buffered_input_stream_get_buffer_size (bstream));
- }
-
- if (g_buffered_input_stream_fill (bstream, -1, NULL, error) <= 0) {
- /* error or end of stream */
- g_set_error (error, TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_DAMAGED_JOURNAL_ENTRY,
- "Damaged journal entry, no terminating zero found");
- return NULL;
- }
- }
-
- result = g_malloc (found_pos + 1);
-
- if (g_input_stream_read (G_INPUT_STREAM (bstream), result, found_pos + 1, NULL, error) < 0)
- return NULL;
- } else {
- gsize str_length;
-
- str_length = strnlen (jreader->current, jreader->end - jreader->current);
- if (str_length == jreader->end - jreader->current) {
- /* damaged journal entry (no terminating '\0' character) */
- g_set_error (error, TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_DAMAGED_JOURNAL_ENTRY,
- "Damaged journal entry, no terminating zero found");
- return NULL;
-
- }
-
- result = g_strdup (jreader->current);
-
- jreader->current += str_length + 1;
- }
-
- if (!g_utf8_validate (result, -1, NULL)) {
- /* damaged journal entry (invalid UTF-8) */
- g_set_error (error, TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_DAMAGED_JOURNAL_ENTRY,
- "Damaged journal entry, invalid UTF-8");
- g_free (result);
- return NULL;
- }
-
- return result;
-}
-
-static gboolean
-journal_verify_header (TrackerDBJournalReader *jreader)
-{
- gchar header[8];
- gint i;
- GError *error = NULL;
-
- /* Version 00003 is identical, it just has no UPDATE operations */
-
- if (jreader->stream) {
- for (i = 0; i < sizeof (header); i++) {
- header[i] = g_data_input_stream_read_byte (jreader->stream, NULL, &error);
- if (error) {
- g_clear_error (&error);
- return FALSE;
- }
- }
-
- if (memcmp (header, "trlog\00004", 8) && memcmp (header, "trlog\00003", 8)) {
- return FALSE;
- }
- } else {
- /* verify journal file header */
- if (jreader->end - jreader->current < 8) {
- return FALSE;
- }
-
- if (memcmp (jreader->current, "trlog\00004", 8) && memcmp (jreader->current, "trlog\00003", 8)) {
- return FALSE;
- }
-
- jreader->current += 8;
- }
-
- return TRUE;
-}
-
-void
-tracker_db_journal_get_rotating (gboolean *do_rotating,
- gsize *chunk_size,
- gchar **rotate_to)
-{
- *do_rotating = rotating_settings.do_rotating;
- *chunk_size = rotating_settings.chunk_size;
- if (rotating_settings.rotate_to) {
- *rotate_to = g_strdup (rotating_settings.rotate_to);
- } else {
- *rotate_to = NULL;
- }
-}
-
-void
-tracker_db_journal_set_rotating (gboolean do_rotating,
- gsize chunk_size,
- const gchar *rotate_to)
-{
- rotating_settings.do_rotating = do_rotating;
- rotating_settings.chunk_size = chunk_size;
- g_free (rotating_settings.rotate_to);
- if (rotate_to) {
- rotating_settings.rotate_to = g_strdup (rotate_to);
- } else {
- rotating_settings.rotate_to = NULL;
- }
-}
-
-static gint
-nearest_pow (gint num)
-{
- gint n = 1;
- while (n < num)
- n <<= 1;
- return n;
-}
-
-static void
-cur_block_maybe_expand (TrackerDBJournal *jwriter,
- guint len)
-{
- guint want_alloc = jwriter->cur_block_len + len;
-
- if (want_alloc > jwriter->cur_block_alloc) {
- want_alloc = nearest_pow (want_alloc);
- want_alloc = MAX (want_alloc, MIN_BLOCK_SIZE);
- jwriter->cur_block = g_realloc (jwriter->cur_block, want_alloc);
- jwriter->cur_block_alloc = want_alloc;
- }
-}
-
-static void
-cur_block_kill (TrackerDBJournal *jwriter)
-{
- jwriter->cur_block_len = 0;
- jwriter->cur_pos = 0;
- jwriter->cur_entry_amount = 0;
- jwriter->cur_block_alloc = 0;
-
- g_free (jwriter->cur_block);
- jwriter->cur_block = NULL;
-}
-
-static void
-cur_setnum (gchar *dest,
- guint *pos,
- guint32 val)
-{
- memset (dest + (*pos)++, val >> 24 & 0xff, 1);
- memset (dest + (*pos)++, val >> 16 & 0xff, 1);
- memset (dest + (*pos)++, val >> 8 & 0xff, 1);
- memset (dest + (*pos)++, val >> 0 & 0xff, 1);
-}
-
-static void
-cur_setstr (gchar *dest,
- guint *pos,
- const gchar *str,
- gsize len)
-{
- memcpy (dest + *pos, str, len);
- (*pos) += len;
- memset (dest + (*pos)++, 0 & 0xff, 1);
-}
-
-static gboolean
-write_all_data (int fd,
- gchar *data,
- gsize len,
- GError **error)
-{
- gssize written;
-
- while (len > 0) {
- written = write (fd, data, len);
-
- if (written < 0) {
- gint err = errno;
-
- if (err == EINTR) {
- /* interrupted by signal, try again */
- continue;
- }
-
- g_set_error (error, TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_COULD_NOT_WRITE,
- "Could not write to journal file, %s",
- g_strerror (err));
-
- return FALSE;
- } else if (written == 0) {
- g_set_error (error, TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_COULD_NOT_WRITE,
- "Could not write to journal file, write returned 0 without error");
-
- return FALSE;
- }
-
- len -= written;
- data += written;
- }
-
- return TRUE; /* Succeeded! */
-}
-
-GQuark
-tracker_db_journal_error_quark (void)
-{
- return g_quark_from_static_string (TRACKER_DB_JOURNAL_ERROR_DOMAIN);
-}
-
-static gboolean
-db_journal_init_file (TrackerDBJournal *jwriter,
- gboolean truncate,
- GError **error)
-{
- struct stat st;
- int flags;
- int mode;
-
- jwriter->cur_block_len = 0;
- jwriter->cur_pos = 0;
- jwriter->cur_entry_amount = 0;
- jwriter->cur_block_alloc = 0;
- jwriter->cur_block = NULL;
-
- mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
- flags = O_WRONLY | O_APPEND | O_CREAT | O_LARGEFILE;
- if (truncate) {
- /* existing journal contents are invalid: reindex where journal
- * does not even contain a single valid entry
- *
- * or should be ignored: only for test cases
- */
- flags |= O_TRUNC;
- }
-
- jwriter->journal = g_open (jwriter->journal_filename, flags, mode);
-
- if (jwriter->journal == -1) {
- g_set_error (error, TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_COULD_NOT_WRITE,
- "Could not open journal for writing, %s",
- g_strerror (errno));
- return FALSE;
- }
-
- if (fstat (jwriter->journal, &st) == 0) {
- jwriter->cur_size = (gsize) st.st_size;
- }
-
- if (jwriter->cur_size == 0) {
- g_assert (jwriter->cur_block_len == 0);
- g_assert (jwriter->cur_block_alloc == 0);
- g_assert (jwriter->cur_block == NULL);
-
- cur_block_maybe_expand (jwriter, 8);
-
- jwriter->cur_block[0] = 't';
- jwriter->cur_block[1] = 'r';
- jwriter->cur_block[2] = 'l';
- jwriter->cur_block[3] = 'o';
- jwriter->cur_block[4] = 'g';
- jwriter->cur_block[5] = '\0';
- jwriter->cur_block[6] = '0';
- jwriter->cur_block[7] = '4';
-
- if (!write_all_data (jwriter->journal, jwriter->cur_block, 8, error)) {
- cur_block_kill (jwriter);
- /* delete empty journal file */
- g_unlink (jwriter->journal_filename);
- close (jwriter->journal);
- jwriter->journal = 0;
- return FALSE;
- }
-
- jwriter->cur_size += 8;
- cur_block_kill (jwriter);
- }
-
- return TRUE;
-}
-
-static gboolean
-db_journal_writer_init (TrackerDBJournal *jwriter,
- gboolean truncate,
- gboolean global_writer,
- const gchar *filename,
- GFile *journal_location,
- GError **error)
-{
- gchar *directory;
- gint mode;
- GError *n_error = NULL;
- gboolean ret;
-
- directory = g_path_get_dirname (filename);
- if (g_strcmp0 (directory, ".")) {
- mode = S_IRWXU | S_IRWXG | S_IRWXO;
- if (g_mkdir_with_parents (directory, mode)) {
-
- g_set_error (error, TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_COULD_NOT_WRITE,
- "tracker data directory does not exist and "
- "could not be created: %s",
- g_strerror (errno));
- g_free (directory);
-
- return FALSE;
- }
- }
- g_free (directory);
-
- jwriter->journal_filename = g_strdup (filename);
- g_set_object (&jwriter->journal_location, journal_location);
-
- ret = db_journal_init_file (jwriter, truncate, &n_error);
-
- if (n_error) {
- g_propagate_error (error, n_error);
- g_free (jwriter->journal_filename);
- jwriter->journal_filename = NULL;
- }
-
- return ret;
-}
-
-TrackerDBJournal *
-tracker_db_journal_new (GFile *data_location,
- gboolean truncate,
- GError **error)
-{
- TrackerDBJournal *writer;
- gboolean ret;
- gchar *filename;
- GError *n_error = NULL;
- GFile *child;
-
- writer = g_new0 (TrackerDBJournal, 1);
- writer->transaction_format = TRANSACTION_FORMAT_DATA;
-
- child = g_file_get_child (data_location, TRACKER_DB_JOURNAL_FILENAME);
- filename = g_file_get_path (child);
- g_object_unref (child);
-
- g_assert (filename != NULL);
-
- ret = db_journal_writer_init (writer, truncate, TRUE, filename, data_location, &n_error);
- g_free (filename);
-
- if (!ret) {
- g_propagate_error (error, n_error);
- g_clear_pointer (&writer, g_free);
- }
-
- return writer;
-}
-
-TrackerDBJournal *
-tracker_db_journal_ontology_new (GFile *data_location,
- GError **error)
-{
- TrackerDBJournal *writer;
- gboolean ret;
- gchar *filename;
- GError *n_error = NULL;
- GFile *child;
-
- writer = g_new0 (TrackerDBJournal, 1);
- writer->transaction_format = TRANSACTION_FORMAT_ONTOLOGY;
-
- child = g_file_get_child (data_location, TRACKER_DB_JOURNAL_ONTOLOGY_FILENAME);
- filename = g_file_get_path (child);
- g_object_unref (child);
-
- g_assert (filename != NULL);
-
- ret = db_journal_writer_init (writer, FALSE, FALSE, filename, data_location, &n_error);
- g_free (filename);
-
- if (!ret) {
- g_propagate_error (error, n_error);
- g_clear_pointer (&writer, g_free);
- }
-
- return writer;
-}
-
-static gboolean
-db_journal_writer_clear (TrackerDBJournal *jwriter,
- GError **error)
-{
- g_clear_pointer (&jwriter->journal_filename, g_free);
- g_clear_object (&jwriter->journal_location);
-
- if (jwriter->journal == 0) {
- return TRUE;
- }
-
- if (close (jwriter->journal) != 0) {
- g_set_error (error, TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_COULD_NOT_CLOSE,
- "Could not close journal, %s",
- g_strerror (errno));
- return FALSE;
- }
-
- jwriter->journal = 0;
-
- return TRUE;
-}
-
-gboolean
-tracker_db_journal_free (TrackerDBJournal *writer,
- GError **error)
-{
- GError *n_error = NULL;
-
- db_journal_writer_clear (writer, &n_error);
- g_free (writer);
-
- if (n_error) {
- g_propagate_error (error, n_error);
- return FALSE;
- }
-
- return TRUE;
-}
-
-gsize
-tracker_db_journal_get_size (TrackerDBJournal *writer)
-{
- g_return_val_if_fail (writer->journal > 0, FALSE);
-
- return writer->cur_size;
-}
-
-gboolean
-tracker_db_journal_start_transaction (TrackerDBJournal *jwriter,
- time_t time)
-{
- guint size;
-
- g_return_val_if_fail (jwriter->journal > 0, FALSE);
- g_return_val_if_fail (jwriter->in_transaction == FALSE, FALSE);
-
- jwriter->in_transaction = TRUE;
-
- size = sizeof (guint32) * 3;
- cur_block_maybe_expand (jwriter, size);
-
- /* Leave space for size, amount and crc
- * Check and keep in sync the offset variable at
- * tracker_db_journal_commit_db_transaction too */
-
- memset (jwriter->cur_block, 0, size);
-
- jwriter->cur_pos = jwriter->cur_block_len = size;
- jwriter->cur_entry_amount = 0;
-
- /* add timestamp */
- cur_block_maybe_expand (jwriter, sizeof (gint32));
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), time);
- jwriter->cur_block_len += sizeof (gint32);
-
- /* Add format */
- cur_block_maybe_expand (jwriter, sizeof (gint32));
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), jwriter->transaction_format);
- jwriter->cur_block_len += sizeof (gint32);
-
- return TRUE;
-}
-
-gboolean
-tracker_db_journal_append_delete_statement (TrackerDBJournal *jwriter,
- gint g_id,
- gint s_id,
- gint p_id,
- const gchar *object)
-{
- gint o_len;
- DataFormat df;
- gint size;
-
- g_return_val_if_fail (jwriter->journal > 0, FALSE);
- g_return_val_if_fail (g_id >= 0, FALSE);
- g_return_val_if_fail (s_id > 0, FALSE);
- g_return_val_if_fail (p_id > 0, FALSE);
- g_return_val_if_fail (object != NULL, FALSE);
- g_return_val_if_fail (jwriter->in_transaction == TRUE, FALSE);
-
- if (jwriter->transaction_format == TRANSACTION_FORMAT_ONTOLOGY) {
- return TRUE;
- }
-
- o_len = strlen (object);
- if (g_id == 0) {
- df = DATA_FORMAT_OPERATION_DELETE;
- size = (sizeof (guint32) * 3) + o_len + 1;
- } else {
- df = DATA_FORMAT_OPERATION_DELETE | DATA_FORMAT_GRAPH;
- size = (sizeof (guint32) * 4) + o_len + 1;
- }
-
- cur_block_maybe_expand (jwriter, size);
-
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), df);
- if (g_id > 0) {
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), g_id);
- }
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), s_id);
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), p_id);
- cur_setstr (jwriter->cur_block, &(jwriter->cur_pos), object, o_len);
-
- jwriter->cur_entry_amount++;
- jwriter->cur_block_len += size;
-
- return TRUE;
-}
-
-gboolean
-tracker_db_journal_append_delete_statement_id (TrackerDBJournal *jwriter,
- gint g_id,
- gint s_id,
- gint p_id,
- gint o_id)
-{
- DataFormat df;
- gint size;
-
- g_return_val_if_fail (jwriter->journal > 0, FALSE);
- g_return_val_if_fail (g_id >= 0, FALSE);
- g_return_val_if_fail (s_id > 0, FALSE);
- g_return_val_if_fail (p_id > 0, FALSE);
- g_return_val_if_fail (o_id > 0, FALSE);
- g_return_val_if_fail (jwriter->in_transaction == TRUE, FALSE);
-
- if (jwriter->transaction_format == TRANSACTION_FORMAT_ONTOLOGY) {
- return TRUE;
- }
-
- if (g_id == 0) {
- df = DATA_FORMAT_OPERATION_DELETE | DATA_FORMAT_OBJECT_ID;
- size = sizeof (guint32) * 4;
- } else {
- df = DATA_FORMAT_OPERATION_DELETE | DATA_FORMAT_OBJECT_ID | DATA_FORMAT_GRAPH;
- size = sizeof (guint32) * 5;
- }
-
- cur_block_maybe_expand (jwriter, size);
-
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), df);
- if (g_id > 0) {
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), g_id);
- }
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), s_id);
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), p_id);
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), o_id);
-
- jwriter->cur_entry_amount++;
- jwriter->cur_block_len += size;
-
- return TRUE;
-}
-
-gboolean
-tracker_db_journal_append_insert_statement (TrackerDBJournal *jwriter,
- gint g_id,
- gint s_id,
- gint p_id,
- const gchar *object)
-{
- gint o_len;
- DataFormat df;
- gint size;
-
- g_return_val_if_fail (jwriter->journal > 0, FALSE);
- g_return_val_if_fail (g_id >= 0, FALSE);
- g_return_val_if_fail (s_id > 0, FALSE);
- g_return_val_if_fail (p_id > 0, FALSE);
- g_return_val_if_fail (object != NULL, FALSE);
- g_return_val_if_fail (jwriter->in_transaction == TRUE, FALSE);
-
- if (jwriter->transaction_format == TRANSACTION_FORMAT_ONTOLOGY) {
- return TRUE;
- }
-
- o_len = strlen (object);
- if (g_id == 0) {
- df = 0x00;
- size = (sizeof (guint32) * 3) + o_len + 1;
- } else {
- df = DATA_FORMAT_GRAPH;
- size = (sizeof (guint32) * 4) + o_len + 1;
- }
-
- cur_block_maybe_expand (jwriter, size);
-
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), df);
- if (g_id > 0) {
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), g_id);
- }
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), s_id);
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), p_id);
- cur_setstr (jwriter->cur_block, &(jwriter->cur_pos), object, o_len);
-
- jwriter->cur_entry_amount++;
- jwriter->cur_block_len += size;
-
- return TRUE;
-}
-
-gboolean
-tracker_db_journal_append_insert_statement_id (TrackerDBJournal *jwriter,
- gint g_id,
- gint s_id,
- gint p_id,
- gint o_id)
-{
- DataFormat df;
- gint size;
-
- g_return_val_if_fail (jwriter->journal > 0, FALSE);
- g_return_val_if_fail (g_id >= 0, FALSE);
- g_return_val_if_fail (s_id > 0, FALSE);
- g_return_val_if_fail (p_id > 0, FALSE);
- g_return_val_if_fail (o_id > 0, FALSE);
- g_return_val_if_fail (jwriter->in_transaction == TRUE, FALSE);
-
- if (jwriter->transaction_format == TRANSACTION_FORMAT_ONTOLOGY) {
- return TRUE;
- }
-
- if (g_id == 0) {
- df = DATA_FORMAT_OBJECT_ID;
- size = sizeof (guint32) * 4;
- } else {
- df = DATA_FORMAT_OBJECT_ID | DATA_FORMAT_GRAPH;
- size = sizeof (guint32) * 5;
- }
-
- cur_block_maybe_expand (jwriter, size);
-
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), df);
- if (g_id > 0) {
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), g_id);
- }
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), s_id);
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), p_id);
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), o_id);
-
- jwriter->cur_entry_amount++;
- jwriter->cur_block_len += size;
-
- return TRUE;
-}
-
-gboolean
-tracker_db_journal_append_update_statement (TrackerDBJournal *jwriter,
- gint g_id,
- gint s_id,
- gint p_id,
- const gchar *object)
-{
- gint o_len;
- DataFormat df;
- gint size;
-
- g_return_val_if_fail (jwriter->journal > 0, FALSE);
- g_return_val_if_fail (g_id >= 0, FALSE);
- g_return_val_if_fail (s_id > 0, FALSE);
- g_return_val_if_fail (p_id > 0, FALSE);
- g_return_val_if_fail (object != NULL, FALSE);
- g_return_val_if_fail (jwriter->in_transaction == TRUE, FALSE);
-
- if (jwriter->transaction_format == TRANSACTION_FORMAT_ONTOLOGY) {
- return TRUE;
- }
-
- o_len = strlen (object);
- if (g_id == 0) {
- df = DATA_FORMAT_OPERATION_UPDATE;
- size = (sizeof (guint32) * 3) + o_len + 1;
- } else {
- df = DATA_FORMAT_OPERATION_UPDATE | DATA_FORMAT_GRAPH;
- size = (sizeof (guint32) * 4) + o_len + 1;
- }
-
- cur_block_maybe_expand (jwriter, size);
-
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), df);
- if (g_id > 0) {
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), g_id);
- }
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), s_id);
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), p_id);
- cur_setstr (jwriter->cur_block, &(jwriter->cur_pos), object, o_len);
-
- jwriter->cur_entry_amount++;
- jwriter->cur_block_len += size;
-
- return TRUE;
-}
-
-gboolean
-tracker_db_journal_append_update_statement_id (TrackerDBJournal *jwriter,
- gint g_id,
- gint s_id,
- gint p_id,
- gint o_id)
-{
- DataFormat df;
- gint size;
-
- g_return_val_if_fail (jwriter->journal > 0, FALSE);
- g_return_val_if_fail (g_id >= 0, FALSE);
- g_return_val_if_fail (s_id > 0, FALSE);
- g_return_val_if_fail (p_id > 0, FALSE);
- g_return_val_if_fail (o_id > 0, FALSE);
- g_return_val_if_fail (jwriter->in_transaction == TRUE, FALSE);
-
- if (jwriter->transaction_format == TRANSACTION_FORMAT_ONTOLOGY) {
- return TRUE;
- }
-
- if (g_id == 0) {
- df = DATA_FORMAT_OPERATION_UPDATE | DATA_FORMAT_OBJECT_ID;
- size = sizeof (guint32) * 4;
- } else {
- df = DATA_FORMAT_OPERATION_UPDATE | DATA_FORMAT_OBJECT_ID | DATA_FORMAT_GRAPH;
- size = sizeof (guint32) * 5;
- }
-
- cur_block_maybe_expand (jwriter, size);
-
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), df);
- if (g_id > 0) {
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), g_id);
- }
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), s_id);
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), p_id);
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), o_id);
-
- jwriter->cur_entry_amount++;
- jwriter->cur_block_len += size;
-
- return TRUE;
-}
-
-gboolean
-tracker_db_journal_append_resource (TrackerDBJournal *jwriter,
- gint s_id,
- const gchar *uri)
-{
- gint o_len;
- DataFormat df;
- gint size;
-
- g_return_val_if_fail (jwriter->journal > 0, FALSE);
-
- o_len = strlen (uri);
- df = DATA_FORMAT_RESOURCE_INSERT;
- size = (sizeof (guint32) * 2) + o_len + 1;
-
- cur_block_maybe_expand (jwriter, size);
-
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), df);
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), s_id);
- cur_setstr (jwriter->cur_block, &(jwriter->cur_pos), uri, o_len);
-
- jwriter->cur_entry_amount++;
- jwriter->cur_block_len += size;
-
- return TRUE;
-}
-
-gboolean
-tracker_db_journal_rollback_transaction (TrackerDBJournal *writer)
-{
- g_return_val_if_fail (writer->journal > 0, FALSE);
- g_return_val_if_fail (writer->in_transaction == TRUE, FALSE);
-
- cur_block_kill (writer);
- writer->in_transaction = FALSE;
-
- return TRUE;
-}
-
-gboolean
-tracker_db_journal_truncate (TrackerDBJournal *writer,
- gsize new_size)
-{
- g_return_val_if_fail (writer->journal > 0, FALSE);
-
- return (ftruncate (writer->journal, new_size) != -1);
-}
-
-static gboolean
-db_journal_writer_commit_db_transaction (TrackerDBJournal *jwriter,
- GError **error)
-{
- guint32 crc;
- guint begin_pos;
- guint size;
- guint offset;
-
- g_return_val_if_fail (jwriter->journal > 0, FALSE);
-
- begin_pos = 0;
- size = sizeof (guint32);
- offset = sizeof (guint32) * 3;
-
- /* Expand by uint32 for the size check at the end of the entry */
- cur_block_maybe_expand (jwriter, size);
-
- jwriter->cur_block_len += size;
-
- /* Write size and amount */
- cur_setnum (jwriter->cur_block, &begin_pos, jwriter->cur_block_len);
- cur_setnum (jwriter->cur_block, &begin_pos, jwriter->cur_entry_amount);
-
- /* Write size check to end of current journal data */
- cur_setnum (jwriter->cur_block, &(jwriter->cur_pos), jwriter->cur_block_len);
-
- /* Calculate CRC from entry triples start (i.e. without size,
- * amount and crc) until the end of the entry block.
- *
- * NOTE: the size check at the end is included in the CRC!
- */
- crc = tracker_crc32 (jwriter->cur_block + offset, jwriter->cur_block_len - offset);
- cur_setnum (jwriter->cur_block, &begin_pos, crc);
-
- if (!write_all_data (jwriter->journal, jwriter->cur_block, jwriter->cur_block_len, error)) {
- return FALSE;
- }
-
- /* Update journal size */
- jwriter->cur_size += jwriter->cur_block_len;
-
- /* Clean up for next transaction */
- cur_block_kill (jwriter);
-
- return TRUE;
-}
-
-gboolean
-tracker_db_journal_commit_db_transaction (TrackerDBJournal *writer,
- GError **error)
-{
- gboolean ret;
- GError *n_error = NULL;
-
- g_return_val_if_fail (writer->in_transaction == TRUE, FALSE);
-
- ret = db_journal_writer_commit_db_transaction (writer, &n_error);
-
- if (ret && writer->transaction_format == TRANSACTION_FORMAT_DATA) {
- if (rotating_settings.do_rotating && (writer->cur_size > rotating_settings.chunk_size)) {
- ret = tracker_db_journal_rotate (writer, &n_error);
- }
- }
-
- if (n_error) {
- g_propagate_error (error, n_error);
- }
-
- writer->in_transaction = FALSE;
-
- return ret;
-}
-
-gboolean
-tracker_db_journal_fsync (TrackerDBJournal *writer)
-{
- g_return_val_if_fail (writer->journal > 0, FALSE);
-
- return fsync (writer->journal) == 0;
-}
-
-/*
- * Reader API
- */
-
-static gboolean db_journal_reader_clear (TrackerDBJournalReader *jreader);
-
-static gchar*
-reader_get_next_filepath (TrackerDBJournalReader *jreader)
-{
- gchar *filename_open = NULL;
- gchar *test;
-
- test = g_strdup_printf ("%s.%d", jreader->filename, jreader->current_file + 1);
-
- if (g_file_test (test, G_FILE_TEST_EXISTS)) {
- jreader->current_file++;
- filename_open = test;
- } else {
- gchar *filename;
- GFile *dest_dir, *possible;
-
- /* This is where chunks are being rotated to */
- if (rotating_settings.rotate_to) {
- dest_dir = g_file_new_for_path (rotating_settings.rotate_to);
- } else {
- GFile *source;
-
- /* keep compressed journal files in same directory */
- source = g_file_new_for_path (test);
- dest_dir = g_file_get_parent (source);
- g_object_unref (source);
- }
-
- filename = g_path_get_basename (test);
- g_free (test);
- test = filename;
- filename = g_strconcat (test, ".gz", NULL);
- g_free (test);
- possible = g_file_get_child (dest_dir, filename);
- g_object_unref (dest_dir);
- g_free (filename);
-
- if (g_file_query_exists (possible, NULL)) {
- jreader->current_file++;
- filename_open = g_file_get_path (possible);
- }
- g_object_unref (possible);
- }
-
- if (filename_open == NULL) {
- filename_open = g_strdup (jreader->filename);
- /* Last file is the active journal file */
- jreader->current_file = 0;
- }
-
- return filename_open;
-}
-
-static gboolean
-db_journal_reader_init_file (TrackerDBJournalReader *jreader,
- const gchar *filename,
- GError **error)
-{
- if (g_str_has_suffix (filename, ".gz")) {
- GFile *file;
- GInputStream *stream, *cstream;
- GConverter *converter;
-
- file = g_file_new_for_path (filename);
-
- stream = G_INPUT_STREAM (g_file_read (file, NULL, error));
- g_object_unref (file);
- if (!stream) {
- return FALSE;
- }
-
- jreader->underlying_stream = g_object_ref (stream);
-
- if (jreader->underlying_stream_info) {
- g_object_unref (jreader->underlying_stream_info);
- jreader->underlying_stream_info = NULL;
- }
-
- converter = G_CONVERTER (g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP));
- cstream = g_converter_input_stream_new (stream, converter);
- g_object_unref (stream);
- g_object_unref (converter);
-
- jreader->stream = g_data_input_stream_new (cstream);
- g_object_unref (cstream);
- } else {
- jreader->file = g_mapped_file_new (filename, FALSE, error);
-
- if (!jreader->file) {
- return FALSE;
- }
-
- jreader->last_success = jreader->start = jreader->current =
- g_mapped_file_get_contents (jreader->file);
-
- jreader->end = jreader->current + g_mapped_file_get_length (jreader->file);
- }
-
- if (!journal_verify_header (jreader)) {
- g_set_error (error, TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_BEGIN_OF_JOURNAL,
- "Damaged journal entry at begin of journal");
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-db_journal_reader_init (TrackerDBJournalReader *jreader,
- gboolean global_reader,
- const gchar *filename,
- GFile *data_location,
- GError **error)
-{
- gchar *filename_open;
- GError *n_error = NULL;
-
- g_return_val_if_fail (jreader->file == NULL, FALSE);
-
- jreader->filename = g_strdup (filename);
- g_set_object (&jreader->journal_location, data_location);
-
- jreader->current_file = 0;
- if (global_reader) {
- filename_open = reader_get_next_filepath (jreader);
- } else {
- filename_open = g_strdup (filename);
- }
-
- jreader->type = TRACKER_DB_JOURNAL_START;
-
- if (!db_journal_reader_init_file (jreader, filename_open, &n_error)) {
- if (!g_error_matches (n_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) &&
- !g_error_matches (n_error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
- /* Do not set error if the file does not exist, just return FALSE */
-
- g_propagate_prefixed_error (error,
- n_error,
- "Could not create TrackerDBJournalReader for file '%s', ",
- jreader->filename);
- } else {
- g_error_free (n_error);
- }
-
- g_free (filename_open);
-
- db_journal_reader_clear (jreader);
- return FALSE;
- }
-
- g_free (filename_open);
-
- return TRUE;
-}
-
-TrackerDBJournalReader *
-tracker_db_journal_reader_new (GFile *data_location,
- GError **error)
-{
- TrackerDBJournalReader *reader;
- GError *n_error = NULL;
- gchar *filename;
- GFile *child;
-
- child = g_file_get_child (data_location, TRACKER_DB_JOURNAL_FILENAME);
- filename = g_file_get_path (child);
- g_object_unref (child);
-
- reader = g_new0 (TrackerDBJournalReader, 1);
-
- if (!db_journal_reader_init (reader, TRUE, filename, data_location, &n_error)) {
- if (n_error)
- g_propagate_error (error, n_error);
- g_clear_pointer (&reader, g_free);
- }
-
- g_free (filename);
-
- return reader;
-}
-
-TrackerDBJournalReader *
-tracker_db_journal_reader_ontology_new (GFile *data_location,
- GError **error)
-{
- TrackerDBJournalReader *reader;
- gchar *filename;
- GError *n_error = NULL;
- GFile *child;
-
- child = g_file_get_child (data_location, TRACKER_DB_JOURNAL_ONTOLOGY_FILENAME);
- filename = g_file_get_path (child);
- g_object_unref (child);
-
- reader = g_new0 (TrackerDBJournalReader, 1);
-
- if (!db_journal_reader_init (reader, TRUE, filename, data_location, &n_error)) {
- g_propagate_error (error, n_error);
- g_clear_pointer (&reader, g_free);
- }
-
- g_free (filename);
-
- return reader;
-}
-
-gsize
-tracker_db_journal_reader_get_size_of_correct (TrackerDBJournalReader *reader)
-{
- g_return_val_if_fail (reader->file != NULL, FALSE);
-
- return (gsize) (reader->last_success - reader->start);
-}
-
-static gboolean
-reader_next_file (TrackerDBJournalReader *reader,
- GError **error)
-{
- gchar *filename_open;
-
- filename_open = reader_get_next_filepath (reader);
-
- if (reader->stream) {
- g_object_unref (reader->stream);
- reader->stream = NULL;
-
- g_object_unref (reader->underlying_stream);
- reader->underlying_stream = NULL;
- if (reader->underlying_stream_info) {
- g_object_unref (reader->underlying_stream_info);
- reader->underlying_stream_info = NULL;
- }
-
- } else {
- g_mapped_file_unref (reader->file);
- reader->file = NULL;
- }
-
- if (!db_journal_reader_init_file (reader, filename_open, error)) {
- g_free (filename_open);
- return FALSE;
- }
-
- g_free (filename_open);
-
- reader->type = TRACKER_DB_JOURNAL_END_TRANSACTION;
-
- reader->entry_begin = NULL;
- reader->entry_end = NULL;
- reader->amount_of_triples = 0;
-
- return TRUE;
-}
-
-static gboolean
-db_journal_reader_clear (TrackerDBJournalReader *jreader)
-{
- if (jreader->stream) {
- g_object_unref (jreader->stream);
- jreader->stream = NULL;
- g_object_unref (jreader->underlying_stream);
- jreader->underlying_stream = NULL;
- if (jreader->underlying_stream_info) {
- g_object_unref (jreader->underlying_stream_info);
- jreader->underlying_stream_info = NULL;
- }
- } else if (jreader->file) {
- g_mapped_file_unref (jreader->file);
- jreader->file = NULL;
- }
-
- g_free (jreader->filename);
- jreader->filename = NULL;
-
- jreader->last_success = NULL;
- jreader->start = NULL;
- jreader->current = NULL;
- jreader->end = NULL;
- jreader->entry_begin = NULL;
- jreader->entry_end = NULL;
- jreader->amount_of_triples = 0;
- jreader->type = TRACKER_DB_JOURNAL_START;
- jreader->uri = NULL;
- jreader->g_id = 0;
- jreader->s_id = 0;
- jreader->p_id = 0;
- jreader->o_id = 0;
- jreader->object = NULL;
-
- return TRUE;
-}
-
-void
-tracker_db_journal_reader_free (TrackerDBJournalReader *reader)
-{
- db_journal_reader_clear (reader);
- g_free (reader);
-}
-
-TrackerDBJournalEntryType
-tracker_db_journal_reader_get_entry_type (TrackerDBJournalReader *reader)
-{
- g_return_val_if_fail (reader->file != NULL || reader->stream != NULL, FALSE);
-
- return reader->type;
-}
-
-static gboolean
-db_journal_reader_next (TrackerDBJournalReader *jreader,
- gboolean global_reader,
- GError **error)
-{
- GError *inner_error = NULL;
- static gboolean debug_unchecked = TRUE;
- static gboolean slow_down = FALSE;
-
- g_return_val_if_fail (jreader->file != NULL || jreader->stream != NULL, FALSE);
-
- /* reset struct */
- g_free (jreader->uri);
- jreader->uri = NULL;
- jreader->g_id = 0;
- jreader->s_id = 0;
- jreader->p_id = 0;
- jreader->o_id = 0;
- g_free (jreader->object);
- jreader->object = NULL;
-
- /*
- * Visual layout of the data in the binary journal:
- *
- * [
- * [magic]
- * [version]
- * [
- * [entry
- * [size]
- * [amount]
- * [crc]
- * [time]
- * [id id id]
- * [id id string]
- * [id ...]
- * [size]
- * ]
- * [entry...]
- * [entry...]
- * ]
- * ]
- *
- * Note: We automatically start at the first entry, upon init
- * of the reader, we move past the [magic] and the [version].
- */
-
- if (jreader->type == TRACKER_DB_JOURNAL_START ||
- jreader->type == TRACKER_DB_JOURNAL_END_TRANSACTION) {
- /* Expect new transaction or end of file */
- guint32 entry_size;
- guint32 entry_size_check;
- guint32 crc;
- guint32 crc_check;
- TransactionFormat t_kind;
-
-
- if (G_UNLIKELY (debug_unchecked)) {
- const gchar *test;
-
- test = g_getenv ("TRACKER_DEBUG_MAKE_JOURNAL_READER_GO_VERY_SLOW");
- if (g_strcmp0 (test, "yes") == 0) {
- slow_down = TRUE;
- }
- debug_unchecked = FALSE;
- }
-
- if (G_UNLIKELY (slow_down)) {
- sleep (1);
- }
-
- /* Check the end is not where we currently are */
- if (journal_eof (jreader)) {
- /* Return FALSE as there is no further entry but
- * do not set error as it's not an error case. */
- if (global_reader && jreader->current_file != 0) {
- if (reader_next_file (jreader, error)) {
- /* read first entry in next file */
- return db_journal_reader_next (jreader, global_reader, error);
- } else {
- /* no more files */
- return FALSE;
- }
- } else {
- return FALSE;
- }
- }
-
- jreader->entry_begin = jreader->current;
-
- /* Read the first uint32 which contains the size */
- entry_size = journal_read_uint32 (jreader, &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
- }
-
- /* Check that entry is big enough for header and footer */
- if (entry_size < 5 * sizeof (guint32)) {
- g_set_error (error, TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_DAMAGED_JOURNAL_ENTRY,
- "Damaged journal entry, size %d < 5 * sizeof(guint32)",
- (gint) entry_size);
- return FALSE;
- }
-
- /* Check that entry is smaller than the rest of the file.
- Very large entry_size could otherwise cause an overflow
- in entry_begin + entry_size below. */
- if ((gint64) entry_size > (gint64) (jreader->end - jreader->entry_begin)) {
- g_set_error (error, TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_DAMAGED_JOURNAL_ENTRY,
- "Damaged journal entry, size %u > %" G_GINT64_FORMAT " (rest of the file)",
- entry_size, (gint64)(jreader->end - jreader->entry_begin));
- return FALSE;
- }
-
- if (!jreader->stream) {
- /* Set the bounds for the entry */
- jreader->entry_end = jreader->entry_begin + entry_size;
-
- /* Check the end of the entry does not exceed the end
- * of the journal.
- */
- if (jreader->end < jreader->entry_end) {
- g_set_error (error, TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_DAMAGED_JOURNAL_ENTRY,
- "Damaged journal entry, end < entry end");
- return FALSE;
- }
-
- /* Read entry size check at the end of the entry */
- entry_size_check = read_uint32 (jreader->entry_end - 4);
-
- if (entry_size != entry_size_check) {
- /* damaged journal entry */
- g_set_error (error, TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_DAMAGED_JOURNAL_ENTRY,
- "Damaged journal entry, %d != %d (entry size != entry size check)",
- entry_size,
- entry_size_check);
- return FALSE;
- }
- }
-
- /* Read the amount of triples */
- jreader->amount_of_triples = journal_read_uint32 (jreader, &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
- }
-
- /* Read the crc */
- crc_check = journal_read_uint32 (jreader, &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
- }
-
- if (!jreader->stream) {
- // Maybe read in whole transaction in one buffer, so we can do CRC even without mmap (when reading compressed journals)
- // might this be too problematic memory-wise
-
- /* Calculate the crc */
- crc = tracker_crc32 (jreader->entry_begin + (sizeof (guint32) * 3), entry_size - (sizeof (guint32) * 3));
-
- /* Verify checksum */
- if (crc != crc_check) {
- /* damaged journal entry */
- g_set_error (error, TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_DAMAGED_JOURNAL_ENTRY,
- "Damaged journal entry, 0x%.8x != 0x%.8x (crc32 failed)",
- crc,
- crc_check);
- return FALSE;
- }
- }
-
- /* Read the timestamp */
- jreader->time = journal_read_uint32 (jreader, &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
- }
-
- t_kind = journal_read_uint32 (jreader, &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
- }
-
- if (t_kind == TRANSACTION_FORMAT_DATA)
- jreader->type = TRACKER_DB_JOURNAL_START_TRANSACTION;
- else
- jreader->type = TRACKER_DB_JOURNAL_START_ONTOLOGY_TRANSACTION;
-
- return TRUE;
- } else if (jreader->amount_of_triples == 0) {
- /* end of transaction */
-
- /* read redundant entry size at end of transaction */
- journal_read_uint32 (jreader, &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
- }
-
- if (!jreader->stream) {
- if (jreader->current != jreader->entry_end) {
- /* damaged journal entry */
- g_set_error (error, TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_DAMAGED_JOURNAL_ENTRY,
- "Damaged journal entry, %p != %p (end of transaction with 0 triples)",
- jreader->current,
- jreader->entry_end);
- return FALSE;
- }
- }
-
- jreader->type = TRACKER_DB_JOURNAL_END_TRANSACTION;
- jreader->last_success = jreader->current;
-
- return TRUE;
- } else {
- DataFormat df;
-
- df = journal_read_uint32 (jreader, &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
- }
-
- if (df == DATA_FORMAT_RESOURCE_INSERT) {
- jreader->type = TRACKER_DB_JOURNAL_RESOURCE;
-
- jreader->s_id = journal_read_uint32 (jreader, &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
- }
-
- jreader->uri = journal_read_string (jreader, &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
- }
- } else {
- if (df & DATA_FORMAT_OPERATION_DELETE) {
- if (df & DATA_FORMAT_OBJECT_ID) {
- jreader->type = TRACKER_DB_JOURNAL_DELETE_STATEMENT_ID;
- } else {
- jreader->type = TRACKER_DB_JOURNAL_DELETE_STATEMENT;
- }
- } else if (df & DATA_FORMAT_OPERATION_UPDATE) {
- if (df & DATA_FORMAT_OBJECT_ID) {
- jreader->type = TRACKER_DB_JOURNAL_UPDATE_STATEMENT_ID;
- } else {
- jreader->type = TRACKER_DB_JOURNAL_UPDATE_STATEMENT;
- }
- } else {
- if (df & DATA_FORMAT_OBJECT_ID) {
- jreader->type = TRACKER_DB_JOURNAL_INSERT_STATEMENT_ID;
- } else {
- jreader->type = TRACKER_DB_JOURNAL_INSERT_STATEMENT;
- }
- }
-
- if (df & DATA_FORMAT_GRAPH) {
- /* named graph */
- jreader->g_id = journal_read_uint32 (jreader, &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
- }
- } else {
- /* default graph */
- jreader->g_id = 0;
- }
-
- jreader->s_id = journal_read_uint32 (jreader, &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
- }
-
- jreader->p_id = journal_read_uint32 (jreader, &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
- }
-
- if (df & DATA_FORMAT_OBJECT_ID) {
- jreader->o_id = journal_read_uint32 (jreader, &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
- }
- } else {
- jreader->object = journal_read_string (jreader, &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
- }
- }
- }
-
- jreader->amount_of_triples--;
- return TRUE;
- }
-}
-
-gboolean
-tracker_db_journal_reader_next (TrackerDBJournalReader *reader,
- GError **error)
-{
- return db_journal_reader_next (reader, TRUE, error);
-}
-
-gboolean
-tracker_db_journal_reader_verify_last (GFile *data_location,
- GError **error)
-{
- guint32 entry_size_check;
- gboolean success = FALSE;
- TrackerDBJournalReader jreader = { 0 };
- GError *n_error = NULL;
- gchar *filename;
- GFile *child;
-
- child = g_file_get_child (data_location, TRACKER_DB_JOURNAL_FILENAME);
- filename = g_file_get_path (child);
- g_object_unref (child);
-
- if (db_journal_reader_init (&jreader, FALSE, filename, data_location, &n_error)) {
-
- if (jreader.end != jreader.current) {
- entry_size_check = read_uint32 (jreader.end - 4);
-
- if (jreader.end - entry_size_check < jreader.current) {
- g_free (filename);
- g_set_error (error, TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_DAMAGED_JOURNAL_ENTRY,
- "Damaged journal entry at end of journal");
- db_journal_reader_clear (&jreader);
- return FALSE;
- }
-
- jreader.current = jreader.end - entry_size_check;
- success = db_journal_reader_next (&jreader, FALSE, NULL);
- } else {
- success = TRUE;
- }
-
- db_journal_reader_clear (&jreader);
- }
-
- g_free (filename);
-
- if (n_error) {
- g_propagate_error (error, n_error);
- }
-
- return success;
-}
-
-gint64
-tracker_db_journal_reader_get_time (TrackerDBJournalReader *reader)
-{
- return reader->time;
-}
-
-gboolean
-tracker_db_journal_reader_get_resource (TrackerDBJournalReader *reader,
- gint *id,
- const gchar **uri)
-{
- g_return_val_if_fail (reader->file != NULL || reader->stream != NULL, FALSE);
- g_return_val_if_fail (reader->type == TRACKER_DB_JOURNAL_RESOURCE, FALSE);
-
- *id = reader->s_id;
- *uri = reader->uri;
-
- return TRUE;
-}
-
-gboolean
-tracker_db_journal_reader_get_statement (TrackerDBJournalReader *reader,
- gint *g_id,
- gint *s_id,
- gint *p_id,
- const gchar **object)
-{
- g_return_val_if_fail (reader->file != NULL || reader->stream != NULL, FALSE);
- g_return_val_if_fail (reader->type == TRACKER_DB_JOURNAL_INSERT_STATEMENT ||
- reader->type == TRACKER_DB_JOURNAL_DELETE_STATEMENT ||
- reader->type == TRACKER_DB_JOURNAL_UPDATE_STATEMENT,
- FALSE);
-
- if (g_id) {
- *g_id = reader->g_id;
- }
- *s_id = reader->s_id;
- *p_id = reader->p_id;
- *object = reader->object;
-
- return TRUE;
-}
-
-gboolean
-tracker_db_journal_reader_get_statement_id (TrackerDBJournalReader *reader,
- gint *g_id,
- gint *s_id,
- gint *p_id,
- gint *o_id)
-{
- g_return_val_if_fail (reader->file != NULL || reader->stream != NULL, FALSE);
- g_return_val_if_fail (reader->type == TRACKER_DB_JOURNAL_INSERT_STATEMENT_ID ||
- reader->type == TRACKER_DB_JOURNAL_DELETE_STATEMENT_ID ||
- reader->type == TRACKER_DB_JOURNAL_UPDATE_STATEMENT_ID,
- FALSE);
-
- if (g_id) {
- *g_id = reader->g_id;
- }
- *s_id = reader->s_id;
- *p_id = reader->p_id;
- *o_id = reader->o_id;
-
- return TRUE;
-}
-
-gdouble
-tracker_db_journal_reader_get_progress (TrackerDBJournalReader *reader)
-{
- gdouble chunk = 0, total = 0, ret = 0;
- guint current_file;
- guint total_chunks = reader->total_chunks;
-
- current_file = reader->current_file == 0 ? reader->total_chunks -1 : reader->current_file -1;
-
- if (reader->total_chunks == 0) {
- gchar *test;
- GFile *dest_dir;
- gboolean cont = TRUE;
-
- total_chunks = 0;
-
- test = g_path_get_basename (reader->filename);
-
- if (rotating_settings.rotate_to) {
- dest_dir = g_file_new_for_path (rotating_settings.rotate_to);
- } else {
- GFile *source;
-
- /* keep compressed journal files in same directory */
- source = g_file_new_for_path (test);
- dest_dir = g_file_get_parent (source);
- g_object_unref (source);
- }
-
- g_free (test);
-
- while (cont) {
- gchar *filename;
- GFile *possible;
-
- test = g_strdup_printf ("%s.%d", reader->filename, total_chunks + 1);
- filename = g_path_get_basename (test);
- g_free (test);
- test = filename;
- filename = g_strconcat (test, ".gz", NULL);
- g_free (test);
- possible = g_file_get_child (dest_dir, filename);
- g_free (filename);
- if (g_file_query_exists (possible, NULL)) {
- total_chunks++;
- } else {
- cont = FALSE;
- }
- g_object_unref (possible);
- }
-
- g_object_unref (dest_dir);
- reader->total_chunks = total_chunks;
- }
-
- if (total_chunks > 0) {
- total = ((gdouble) ((gdouble) current_file) / ((gdouble) total_chunks));
- }
-
- if (reader->start != 0) {
- /* When the last uncompressed part is being processed: */
- gdouble percent = ((gdouble)(reader->end - reader->start));
- ret = chunk = (((gdouble)(reader->current - reader->start)) / percent);
- } else if (reader->underlying_stream) {
- goffset size;
-
- /* When a compressed part is being processed: */
-
- if (!reader->underlying_stream_info) {
- reader->underlying_stream_info =
- g_file_input_stream_query_info (G_FILE_INPUT_STREAM (reader->underlying_stream),
- G_FILE_ATTRIBUTE_STANDARD_SIZE,
- NULL, NULL);
- }
-
- if (reader->underlying_stream_info) {
- size = g_file_info_get_size (reader->underlying_stream_info);
- ret = chunk = (gdouble) ((gdouble)g_seekable_tell (G_SEEKABLE (reader->underlying_stream))) / ((gdouble)size);
- }
- }
-
- if (total_chunks > 0) {
- ret = total + (chunk / (gdouble) total_chunks);
- }
-
- return ret;
-}
-
-static void
-on_chunk_copied_delete (GObject *source_object,
- GAsyncResult *res,
- gpointer user_data)
-{
- GOutputStream *ostream = G_OUTPUT_STREAM (source_object);
- GError *error = NULL;
- GFile *source = G_FILE (user_data);
-
- g_output_stream_splice_finish (ostream, res, &error);
- if (!error) {
- g_file_delete (G_FILE (source), NULL, &error);
- }
-
- g_object_unref (source);
-
- if (error) {
- g_critical ("Error compressing rotated journal chunk: '%s'", error->message);
- g_error_free (error);
- }
-}
-
-static gboolean
-tracker_db_journal_rotate (TrackerDBJournal *writer,
- GError **error)
-{
- GFile *source, *destination;
- GFile *dest_dir;
- gchar *filename, *gzfilename;
- gchar *fullpath;
- GConverter *converter;
- GInputStream *istream;
- GOutputStream *ostream, *cstream;
- GError *n_error = NULL;
- gboolean ret;
-
-#ifdef DISABLE_JOURNAL
- g_critical ("Journal is disabled, yet a journal function got called");
-#endif
-
- if (writer->cur_journal_file == 0) {
- gchar *directory;
- GDir *journal_dir;
- const gchar *f_name;
-
- directory = g_path_get_dirname (writer->journal_filename);
- journal_dir = g_dir_open (directory, 0, NULL);
-
- f_name = g_dir_read_name (journal_dir);
-
- while (f_name) {
- const gchar *ptr;
- guint cur;
-
- if (f_name) {
-
- if (!g_str_has_prefix (f_name, TRACKER_DB_JOURNAL_FILENAME ".")) {
- f_name = g_dir_read_name (journal_dir);
- continue;
- }
-
- ptr = f_name + strlen (TRACKER_DB_JOURNAL_FILENAME ".");
- cur = atoi (ptr);
- writer->cur_journal_file = MAX (cur, writer->cur_journal_file);
- }
-
- f_name = g_dir_read_name (journal_dir);
- }
-
- g_dir_close (journal_dir);
- g_free (directory);
- }
-
- tracker_db_journal_fsync (writer);
-
- if (close (writer->journal) != 0) {
- g_set_error (error, TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_COULD_NOT_CLOSE,
- "Could not close journal, %s",
- g_strerror (errno));
- return FALSE;
- }
-
- fullpath = g_strdup_printf ("%s.%d", writer->journal_filename, ++writer->cur_journal_file);
-
- if (g_rename (writer->journal_filename, fullpath) < 0) {
- g_set_error (error, TRACKER_DB_JOURNAL_ERROR,
- TRACKER_DB_JOURNAL_ERROR_COULD_NOT_WRITE,
- "Could not rotate journal file %s: %s",
- writer->journal_filename,
- g_strerror (errno));
- return FALSE;
- }
-
- /* Recalculate progress next time */
- rotating_settings.rotate_progress_flag = FALSE;
-
- source = g_file_new_for_path (fullpath);
- if (rotating_settings.rotate_to) {
- dest_dir = g_file_new_for_path (rotating_settings.rotate_to);
- } else {
- /* keep compressed journal files in same directory */
- dest_dir = g_file_get_parent (source);
- }
- filename = g_path_get_basename (fullpath);
- gzfilename = g_strconcat (filename, ".gz", NULL);
- destination = g_file_get_child (dest_dir, gzfilename);
- g_object_unref (dest_dir);
- g_free (filename);
- g_free (gzfilename);
-
- istream = G_INPUT_STREAM (g_file_read (source, NULL, NULL));
- ostream = G_OUTPUT_STREAM (g_file_create (destination, 0, NULL, NULL));
- converter = G_CONVERTER (g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP, -1));
- cstream = g_converter_output_stream_new (ostream, converter);
- g_output_stream_splice_async (cstream, istream, 0, 0, NULL, on_chunk_copied_delete, source);
- g_object_unref (istream);
- g_object_unref (ostream);
- g_object_unref (converter);
- g_object_unref (cstream);
-
- g_object_unref (destination);
-
- g_free (fullpath);
-
- ret = db_journal_init_file (writer, TRUE, &n_error);
-
- if (n_error) {
- g_propagate_error (error, n_error);
- g_free (writer->journal_filename);
- writer->journal_filename = NULL;
- }
-
- return ret;
-}
-
-void
-tracker_db_journal_remove (TrackerDBJournal *writer)
-{
- gchar *path;
- gchar *directory;
- const gchar *dirs[3] = { NULL, NULL, NULL };
- guint i;
- GError *error = NULL;
-
- /* We duplicate the path here because later we shutdown the
- * journal which frees this data. We want to survive that.
- */
- path = g_strdup (writer->journal_filename);
- if (!path) {
- return;
- }
-
- g_info (" Removing journal:'%s'", path);
-
- directory = g_path_get_dirname (path);
- tracker_db_journal_free (writer, &error);
-
- if (error) {
- /* TODO: propagate error */
- g_info ("Ignored error while shutting down journal during remove: %s",
- error->message ? error->message : "No error given");
- g_error_free (error);
- }
-
- dirs[0] = directory;
- dirs[1] = rotating_settings.do_rotating ? rotating_settings.rotate_to : NULL;
-
- for (i = 0; dirs[i] != NULL; i++) {
- GDir *journal_dir;
- const gchar *f;
-
- journal_dir = g_dir_open (dirs[i], 0, NULL);
- if (!journal_dir) {
- continue;
- }
-
- /* Remove rotated chunks */
- while ((f = g_dir_read_name (journal_dir)) != NULL) {
- gchar *fullpath;
-
- if (!g_str_has_prefix (f, TRACKER_DB_JOURNAL_FILENAME ".")) {
- continue;
- }
-
- fullpath = g_build_filename (dirs[i], f, NULL);
- if (g_unlink (fullpath) == -1) {
- g_info ("Could not unlink rotated journal: %m");
- }
- g_free (fullpath);
- }
-
- g_dir_close (journal_dir);
- }
-
- g_free (directory);
-
- /* Remove active journal */
- if (g_unlink (path) == -1) {
- g_info ("%s", g_strerror (errno));
- }
- g_free (path);
-}
-
-#else /* DISABLE_JOURNAL */
-void
-tracker_db_journal_set_rotating (gboolean do_rotating,
- gsize chunk_size,
- const gchar *rotate_to)
-{
- /* intentionally left blank, used for internal API compatibility */
-}
-#endif /* DISABLE_JOURNAL */
diff --git a/src/libtracker-data/tracker-db-journal.h b/src/libtracker-data/tracker-db-journal.h
deleted file mode 100644
index 9fd64f3db..000000000
--- a/src/libtracker-data/tracker-db-journal.h
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2009, Nokia <ivan.frade@nokia.com>
- *
- * 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.
- *
- * Author: Philip Van Hoof <philip@codeminded.be>
- */
-
-#ifndef __LIBTRACKER_DB_JOURNAL_H__
-#define __LIBTRACKER_DB_JOURNAL_H__
-
-#include <glib.h>
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-#define TRACKER_DB_JOURNAL_ERROR_DOMAIN "TrackerDBJournal"
-#define TRACKER_DB_JOURNAL_ERROR tracker_db_journal_error_quark()
-#define TRACKER_DB_JOURNAL_FILENAME "tracker-store.journal"
-#define TRACKER_DB_JOURNAL_ONTOLOGY_FILENAME "tracker-store.ontology.journal"
-
-enum {
- TRACKER_DB_JOURNAL_ERROR_UNKNOWN = 0,
- TRACKER_DB_JOURNAL_ERROR_DAMAGED_JOURNAL_ENTRY,
- TRACKER_DB_JOURNAL_ERROR_COULD_NOT_WRITE,
- TRACKER_DB_JOURNAL_ERROR_COULD_NOT_CLOSE,
- TRACKER_DB_JOURNAL_ERROR_BEGIN_OF_JOURNAL
-};
-
-typedef enum {
- TRACKER_DB_JOURNAL_START,
- TRACKER_DB_JOURNAL_START_TRANSACTION,
- TRACKER_DB_JOURNAL_START_ONTOLOGY_TRANSACTION,
- TRACKER_DB_JOURNAL_END_TRANSACTION,
- TRACKER_DB_JOURNAL_RESOURCE,
- TRACKER_DB_JOURNAL_INSERT_STATEMENT,
- TRACKER_DB_JOURNAL_INSERT_STATEMENT_ID,
- TRACKER_DB_JOURNAL_DELETE_STATEMENT,
- TRACKER_DB_JOURNAL_DELETE_STATEMENT_ID,
- TRACKER_DB_JOURNAL_UPDATE_STATEMENT,
- TRACKER_DB_JOURNAL_UPDATE_STATEMENT_ID,
-} TrackerDBJournalEntryType;
-
-typedef struct _TrackerDBJournal TrackerDBJournal;
-typedef struct _TrackerDBJournalReader TrackerDBJournalReader;
-
-GQuark tracker_db_journal_error_quark (void);
-
-/*
- * Writer API
- */
-TrackerDBJournal *
- tracker_db_journal_new (GFile *data_location,
- gboolean truncate,
- GError **error);
-TrackerDBJournal *
- tracker_db_journal_ontology_new (GFile *data_location,
- GError **error);
-gboolean tracker_db_journal_free (TrackerDBJournal *writer,
- GError **error);
-
-gsize tracker_db_journal_get_size (TrackerDBJournal *writer);
-
-void tracker_db_journal_set_rotating (gboolean do_rotating,
- gsize chunk_size,
- const gchar *rotate_to);
-
-void tracker_db_journal_get_rotating (gboolean *do_rotating,
- gsize *chunk_size,
- gchar **rotate_to);
-
-gboolean tracker_db_journal_start_transaction (TrackerDBJournal *writer,
- time_t time);
-
-gboolean tracker_db_journal_append_delete_statement (TrackerDBJournal *writer,
- gint g_id,
- gint s_id,
- gint p_id,
- const gchar *object);
-gboolean tracker_db_journal_append_delete_statement_id (TrackerDBJournal *writer,
- gint g_id,
- gint s_id,
- gint p_id,
- gint o_id);
-gboolean tracker_db_journal_append_insert_statement (TrackerDBJournal *writer,
- gint g_id,
- gint s_id,
- gint p_id,
- const gchar *object);
-gboolean tracker_db_journal_append_insert_statement_id (TrackerDBJournal *writer,
- gint g_id,
- gint s_id,
- gint p_id,
- gint o_id);
-gboolean tracker_db_journal_append_update_statement (TrackerDBJournal *writer,
- gint g_id,
- gint s_id,
- gint p_id,
- const gchar *object);
-gboolean tracker_db_journal_append_update_statement_id (TrackerDBJournal *writer,
- gint g_id,
- gint s_id,
- gint p_id,
- gint o_id);
-gboolean tracker_db_journal_append_resource (TrackerDBJournal *writer,
- gint s_id,
- const gchar *uri);
-
-gboolean tracker_db_journal_rollback_transaction (TrackerDBJournal *writer);
-gboolean tracker_db_journal_commit_db_transaction (TrackerDBJournal *writer,
- GError **error);
-
-gboolean tracker_db_journal_fsync (TrackerDBJournal *writer);
-gboolean tracker_db_journal_truncate (TrackerDBJournal *writer,
- gsize new_size);
-
-void tracker_db_journal_remove (TrackerDBJournal *writer);
-
-/*
- * Reader API
- */
-TrackerDBJournalReader *
- tracker_db_journal_reader_new (GFile *data_location,
- GError **error);
-TrackerDBJournalReader *
- tracker_db_journal_reader_ontology_new (GFile *data_location,
- GError **error);
-void tracker_db_journal_reader_free (TrackerDBJournalReader *reader);
-TrackerDBJournalEntryType
- tracker_db_journal_reader_get_entry_type (TrackerDBJournalReader *reader);
-
-gboolean tracker_db_journal_reader_next (TrackerDBJournalReader *reader,
- GError **error);
-gint64 tracker_db_journal_reader_get_time (TrackerDBJournalReader *reader);
-gboolean tracker_db_journal_reader_get_resource (TrackerDBJournalReader *reader,
- gint *id,
- const gchar **uri);
-gboolean tracker_db_journal_reader_get_statement (TrackerDBJournalReader *reader,
- gint *g_id,
- gint *s_id,
- gint *p_id,
- const gchar **object);
-gboolean tracker_db_journal_reader_get_statement_id (TrackerDBJournalReader *reader,
- gint *g_id,
- gint *s_id,
- gint *p_id,
- gint *o_id);
-gsize tracker_db_journal_reader_get_size_of_correct (TrackerDBJournalReader *reader);
-gdouble tracker_db_journal_reader_get_progress (TrackerDBJournalReader *reader);
-
-gboolean tracker_db_journal_reader_verify_last (GFile *data_location,
- GError **error);
-
-G_END_DECLS
-
-#endif /* __LIBTRACKER_DB_JOURNAL_H__ */
diff --git a/src/libtracker-data/tracker-db-manager.c b/src/libtracker-data/tracker-db-manager.c
index 556bea39d..4c03dc152 100644
--- a/src/libtracker-data/tracker-db-manager.c
+++ b/src/libtracker-data/tracker-db-manager.c
@@ -40,7 +40,6 @@
#include <libtracker-fts/tracker-fts.h>
#endif
-#include "tracker-db-journal.h"
#include "tracker-db-manager.h"
#include "tracker-db-interface-sqlite.h"
#include "tracker-db-interface.h"
@@ -61,16 +60,12 @@
#define TRACKER_DB_PAGE_SIZE_DONT_SET -1
/* Set current database version we are working with */
-#define TRACKER_DB_VERSION_NOW TRACKER_DB_VERSION_0_15_2
-#define TRACKER_DB_VERSION_FILE "db-version.txt"
-#define TRACKER_DB_LOCALE_FILE "db-locale.txt"
+#define TRACKER_DB_VERSION_NOW TRACKER_DB_VERSION_2_3
#define TRACKER_VACUUM_CHECK_SIZE ((goffset) 4 * 1024 * 1024 * 1024) /* 4GB */
#define IN_USE_FILENAME ".meta.isrunning"
-#define PARSER_VERSION_FILENAME "parser-version.txt"
-
#define TOSTRING1(x) #x
#define TOSTRING(x) TOSTRING1(x)
#define TRACKER_PARSER_VERSION_STRING TOSTRING(TRACKER_PARSER_VERSION)
@@ -100,7 +95,8 @@ typedef enum {
TRACKER_DB_VERSION_0_9_24, /* nmo:PhoneMessage class */
TRACKER_DB_VERSION_0_9_34, /* ontology cache */
TRACKER_DB_VERSION_0_9_38, /* nie:url an inverse functional property */
- TRACKER_DB_VERSION_0_15_2 /* fts4 */
+ TRACKER_DB_VERSION_0_15_2, /* fts4 */
+ TRACKER_DB_VERSION_2_3 /* sparql1.1 */
} TrackerDBVersion;
typedef struct {
@@ -129,6 +125,7 @@ static TrackerDBDefinition db_base = {
};
struct _TrackerDBManager {
+ GObject parent_instance;
TrackerDBDefinition db;
gboolean locations_initialized;
gchar *data_dir;
@@ -148,13 +145,22 @@ struct _TrackerDBManager {
GThread *wal_thread;
};
+enum {
+ SETUP_INTERFACE,
+ UPDATE_INTERFACE,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0 };
+
+G_DEFINE_TYPE (TrackerDBManager, tracker_db_manager, G_TYPE_OBJECT)
+
static gboolean db_exec_no_reply (TrackerDBInterface *iface,
const gchar *query,
...);
static TrackerDBInterface *tracker_db_manager_create_db_interface (TrackerDBManager *db_manager,
gboolean readonly,
GError **error);
-static void db_remove_locale_file (TrackerDBManager *db_manager);
static TrackerDBInterface * init_writable_db_interface (TrackerDBManager *db_manager);
@@ -187,32 +193,35 @@ tracker_db_manager_get_flags (TrackerDBManager *db_manager,
}
static void
-db_set_params (TrackerDBInterface *iface,
- gint cache_size,
- gint page_size,
- gboolean readonly,
- GError **error)
+iface_set_params (TrackerDBInterface *iface,
+ gboolean readonly,
+ GError **error)
{
- GError *internal_error = NULL;
- TrackerDBStatement *stmt;
-
-#ifdef DISABLE_JOURNAL
- tracker_db_interface_execute_query (iface, NULL, "PRAGMA synchronous = NORMAL;");
-#else
- tracker_db_interface_execute_query (iface, NULL, "PRAGMA synchronous = OFF;");
-#endif /* DISABLE_JOURNAL */
tracker_db_interface_execute_query (iface, NULL, "PRAGMA encoding = \"UTF-8\"");
- tracker_db_interface_execute_query (iface, NULL, "PRAGMA auto_vacuum = 0;");
if (readonly) {
tracker_db_interface_execute_query (iface, NULL, "PRAGMA temp_store = MEMORY;");
} else {
tracker_db_interface_execute_query (iface, NULL, "PRAGMA temp_store = FILE;");
}
+}
+
+static void
+db_set_params (TrackerDBInterface *iface,
+ const gchar *database,
+ gint cache_size,
+ gint page_size,
+ GError **error)
+{
+ GError *internal_error = NULL;
+ TrackerDBStatement *stmt;
+
+ tracker_db_interface_execute_query (iface, NULL, "PRAGMA \"%s\".synchronous = NORMAL", database);
+ tracker_db_interface_execute_query (iface, NULL, "PRAGMA \"%s\".auto_vacuum = 0", database);
stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
&internal_error,
- "PRAGMA journal_mode = WAL;");
+ "PRAGMA \"%s\".journal_mode = WAL", database);
if (internal_error) {
g_info ("Can't set journal mode to WAL: '%s'",
@@ -238,17 +247,17 @@ db_set_params (TrackerDBInterface *iface,
}
/* disable autocheckpoint */
- tracker_db_interface_execute_query (iface, NULL, "PRAGMA wal_autocheckpoint = 0");
+ tracker_db_interface_execute_query (iface, NULL, "PRAGMA \"%s\".wal_autocheckpoint = 0", database);
- tracker_db_interface_execute_query (iface, NULL, "PRAGMA journal_size_limit = 10240000");
+ tracker_db_interface_execute_query (iface, NULL, "PRAGMA \"%s\".journal_size_limit = 10240000", database);
if (page_size != TRACKER_DB_PAGE_SIZE_DONT_SET) {
- g_info (" Setting page size to %d", page_size);
- tracker_db_interface_execute_query (iface, NULL, "PRAGMA page_size = %d", page_size);
+ g_debug (" Setting page size to %d", page_size);
+ tracker_db_interface_execute_query (iface, NULL, "PRAGMA \"%s\".page_size = %d", database, page_size);
}
- tracker_db_interface_execute_query (iface, NULL, "PRAGMA cache_size = %d", cache_size);
- g_info (" Setting cache size to %d", cache_size);
+ tracker_db_interface_execute_query (iface, NULL, "PRAGMA \"%s\".cache_size = %d", database, cache_size);
+ g_debug (" Setting cache size to %d", cache_size);
}
void
@@ -256,9 +265,8 @@ tracker_db_manager_remove_all (TrackerDBManager *db_manager)
{
gchar *filename;
- g_info ("Removing all database/storage files");
+ g_info ("Removing all files for database %s", db_manager->db.abs_filename);
- g_info (" Removing database:'%s'", db_manager->db.abs_filename);
g_unlink (db_manager->db.abs_filename);
/* also delete shm and wal helper files */
@@ -269,124 +277,127 @@ tracker_db_manager_remove_all (TrackerDBManager *db_manager)
filename = g_strdup_printf ("%s-wal", db_manager->db.abs_filename);
g_unlink (filename);
g_free (filename);
-
- /* Remove locale file also */
- db_remove_locale_file (db_manager);
}
-static TrackerDBVersion
-db_get_version (TrackerDBManager *db_manager)
+static gboolean
+tracker_db_manager_get_metadata (TrackerDBManager *db_manager,
+ const gchar *key,
+ GValue *value)
{
- TrackerDBVersion version;
- gchar *filename;
-
- filename = g_build_filename (db_manager->data_dir, TRACKER_DB_VERSION_FILE, NULL);
+ TrackerDBInterface *iface;
+ TrackerDBStatement *stmt;
+ TrackerDBCursor *cursor;
- if (G_LIKELY (g_file_test (filename, G_FILE_TEST_EXISTS))) {
- gchar *contents;
+ iface = tracker_db_manager_get_writable_db_interface (db_manager);
+ stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
+ NULL,
+ "SELECT value FROM metadata WHERE key = ?");
+ if (!stmt)
+ return FALSE;
+
+ tracker_db_statement_bind_text (stmt, 0, key);
+ cursor = tracker_db_statement_start_cursor (stmt, NULL);
+ g_object_unref (stmt);
+
+ if (!cursor || !tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
+ g_clear_object (&cursor);
+ return FALSE;
+ }
- /* Check version is correct */
- if (G_LIKELY (g_file_get_contents (filename, &contents, NULL, NULL))) {
- if (contents && strlen (contents) <= 2) {
- version = atoi (contents);
- } else {
- g_info (" Version file content size is either 0 or bigger than expected");
+ tracker_db_cursor_get_value (cursor, 0, value);
+ g_object_unref (cursor);
- version = TRACKER_DB_VERSION_UNKNOWN;
- }
+ return G_VALUE_TYPE (value) != G_TYPE_INVALID;
+}
- g_free (contents);
- } else {
- g_info (" Could not get content of file '%s'", filename);
+static void
+tracker_db_manager_set_metadata (TrackerDBManager *db_manager,
+ const gchar *key,
+ GValue *value)
+{
+ TrackerDBInterface *iface;
+ TrackerDBStatement *stmt;
+ GError *error = NULL;
- version = TRACKER_DB_VERSION_UNKNOWN;
- }
- } else {
- g_info (" Could not find database version file:'%s'", filename);
- g_info (" Current databases are either old or no databases are set up yet");
+ iface = tracker_db_manager_get_writable_db_interface (db_manager);
+ stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
+ &error,
+ "INSERT OR REPLACE INTO metadata VALUES (?, ?)");
+ if (stmt) {
+ tracker_db_statement_bind_text (stmt, 0, key);
+ tracker_db_statement_bind_value (stmt, 1, value);
+ tracker_db_statement_execute (stmt, &error);
- version = TRACKER_DB_VERSION_UNKNOWN;
+ g_object_unref (stmt);
}
- g_free (filename);
-
- return version;
+ if (error) {
+ g_critical ("Could not store database metadata: %s\n", error->message);
+ g_error_free (error);
+ }
}
-void
-tracker_db_manager_create_version_file (TrackerDBManager *db_manager)
+static TrackerDBVersion
+db_get_version (TrackerDBManager *db_manager)
{
- GError *error = NULL;
- gchar *filename;
- gchar *str;
+ TrackerDBInterface *iface;
+ TrackerDBStatement *stmt;
+ TrackerDBCursor *cursor;
+ TrackerDBVersion version;
- filename = g_build_filename (db_manager->data_dir, TRACKER_DB_VERSION_FILE, NULL);
- g_info (" Creating version file '%s'", filename);
+ iface = tracker_db_manager_get_writable_db_interface (db_manager);
+ stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
+ NULL, "PRAGMA user_version");
+ if (!stmt)
+ return TRACKER_DB_VERSION_UNKNOWN;
- str = g_strdup_printf ("%d", TRACKER_DB_VERSION_NOW);
+ cursor = tracker_db_statement_start_cursor (stmt, NULL);
+ g_object_unref (stmt);
- if (!g_file_set_contents (filename, str, -1, &error)) {
- g_info (" Could not set file contents, %s",
- error ? error->message : "no error given");
- g_clear_error (&error);
+ if (!cursor || !tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
+ g_clear_object (&cursor);
+ return TRACKER_DB_VERSION_UNKNOWN;
}
- g_free (str);
- g_free (filename);
+ version = tracker_db_cursor_get_int (cursor, 0);
+ g_object_unref (cursor);
+
+ return version;
}
void
-tracker_db_manager_remove_version_file (TrackerDBManager *db_manager)
+tracker_db_manager_update_version (TrackerDBManager *db_manager)
{
- gchar *filename;
-
- filename = g_build_filename (db_manager->data_dir, TRACKER_DB_VERSION_FILE, NULL);
- g_info (" Removing db-version file:'%s'", filename);
- g_unlink (filename);
- g_free (filename);
-}
+ TrackerDBInterface *iface;
+ TrackerDBStatement *stmt;
+ GError *error = NULL;
-static void
-db_remove_locale_file (TrackerDBManager *db_manager)
-{
- gchar *filename;
+ iface = tracker_db_manager_get_writable_db_interface (db_manager);
+ stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
+ &error, "PRAGMA user_version = %d",
+ TRACKER_DB_VERSION_NOW);
+ if (stmt) {
+ tracker_db_statement_execute (stmt, &error);
+ g_object_unref (stmt);
+ }
- filename = g_build_filename (db_manager->data_dir, TRACKER_DB_LOCALE_FILE, NULL);
- g_info (" Removing db-locale file:'%s'", filename);
- g_unlink (filename);
- g_free (filename);
+ if (error) {
+ g_critical ("Could not set database version: %s\n", error->message);
+ g_error_free (error);
+ }
}
static gchar *
db_get_locale (TrackerDBManager *db_manager)
{
+ GValue value = G_VALUE_INIT;
gchar *locale = NULL;
- gchar *filename;
-
- filename = g_build_filename (db_manager->data_dir, TRACKER_DB_LOCALE_FILE, NULL);
- if (G_LIKELY (g_file_test (filename, G_FILE_TEST_EXISTS))) {
- gchar *contents;
-
- /* Check locale is correct */
- if (G_LIKELY (g_file_get_contents (filename, &contents, NULL, NULL))) {
- if (contents && strlen (contents) == 0) {
- g_critical (" Empty locale file found at '%s'", filename);
- g_free (contents);
- } else {
- /* Re-use contents */
- locale = contents;
- }
- } else {
- g_critical (" Could not get content of file '%s'", filename);
- }
- } else {
- /* expected when restoring from backup, always recreate indices */
- g_info (" Could not find database locale file:'%s'", filename);
- locale = g_strdup ("unknown");
- }
+ if (!tracker_db_manager_get_metadata (db_manager, "locale", &value))
+ return NULL;
- g_free (filename);
+ locale = g_value_dup_string (&value);
+ g_value_unset (&value);
return locale;
}
@@ -395,23 +406,13 @@ static void
db_set_locale (TrackerDBManager *db_manager,
const gchar *locale)
{
- GError *error = NULL;
- gchar *filename;
- gchar *str;
-
- filename = g_build_filename (db_manager->data_dir, TRACKER_DB_LOCALE_FILE, NULL);
- g_info (" Creating locale file '%s'", filename);
+ GValue value = G_VALUE_INIT;
- str = g_strdup_printf ("%s", locale ? locale : "");
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_set_string (&value, locale);
- if (!g_file_set_contents (filename, str, -1, &error)) {
- g_info (" Could not set file contents, %s",
- error ? error->message : "no error given");
- g_clear_error (&error);
- }
-
- g_free (str);
- g_free (filename);
+ tracker_db_manager_set_metadata (db_manager, "locale", &value);
+ g_value_unset (&value);
}
gboolean
@@ -442,7 +443,8 @@ tracker_db_manager_locale_changed (TrackerDBManager *db_manager,
TRACKER_DB_INTERFACE_ERROR,
TRACKER_DB_OPEN_ERROR,
"Locale change detected (DB:%s, User/App:%s)",
- db_locale, current_locale);
+ db_locale ? db_locale : "unknown",
+ current_locale);
changed = TRUE;
} else {
g_info ("Current and DB locales match: '%s'", db_locale);
@@ -490,19 +492,18 @@ static void
db_recreate_all (TrackerDBManager *db_manager,
GError **error)
{
- gchar *locale;
GError *internal_error = NULL;
/* We call an internal version of this function here
* because at the time 'initialized' = FALSE and that
* will cause errors and do nothing.
*/
- g_info ("Cleaning up database files for reindex");
+ g_debug ("Cleaning up database files for reindex");
tracker_db_manager_remove_all (db_manager);
/* Now create the databases and close them */
- g_info ("Creating database files, this may take a few moments...");
+ g_info ("Creating database files for %s...", db_manager->db.abs_filename);
db_manager->db.iface = tracker_db_manager_create_db_interface (db_manager, FALSE, &internal_error);
if (internal_error) {
@@ -512,11 +513,6 @@ db_recreate_all (TrackerDBManager *db_manager,
g_clear_object (&db_manager->db.iface);
g_clear_object (&db_manager->db.wal_iface);
-
- locale = tracker_locale_get (TRACKER_LOCALE_COLLATE);
- /* Initialize locale file */
- db_set_locale (db_manager, locale);
- g_free (locale);
}
void
@@ -533,8 +529,6 @@ tracker_db_manager_ensure_locations (TrackerDBManager *db_manager,
db_manager->locations_initialized = TRUE;
db_manager->data_dir = g_file_get_path (cache_location);
- /* For DISABLE_JOURNAL case we should use g_get_user_data_dir here. For now
- * keeping this as-is */
db_manager->user_data_dir = g_file_get_path (data_location);
db_manager->db = db_base;
@@ -605,7 +599,7 @@ tracker_db_manager_new (TrackerDBManagerFlags flags,
return NULL;
}
- db_manager = g_new0 (TrackerDBManager, 1);
+ db_manager = g_object_new (TRACKER_TYPE_DB_MANAGER, NULL);
db_manager->vtab_data = vtab_data;
/* First set defaults for return values */
@@ -616,7 +610,7 @@ tracker_db_manager_new (TrackerDBManagerFlags flags,
need_reindex = FALSE;
/* Set up locations */
- g_info ("Setting database locations");
+ g_debug ("Setting database locations");
db_manager->flags = flags;
db_manager->s_cache_size = select_cache_size;
@@ -636,26 +630,13 @@ tracker_db_manager_new (TrackerDBManagerFlags flags,
if ((flags & TRACKER_DB_MANAGER_READONLY) == 0) {
/* Make sure the directories exist */
- g_info ("Checking database directories exist");
+ g_debug ("Checking database directories exist");
g_mkdir_with_parents (db_manager->data_dir, 00755);
g_mkdir_with_parents (db_manager->user_data_dir, 00755);
-
- g_info ("Checking database version");
-
- version = db_get_version (db_manager);
-
- if (version < TRACKER_DB_VERSION_NOW) {
- g_info (" A reindex will be forced");
- need_reindex = TRUE;
- }
-
- if (need_reindex) {
- tracker_db_manager_create_version_file (db_manager);
- }
}
- g_info ("Checking whether database files exist");
+ g_debug ("Checking whether database files exist");
/* Check we have the database in place, if it is
* missing, we reindex.
@@ -663,8 +644,7 @@ tracker_db_manager_new (TrackerDBManagerFlags flags,
* There's no need to check for files not existing (for
* reindex) if reindexing is already needed.
*/
- if (!need_reindex &&
- !g_file_test (db_manager->db.abs_filename, G_FILE_TEST_EXISTS)) {
+ if (!g_file_test (db_manager->db.abs_filename, G_FILE_TEST_EXISTS)) {
if ((flags & TRACKER_DB_MANAGER_READONLY) == 0) {
g_info ("Could not find database file:'%s', reindex will be forced", db_manager->db.abs_filename);
need_reindex = TRUE;
@@ -674,11 +654,21 @@ tracker_db_manager_new (TrackerDBManagerFlags flags,
TRACKER_DB_OPEN_ERROR,
"Could not find database file:'%s'.", db_manager->db.abs_filename);
- tracker_db_manager_free (db_manager);
+ g_object_unref (db_manager);
return NULL;
}
+ } else {
+ g_info ("Checking database version");
+
+ version = db_get_version (db_manager);
+
+ if (version < TRACKER_DB_VERSION_NOW) {
+ g_info (" A reindex will be forced");
+ need_reindex = TRUE;
+ }
}
+
db_manager->locations_initialized = TRUE;
/* Don't do remove-dbs for readonly (direct-access) */
@@ -712,7 +702,7 @@ tracker_db_manager_new (TrackerDBManagerFlags flags,
TRACKER_DB_OPEN_ERROR,
"No reindexing supported in read-only mode (direct access)");
- tracker_db_manager_free (db_manager);
+ g_object_unref (db_manager);
return NULL;
}
@@ -720,32 +710,26 @@ tracker_db_manager_new (TrackerDBManagerFlags flags,
if (internal_error) {
g_propagate_error (error, internal_error);
- tracker_db_manager_free (db_manager);
+ g_object_unref (db_manager);
return NULL;
}
+ tracker_db_manager_update_version (db_manager);
+
/* Load databases */
- g_info ("Loading databases files...");
+ g_info ("Loading files for database %s...", db_manager->db.abs_filename);
} else if ((flags & TRACKER_DB_MANAGER_READONLY) == 0) {
/* do not do shutdown check for read-only mode (direct access) */
gboolean must_recreate = FALSE;
/* Load databases */
- g_info ("Loading databases files...");
-
-#ifndef DISABLE_JOURNAL
- must_recreate = !tracker_db_journal_reader_verify_last (data_location,
- NULL);
-#endif /* DISABLE_JOURNAL */
+ g_info ("Loading files for database %s...", db_manager->db.abs_filename);
if (!must_recreate && g_file_test (db_manager->in_use_filename, G_FILE_TEST_EXISTS)) {
gsize size = 0;
struct stat st;
TrackerDBStatement *stmt;
-#ifndef DISABLE_JOURNAL
- gchar *busy_status;
-#endif /* DISABLE_JOURNAL */
g_info ("Didn't shut down cleanly last time, doing integrity checks");
@@ -764,7 +748,7 @@ tracker_db_manager_new (TrackerDBManagerFlags flags,
TRACKER_DB_INTERFACE_ERROR,
TRACKER_DB_OPEN_ERROR,
"Corrupt db file");
- tracker_db_manager_free (db_manager);
+ g_object_unref (db_manager);
return NULL;
}
}
@@ -780,18 +764,19 @@ tracker_db_manager_new (TrackerDBManagerFlags flags,
must_recreate = TRUE;
} else {
g_propagate_error (error, internal_error);
- tracker_db_manager_free (db_manager);
+ g_object_unref (db_manager);
return NULL;
}
}
}
if (!must_recreate) {
+ gchar *busy_status;
+
db_manager->db.mtime = tracker_file_get_mtime (db_manager->db.abs_filename);
loaded = TRUE;
-#ifndef DISABLE_JOURNAL
/* Report OPERATION - STATUS */
busy_status = g_strdup_printf ("%s - %s",
busy_operation,
@@ -831,7 +816,6 @@ tracker_db_manager_new (TrackerDBManagerFlags flags,
g_object_unref (cursor);
}
}
-#endif /* DISABLE_JOURNAL */
}
if (!must_recreate) {
@@ -853,12 +837,7 @@ tracker_db_manager_new (TrackerDBManagerFlags flags,
}
if (must_recreate) {
- g_info ("Database severely damaged. We will recreate it"
-#ifndef DISABLE_JOURNAL
- " and replay the journal if available.");
-#else
- ".");
-#endif /* DISABLE_JOURNAL */
+ g_info ("Database severely damaged. We will recreate it.");
perform_recreate (db_manager, first_time, &internal_error);
if (internal_error) {
@@ -905,12 +884,12 @@ tracker_db_manager_new (TrackerDBManagerFlags flags,
/* Most serious error is the recreate one here */
g_clear_error (&internal_error);
g_propagate_error (error, new_error);
- tracker_db_manager_free (db_manager);
+ g_object_unref (db_manager);
return NULL;
}
} else {
g_propagate_error (error, internal_error);
- tracker_db_manager_free (db_manager);
+ g_object_unref (db_manager);
return NULL;
}
}
@@ -921,8 +900,9 @@ tracker_db_manager_new (TrackerDBManagerFlags flags,
}
void
-tracker_db_manager_free (TrackerDBManager *db_manager)
+tracker_db_manager_finalize (GObject *object)
{
+ TrackerDBManager *db_manager = TRACKER_DB_MANAGER (object);
gboolean readonly = (db_manager->flags & TRACKER_DB_MANAGER_READONLY) != 0;
g_async_queue_unref (db_manager->interfaces);
@@ -950,7 +930,8 @@ tracker_db_manager_free (TrackerDBManager *db_manager)
}
g_free (db_manager->in_use_filename);
- g_free (db_manager);
+
+ G_OBJECT_CLASS (tracker_db_manager_parent_class)->finalize (object);
}
void
@@ -1016,10 +997,12 @@ tracker_db_manager_create_db_interface (TrackerDBManager *db_manager,
tracker_db_interface_init_vtabs (connection, db_manager->vtab_data);
- db_set_params (connection,
+ iface_set_params (connection,
+ readonly,
+ &internal_error);
+ db_set_params (connection, "main",
db_manager->db.cache_size,
db_manager->db.page_size,
- readonly,
&internal_error);
if (internal_error) {
@@ -1074,15 +1057,15 @@ tracker_db_manager_get_db_interface (TrackerDBManager *db_manager)
interface = NULL;
}
- if (!interface) {
+ if (interface) {
+ g_signal_emit (db_manager, signals[UPDATE_INTERFACE], 0, interface);
+ } else {
/* Create a new one to satisfy the request */
interface = tracker_db_manager_create_db_interface (db_manager,
TRUE, &internal_error);
if (interface) {
-#if HAVE_TRACKER_FTS
- tracker_data_manager_init_fts (interface, FALSE);
-#endif
+ g_signal_emit (db_manager, signals[SETUP_INTERFACE], 0, interface);
} else {
if (g_async_queue_length_unlocked (db_manager->interfaces) == 0) {
g_critical ("Error opening database: %s", internal_error->message);
@@ -1104,13 +1087,41 @@ tracker_db_manager_get_db_interface (TrackerDBManager *db_manager)
}
static void
+tracker_db_manager_init (TrackerDBManager *manager)
+{
+}
+
+static void
+tracker_db_manager_class_init (TrackerDBManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = tracker_db_manager_finalize;
+
+ signals[SETUP_INTERFACE] =
+ g_signal_new ("setup-interface",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1, TRACKER_TYPE_DB_INTERFACE);
+ signals[UPDATE_INTERFACE] =
+ g_signal_new ("update-interface",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1, TRACKER_TYPE_DB_INTERFACE);
+}
+
+static void
wal_checkpoint (TrackerDBInterface *iface,
gboolean blocking)
{
GError *error = NULL;
- g_debug ("Checkpointing database...");
-
tracker_db_interface_sqlite_wal_checkpoint (iface, blocking,
blocking ? &error : NULL);
@@ -1120,8 +1131,6 @@ wal_checkpoint (TrackerDBInterface *iface,
error->message);
g_error_free (error);
}
-
- g_debug ("Checkpointing complete");
}
static gpointer
@@ -1132,7 +1141,8 @@ wal_checkpoint_thread (gpointer data)
if (!db_manager->db.wal_iface)
db_manager->db.wal_iface = init_writable_db_interface (db_manager);
- wal_checkpoint (db_manager->db.wal_iface, FALSE);
+ if (db_manager->db.wal_iface)
+ wal_checkpoint (db_manager->db.wal_iface, FALSE);
return NULL;
}
@@ -1208,29 +1218,19 @@ tracker_db_manager_has_enough_space (TrackerDBManager *db_manager)
return tracker_file_system_has_enough_space (db_manager->data_dir, TRACKER_DB_MIN_REQUIRED_SPACE, FALSE);
}
-inline static gchar *
-get_parser_version_filename (TrackerDBManager *db_manager)
-{
- return g_build_filename (db_manager->data_dir,
- PARSER_VERSION_FILENAME,
- NULL);
-}
-
-
gboolean
tracker_db_manager_get_tokenizer_changed (TrackerDBManager *db_manager)
{
- gchar *filename, *version;
- gboolean changed = TRUE;
-
- filename = get_parser_version_filename (db_manager);
+ GValue value = G_VALUE_INIT;
+ const gchar *version;
+ gboolean changed;
- if (g_file_get_contents (filename, &version, NULL, NULL)) {
- changed = strcmp (version, TRACKER_PARSER_VERSION_STRING) != 0;
- g_free (version);
- }
+ if (!tracker_db_manager_get_metadata (db_manager, "parser-version", &value))
+ return TRUE;
- g_free (filename);
+ version = g_value_get_string (&value);
+ changed = strcmp (version, TRACKER_PARSER_VERSION_STRING) != 0;
+ g_value_unset (&value);
return changed;
}
@@ -1238,20 +1238,13 @@ tracker_db_manager_get_tokenizer_changed (TrackerDBManager *db_manager)
void
tracker_db_manager_tokenizer_update (TrackerDBManager *db_manager)
{
- GError *error = NULL;
- gchar *filename;
+ GValue value = G_VALUE_INIT;
- filename = get_parser_version_filename (db_manager);
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_set_string (&value, TRACKER_PARSER_VERSION_STRING);
- if (!g_file_set_contents (filename, TRACKER_PARSER_VERSION_STRING, -1, &error)) {
- g_warning ("The file '%s' could not be rewritten by Tracker and "
- "should be deleted manually. Not doing so will result "
- "in Tracker rebuilding its FTS tokens on every startup. "
- "The error received was: '%s'", filename, error->message);
- g_error_free (error);
- }
-
- g_free (filename);
+ tracker_db_manager_set_metadata (db_manager, "parser-version", &value);
+ g_value_unset (&value);
}
void
@@ -1265,3 +1258,54 @@ tracker_db_manager_check_perform_vacuum (TrackerDBManager *db_manager)
iface = tracker_db_manager_get_writable_db_interface (db_manager);
tracker_db_interface_execute_query (iface, NULL, "VACUUM");
}
+
+gboolean
+tracker_db_manager_attach_database (TrackerDBManager *db_manager,
+ TrackerDBInterface *iface,
+ const gchar *name,
+ gboolean create,
+ GError **error)
+{
+ gchar *filename, *escaped;
+ GFile *file;
+
+ filename = g_strdup_printf ("%s.db", name);
+ escaped = g_uri_escape_string (filename, NULL, FALSE);
+ file = g_file_get_child (db_manager->cache_location, escaped);
+ g_free (filename);
+ g_free (escaped);
+
+ if (create) {
+ GError *inner_error = NULL;
+
+ /* Create the database from scratch */
+ if (!g_file_delete (file, NULL, &inner_error)) {
+ if (!g_error_matches (inner_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
+ g_object_unref (file);
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+ }
+ }
+
+ if (!tracker_db_interface_attach_database (iface, file, name, error)) {
+ g_object_unref (file);
+ return FALSE;
+ }
+
+ g_object_unref (file);
+ db_set_params (iface, name,
+ db_manager->db.cache_size,
+ db_manager->db.page_size,
+ error);
+ return TRUE;
+}
+
+gboolean
+tracker_db_manager_detach_database (TrackerDBManager *db_manager,
+ TrackerDBInterface *iface,
+ const gchar *name,
+ GError **error)
+{
+ return tracker_db_interface_detach_database (iface, name, error);
+}
diff --git a/src/libtracker-data/tracker-db-manager.h b/src/libtracker-data/tracker-db-manager.h
index 432a39810..8ec850e7f 100644
--- a/src/libtracker-data/tracker-db-manager.h
+++ b/src/libtracker-data/tracker-db-manager.h
@@ -30,6 +30,10 @@ G_BEGIN_DECLS
#error "only <libtracker-data/tracker-data.h> must be included directly."
#endif
+#define TRACKER_TYPE_DB_MANAGER (tracker_db_manager_get_type ())
+G_DECLARE_FINAL_TYPE (TrackerDBManager, tracker_db_manager,
+ TRACKER, DB_MANAGER, GObject)
+
#define TRACKER_DB_CACHE_SIZE_DEFAULT 250
#define TRACKER_DB_CACHE_SIZE_UPDATE 2000
@@ -57,7 +61,6 @@ TrackerDBManager *tracker_db_manager_new (TrackerDBManagerF
GObject *iface_data,
gpointer vtab_data,
GError **error);
-void tracker_db_manager_free (TrackerDBManager *db_manager);
void tracker_db_manager_remove_all (TrackerDBManager *db_manager);
void tracker_db_manager_optimize (TrackerDBManager *db_manager);
const gchar * tracker_db_manager_get_file (TrackerDBManager *db_manager);
@@ -68,8 +71,7 @@ void tracker_db_manager_ensure_locations (TrackerDBManager
GFile *cache_location,
GFile *data_location);
gboolean tracker_db_manager_has_enough_space (TrackerDBManager *db_manager);
-void tracker_db_manager_create_version_file (TrackerDBManager *db_manager);
-void tracker_db_manager_remove_version_file (TrackerDBManager *db_manager);
+void tracker_db_manager_update_version (TrackerDBManager *db_manager);
TrackerDBManagerFlags
tracker_db_manager_get_flags (TrackerDBManager *db_manager,
@@ -96,6 +98,16 @@ void tracker_db_manager_tokenizer_update (TrackerDBManager
void tracker_db_manager_check_perform_vacuum (TrackerDBManager *db_manager);
+gboolean tracker_db_manager_attach_database (TrackerDBManager *db_manager,
+ TrackerDBInterface *iface,
+ const gchar *name,
+ gboolean create,
+ GError **error);
+gboolean tracker_db_manager_detach_database (TrackerDBManager *db_manager,
+ TrackerDBInterface *iface,
+ const gchar *name,
+ GError **error);
+
G_END_DECLS
#endif /* __LIBTRACKER_DB_MANAGER_H__ */
diff --git a/src/libtracker-data/tracker-ontologies.c b/src/libtracker-data/tracker-ontologies.c
index fc058b23a..7a8a6589f 100644
--- a/src/libtracker-data/tracker-ontologies.c
+++ b/src/libtracker-data/tracker-ontologies.c
@@ -217,10 +217,16 @@ tracker_ontologies_get_class_by_uri (TrackerOntologies *ontologies,
if (!class && priv->gvdb_table) {
if (tracker_ontologies_get_class_string_gvdb (ontologies, class_uri, "name") != NULL) {
+ const gchar *id_str;
+
class = tracker_class_new (TRUE);
tracker_class_set_ontologies (class, ontologies);
tracker_class_set_uri (class, class_uri);
+ id_str = tracker_ontologies_get_class_string_gvdb (ontologies, class_uri, "id");
+ if (id_str)
+ tracker_class_set_id (class, g_ascii_strtoll (id_str, NULL, 10));
+
g_hash_table_insert (priv->class_uris,
g_strdup (class_uri),
class);
@@ -379,10 +385,16 @@ tracker_ontologies_get_property_by_uri (TrackerOntologies *ontologies,
if (!property && priv->gvdb_table) {
if (tracker_ontologies_get_property_string_gvdb (ontologies, uri, "name") != NULL) {
+ const gchar *id_str;
+
property = tracker_property_new (TRUE);
tracker_property_set_ontologies (property, ontologies);
tracker_property_set_uri (property, uri);
+ id_str = tracker_ontologies_get_property_string_gvdb (ontologies, uri, "id");
+ if (id_str)
+ tracker_property_set_id (property, g_ascii_strtoll (id_str, NULL, 10));
+
g_hash_table_insert (priv->property_uris,
g_strdup (uri),
property);
@@ -518,6 +530,7 @@ tracker_ontologies_write_gvdb (TrackerOntologies *ontologies,
GvdbItem *root, *item;
const gchar *uri;
gboolean retval;
+ gchar *str;
gint i;
root_table = gvdb_hash_table_new (NULL, NULL);
@@ -548,6 +561,10 @@ tracker_ontologies_write_gvdb (TrackerOntologies *ontologies,
item = gvdb_hash_table_insert_item (table, root, uri);
+ str = g_strdup_printf ("%d", tracker_class_get_id (class));
+ gvdb_hash_table_insert_statement (table, item, uri, "id", str);
+ g_free (str);
+
gvdb_hash_table_insert_statement (table, item, uri, "name", tracker_class_get_name (class));
super_classes = tracker_class_get_super_classes (class);
@@ -576,6 +593,10 @@ tracker_ontologies_write_gvdb (TrackerOntologies *ontologies,
item = gvdb_hash_table_insert_item (table, root, uri);
+ str = g_strdup_printf ("%d", tracker_property_get_id (property));
+ gvdb_hash_table_insert_statement (table, item, uri, "id", str);
+ g_free (str);
+
gvdb_hash_table_insert_statement (table, item, uri, "name", tracker_property_get_name (property));
gvdb_hash_table_insert_statement (table, item, uri, "domain", tracker_class_get_uri (tracker_property_get_domain (property)));
gvdb_hash_table_insert_statement (table, item, uri, "range", tracker_class_get_uri (tracker_property_get_range (property)));
diff --git a/src/libtracker-data/tracker-property.c b/src/libtracker-data/tracker-property.c
index 739d360a9..8fc4dfaf3 100644
--- a/src/libtracker-data/tracker-property.c
+++ b/src/libtracker-data/tracker-property.c
@@ -36,6 +36,7 @@
#define XSD_DOUBLE TRACKER_PREFIX_XSD "double"
#define XSD_INTEGER TRACKER_PREFIX_XSD "integer"
#define XSD_STRING TRACKER_PREFIX_XSD "string"
+#define RDF_LANGSTRING TRACKER_PREFIX_RDF "langString"
typedef struct _TrackerPropertyPrivate TrackerPropertyPrivate;
@@ -58,7 +59,6 @@ struct _TrackerPropertyPrivate {
gboolean fulltext_indexed;
gboolean multiple_values;
gboolean last_multiple_values;
- gboolean transient;
gboolean is_inverse_functional_property;
gboolean is_new;
gboolean db_schema_changed;
@@ -109,6 +109,9 @@ tracker_property_type_get_type (void)
{ TRACKER_PROPERTY_TYPE_RESOURCE,
"TRACKER_PROPERTY_TYPE_RESOURCE",
"resource" },
+ { TRACKER_PROPERTY_TYPE_LANGSTRING,
+ "TRACKER_PROPERTY_TYPE_LANGSTRING",
+ "langString" },
{ 0, NULL, NULL }
};
@@ -137,7 +140,6 @@ tracker_property_init (TrackerProperty *property)
priv->id = 0;
priv->weight = 1;
- priv->transient = FALSE;
priv->multiple_values = TRUE;
priv->force_journal = TRUE;
priv->super_properties = g_array_new (TRUE, TRUE, sizeof (TrackerProperty *));
@@ -220,19 +222,6 @@ tracker_property_get_uri (TrackerProperty *property)
return priv->uri;
}
-gboolean
-tracker_property_get_transient (TrackerProperty *property)
-{
- TrackerPropertyPrivate *priv;
-
- g_return_val_if_fail (TRACKER_IS_PROPERTY (property), FALSE);
-
- priv = tracker_property_get_instance_private (property);
-
- return priv->transient;
-}
-
-
const gchar *
tracker_property_get_name (TrackerProperty *property)
{
@@ -282,6 +271,8 @@ tracker_property_get_data_type (TrackerProperty *property)
range_uri = tracker_ontologies_get_property_string_gvdb (priv->ontologies, priv->uri, "range");
if (strcmp (range_uri, XSD_STRING) == 0) {
priv->data_type = TRACKER_PROPERTY_TYPE_STRING;
+ } else if (strcmp (range_uri, RDF_LANGSTRING) == 0) {
+ priv->data_type = TRACKER_PROPERTY_TYPE_LANGSTRING;
} else if (strcmp (range_uri, XSD_BOOLEAN) == 0) {
priv->data_type = TRACKER_PROPERTY_TYPE_BOOLEAN;
} else if (strcmp (range_uri, XSD_INTEGER) == 0) {
@@ -728,19 +719,6 @@ tracker_property_set_uri (TrackerProperty *property,
}
void
-tracker_property_set_transient (TrackerProperty *property,
- gboolean value)
-{
- TrackerPropertyPrivate *priv;
-
- g_return_if_fail (TRACKER_IS_PROPERTY (property));
-
- priv = tracker_property_get_instance_private (property);
-
- priv->transient = value;
-}
-
-void
tracker_property_set_domain (TrackerProperty *property,
TrackerClass *value)
{
@@ -855,6 +833,8 @@ tracker_property_set_range (TrackerProperty *property,
range_uri = tracker_class_get_uri (priv->range);
if (strcmp (range_uri, XSD_STRING) == 0) {
priv->data_type = TRACKER_PROPERTY_TYPE_STRING;
+ } else if (strcmp (range_uri, RDF_LANGSTRING) == 0) {
+ priv->data_type = TRACKER_PROPERTY_TYPE_LANGSTRING;
} else if (strcmp (range_uri, XSD_BOOLEAN) == 0) {
priv->data_type = TRACKER_PROPERTY_TYPE_BOOLEAN;
} else if (strcmp (range_uri, XSD_INTEGER) == 0) {
diff --git a/src/libtracker-data/tracker-property.h b/src/libtracker-data/tracker-property.h
index d6c314d47..5101a791a 100644
--- a/src/libtracker-data/tracker-property.h
+++ b/src/libtracker-data/tracker-property.h
@@ -44,6 +44,7 @@ typedef enum {
TRACKER_PROPERTY_TYPE_DATE,
TRACKER_PROPERTY_TYPE_DATETIME,
TRACKER_PROPERTY_TYPE_RESOURCE,
+ TRACKER_PROPERTY_TYPE_LANGSTRING,
} TrackerPropertyType;
GType tracker_property_type_get_type (void) G_GNUC_CONST;
@@ -89,7 +90,6 @@ gboolean tracker_property_get_fulltext_indexed (TrackerProperty
gboolean tracker_property_get_multiple_values (TrackerProperty *property);
gboolean tracker_property_get_last_multiple_values(TrackerProperty *property);
gboolean tracker_property_get_orig_multiple_values(TrackerProperty *property);
-gboolean tracker_property_get_transient (TrackerProperty *property);
gboolean tracker_property_get_is_new (TrackerProperty *property);
gboolean tracker_property_get_is_new_domain_index (TrackerProperty *property,
TrackerClass *class);
@@ -130,8 +130,6 @@ void tracker_property_set_last_multiple_values(TrackerProperty
gboolean value);
void tracker_property_set_orig_multiple_values(TrackerProperty *property,
gboolean value);
-void tracker_property_set_transient (TrackerProperty *property,
- gboolean value);
void tracker_property_set_is_new (TrackerProperty *property,
gboolean value);
void tracker_property_set_is_new_domain_index (TrackerProperty *property,
diff --git a/src/libtracker-data/tracker-sparql-grammar.h b/src/libtracker-data/tracker-sparql-grammar.h
index 0dbbf2e62..0372a890f 100644
--- a/src/libtracker-data/tracker-sparql-grammar.h
+++ b/src/libtracker-data/tracker-sparql-grammar.h
@@ -1149,7 +1149,7 @@ static const TrackerGrammarRule helper_InlineDataFull_seq_1[] = { L(OPEN_PARENS)
static const TrackerGrammarRule helper_InlineDataFull_or_1[] = { T(NIL), S(helper_InlineDataFull_seq_1), NIL };
static const TrackerGrammarRule helper_InlineDataFull_gte0_2[] = { R(DataBlockValue), NIL };
static const TrackerGrammarRule helper_InlineDataFull_seq_2[] = { L(OPEN_PARENS), GTE0(helper_InlineDataFull_gte0_2), L(CLOSE_PARENS), NIL };
-static const TrackerGrammarRule helper_InlineDataFull_or_2[] = { S(helper_InlineDataFull_seq_2), T(NIL), NIL };
+static const TrackerGrammarRule helper_InlineDataFull_or_2[] = { T(NIL), S(helper_InlineDataFull_seq_2), NIL };
static const TrackerGrammarRule helper_InlineDataFull_gte0_3[] = { OR(helper_InlineDataFull_or_2), NIL };
static const TrackerGrammarRule rule_InlineDataFull[] = { OR(helper_InlineDataFull_or_1), L(OPEN_BRACE), GTE0(helper_InlineDataFull_gte0_3), L(CLOSE_BRACE), NIL };
@@ -1249,7 +1249,7 @@ static const TrackerGrammarRule rule_GraphRefAll[] = { OR(helper_GraphRefAll_or)
/* GraphOrDefault ::= 'DEFAULT' | 'GRAPH'? iri
*/
-static const TrackerGrammarRule helper_GraphOrDefault_seq[] = { L(NAMED), R(iri), NIL };
+static const TrackerGrammarRule helper_GraphOrDefault_seq[] = { L(GRAPH), R(iri), NIL };
static const TrackerGrammarRule helper_GraphOrDefault_or[] = { L(DEFAULT), S(helper_GraphOrDefault_seq), NIL };
static const TrackerGrammarRule rule_GraphOrDefault[] = { OR(helper_GraphOrDefault_or), NIL };
diff --git a/src/libtracker-data/tracker-sparql-parser.c b/src/libtracker-data/tracker-sparql-parser.c
index 8afee0587..71a963209 100644
--- a/src/libtracker-data/tracker-sparql-parser.c
+++ b/src/libtracker-data/tracker-sparql-parser.c
@@ -598,6 +598,7 @@ tracker_parser_state_iterate (TrackerParserState *state,
child = tracker_parser_state_lookup_child (state);
if (child) {
+ tracker_parser_state_skip_whitespace (state, parser);
tracker_parser_state_push (state, child);
return TRUE;
}
@@ -611,6 +612,7 @@ tracker_parser_state_iterate (TrackerParserState *state,
if (tracker_parser_state_next_child (state, TRUE)) {
child = tracker_parser_state_lookup_child (state);
+ tracker_parser_state_skip_whitespace (state, parser);
tracker_parser_state_push (state, child);
return TRUE;
}
@@ -640,6 +642,7 @@ tracker_parser_state_rollback (TrackerParserState *state,
if (tracker_parser_state_next_child (state, FALSE)) {
tracker_node_tree_reset (state->node_tree, discard);
child = tracker_parser_state_lookup_child (state);
+ tracker_parser_state_skip_whitespace (state, parser);
tracker_parser_state_push (state, child);
return TRUE;
}
@@ -678,11 +681,9 @@ static gboolean
tracker_grammar_parser_read (TrackerGrammarParser *parser,
TrackerParserState *state)
{
-
while (state->rule_states.len > 0) {
const TrackerGrammarRule *rule;
- tracker_parser_state_skip_whitespace (state, parser);
rule = tracker_parser_state_peek_current_rule (state);
if (tracker_grammar_parser_apply_rule (parser, state, rule)) {
diff --git a/src/libtracker-data/tracker-sparql-types.c b/src/libtracker-data/tracker-sparql-types.c
index 38765cb44..254523238 100644
--- a/src/libtracker-data/tracker-sparql-types.c
+++ b/src/libtracker-data/tracker-sparql-types.c
@@ -33,13 +33,13 @@ enum {
/* Helper structs */
static TrackerDataTable *
tracker_data_table_new (const gchar *tablename,
- const gchar *subject,
+ const gchar *graph,
gint idx)
{
TrackerDataTable *table;
table = g_new0 (TrackerDataTable, 1);
- table->subject = g_strdup (subject);
+ table->graph = g_strdup (graph);
table->sql_db_tablename = g_strdup (tablename);
table->sql_query_tablename = g_strdup_printf ("%s%d", tablename, idx);
@@ -49,7 +49,7 @@ tracker_data_table_new (const gchar *tablename,
static void
tracker_data_table_free (TrackerDataTable *table)
{
- g_free (table->subject);
+ g_free (table->graph);
g_free (table->sql_db_tablename);
g_free (table->sql_query_tablename);
g_free (table);
@@ -62,6 +62,13 @@ tracker_data_table_set_predicate_variable (TrackerDataTable *table,
table->predicate_variable = is_variable;
}
+void
+tracker_data_table_set_predicate_path (TrackerDataTable *table,
+ gboolean is_path)
+{
+ table->predicate_path = is_path;
+}
+
static TrackerVariable *
tracker_variable_new (const gchar *sql_prefix,
const gchar *name)
@@ -141,10 +148,13 @@ tracker_variable_equal (gconstpointer data1,
void
tracker_token_literal_init (TrackerToken *token,
- const gchar *literal)
+ const gchar *literal,
+ gssize len)
{
+ if (len < 0)
+ len = strlen (literal) + 1;
token->type = TOKEN_TYPE_LITERAL;
- token->content.literal = g_strdup (literal);
+ token->content.literal = g_bytes_new (literal, len);
}
void
@@ -175,7 +185,7 @@ void
tracker_token_unset (TrackerToken *token)
{
if (token->type == TOKEN_TYPE_LITERAL)
- g_clear_pointer (&token->content.literal, g_free);
+ g_clear_pointer (&token->content.literal, g_bytes_unref);
else if (token->type == TOKEN_TYPE_PARAMETER)
g_clear_pointer (&token->content.parameter, g_free);
token->type = TOKEN_TYPE_NONE;
@@ -187,7 +197,7 @@ tracker_token_is_empty (TrackerToken *token)
return token->type == TOKEN_TYPE_NONE;
}
-const gchar *
+GBytes *
tracker_token_get_literal (TrackerToken *token)
{
if (token->type == TOKEN_TYPE_LITERAL)
@@ -223,7 +233,7 @@ const gchar *
tracker_token_get_idstring (TrackerToken *token)
{
if (token->type == TOKEN_TYPE_LITERAL)
- return token->content.literal;
+ return g_bytes_get_data (token->content.literal, NULL);
else if (token->type == TOKEN_TYPE_VARIABLE)
return token->content.var->sql_expression;
else if (token->type == TOKEN_TYPE_PATH)
@@ -391,7 +401,7 @@ tracker_literal_binding_finalize (GObject *object)
{
TrackerLiteralBinding *binding = TRACKER_LITERAL_BINDING (object);
- g_free (binding->literal);
+ g_bytes_unref (binding->bytes);
G_OBJECT_CLASS (tracker_literal_binding_parent_class)->finalize (object);
}
@@ -410,14 +420,15 @@ tracker_literal_binding_init (TrackerLiteralBinding *binding)
}
TrackerBinding *
-tracker_literal_binding_new (const gchar *literal,
+tracker_literal_binding_new (GBytes *bytes,
TrackerDataTable *table)
{
TrackerBinding *binding;
binding = g_object_new (TRACKER_TYPE_LITERAL_BINDING, NULL);
binding->table = table;
- TRACKER_LITERAL_BINDING (binding)->literal = g_strdup (literal);
+ TRACKER_LITERAL_BINDING (binding)->bytes = g_bytes_ref (bytes);
+ TRACKER_LITERAL_BINDING (binding)->literal = g_bytes_get_data (bytes, NULL);
return binding;
}
@@ -523,14 +534,17 @@ tracker_path_element_free (TrackerPathElement *elem)
}
TrackerPathElement *
-tracker_path_element_property_new (TrackerProperty *prop)
+tracker_path_element_property_new (TrackerPathOperator op,
+ TrackerProperty *prop)
{
TrackerPathElement *elem;
g_return_val_if_fail (TRACKER_IS_PROPERTY (prop), NULL);
+ g_return_val_if_fail (op == TRACKER_PATH_OPERATOR_NONE ||
+ op == TRACKER_PATH_OPERATOR_NEGATED, NULL);
elem = g_new0 (TrackerPathElement, 1);
- elem->op = TRACKER_PATH_OPERATOR_NONE;
+ elem->op = op;
elem->type = tracker_property_get_data_type (prop);
elem->data.property = prop;
@@ -544,11 +558,13 @@ tracker_path_element_operator_new (TrackerPathOperator op,
{
TrackerPathElement *elem;
- g_return_val_if_fail (op != TRACKER_PATH_OPERATOR_NONE, NULL);
+ g_return_val_if_fail (op != TRACKER_PATH_OPERATOR_NONE &&
+ op != TRACKER_PATH_OPERATOR_NEGATED, NULL);
g_return_val_if_fail (child1 != NULL, NULL);
g_return_val_if_fail (child2 == NULL ||
op == TRACKER_PATH_OPERATOR_SEQUENCE ||
- op == TRACKER_PATH_OPERATOR_ALTERNATIVE, NULL);
+ op == TRACKER_PATH_OPERATOR_ALTERNATIVE ||
+ op == TRACKER_PATH_OPERATOR_INTERSECTION, NULL);
elem = g_new0 (TrackerPathElement, 1);
elem->op = op;
@@ -587,6 +603,12 @@ tracker_path_element_set_unique_name (TrackerPathElement *elem,
case TRACKER_PATH_OPERATOR_ONEORMORE:
name = "oneormore";
break;
+ case TRACKER_PATH_OPERATOR_NEGATED:
+ name = "neg";
+ break;
+ case TRACKER_PATH_OPERATOR_INTERSECTION:
+ name = "intersect";
+ break;
default:
g_assert_not_reached ();
}
@@ -775,12 +797,19 @@ void
tracker_select_context_add_literal_binding (TrackerSelectContext *context,
TrackerLiteralBinding *binding)
{
+ gint i;
+
/* Literal bindings are reserved to the root context */
g_assert (TRACKER_CONTEXT (context)->parent == NULL);
if (!context->literal_bindings)
context->literal_bindings = g_ptr_array_new_with_free_func (g_object_unref);
+ for (i = 0; i < context->literal_bindings->len; i++) {
+ if (binding == g_ptr_array_index (context->literal_bindings, i))
+ return;
+ }
+
g_ptr_array_add (context->literal_bindings, g_object_ref (binding));
}
@@ -877,7 +906,7 @@ tracker_triple_context_new (void)
TrackerDataTable *
tracker_triple_context_lookup_table (TrackerTripleContext *context,
- const gchar *subject,
+ const gchar *graph,
const gchar *tablename)
{
TrackerDataTable *table = NULL;
@@ -888,7 +917,7 @@ tracker_triple_context_lookup_table (TrackerTripleContext *context,
table = g_ptr_array_index (context->sql_tables, i);
- if (g_strcmp0 (table->subject, subject) == 0 &&
+ if (g_strcmp0 (table->graph, graph) == 0 &&
g_strcmp0 (table->sql_db_tablename, tablename) == 0)
return table;
}
@@ -898,12 +927,12 @@ tracker_triple_context_lookup_table (TrackerTripleContext *context,
TrackerDataTable *
tracker_triple_context_add_table (TrackerTripleContext *context,
- const gchar *subject,
+ const gchar *graph,
const gchar *tablename)
{
TrackerDataTable *table;
- table = tracker_data_table_new (tablename, subject, ++context->table_counter);
+ table = tracker_data_table_new (tablename, graph, ++context->table_counter);
g_ptr_array_add (context->sql_tables, table);
return table;
diff --git a/src/libtracker-data/tracker-sparql-types.h b/src/libtracker-data/tracker-sparql-types.h
index a474f5142..2580f6cdd 100644
--- a/src/libtracker-data/tracker-sparql-types.h
+++ b/src/libtracker-data/tracker-sparql-types.h
@@ -73,10 +73,11 @@ typedef struct _TrackerSolution TrackerSolution;
typedef struct _TrackerPathElement TrackerPathElement;
struct _TrackerDataTable {
- gchar *subject; /* Subject this table is pulled from */
+ gchar *graph; /* Graph for this table, if specified */
gchar *sql_db_tablename; /* as in db schema */
gchar *sql_query_tablename; /* temp. name, generated */
gboolean predicate_variable;
+ gboolean predicate_path;
};
struct _TrackerBinding {
@@ -94,7 +95,8 @@ struct _TrackerBindingClass {
/* Represents a mapping of a SPARQL literal to a SQL table and column */
struct _TrackerLiteralBinding {
TrackerBinding parent_instance;
- gchar *literal;
+ GBytes *bytes;
+ const gchar *literal;
};
struct _TrackerLiteralBindingClass {
@@ -132,7 +134,7 @@ struct _TrackerVariable {
struct _TrackerToken {
guint type;
union {
- gchar *literal;
+ GBytes *literal;
gchar *parameter;
TrackerVariable *var;
TrackerPathElement *path;
@@ -154,6 +156,8 @@ typedef enum {
TRACKER_PATH_OPERATOR_ZEROORONE, /* ? */
TRACKER_PATH_OPERATOR_ONEORMORE, /* + */
TRACKER_PATH_OPERATOR_ZEROORMORE, /* * */
+ TRACKER_PATH_OPERATOR_NEGATED, /* ! */
+ TRACKER_PATH_OPERATOR_INTERSECTION, /* Used for negated paths */
} TrackerPathOperator;
struct _TrackerPathElement {
@@ -236,6 +240,8 @@ struct _TrackerTripleContextClass {
/* Data table */
void tracker_data_table_set_predicate_variable (TrackerDataTable *table,
gboolean is_variable);
+void tracker_data_table_set_predicate_path (TrackerDataTable *table,
+ gboolean is_path);
/* Binding */
GType tracker_binding_get_type (void) G_GNUC_CONST;
@@ -256,7 +262,7 @@ gchar * tracker_binding_get_extra_sql_expression (TrackerBinding *binding,
/* Literal binding */
GType tracker_literal_binding_get_type (void) G_GNUC_CONST;
-TrackerBinding * tracker_literal_binding_new (const gchar *literal,
+TrackerBinding * tracker_literal_binding_new (GBytes *bytes,
TrackerDataTable *table);
/* Parameter binding */
@@ -288,7 +294,8 @@ TrackerVariableBinding * tracker_variable_get_sample_binding (TrackerVariable *v
/* Token */
void tracker_token_literal_init (TrackerToken *token,
- const gchar *literal);
+ const gchar *literal,
+ gssize len);
void tracker_token_variable_init (TrackerToken *token,
TrackerVariable *variable);
void tracker_token_parameter_init (TrackerToken *token,
@@ -298,7 +305,7 @@ void tracker_token_path_init (TrackerToken *token,
void tracker_token_unset (TrackerToken *token);
gboolean tracker_token_is_empty (TrackerToken *token);
-const gchar * tracker_token_get_literal (TrackerToken *token);
+GBytes * tracker_token_get_literal (TrackerToken *token);
TrackerVariable * tracker_token_get_variable (TrackerToken *token);
const gchar * tracker_token_get_idstring (TrackerToken *token);
const gchar * tracker_token_get_parameter (TrackerToken *token);
@@ -317,7 +324,8 @@ void tracker_solution_add_value (TrackerSolution *solution,
GHashTable * tracker_solution_get_bindings (TrackerSolution *solution);
/* Property path element */
-TrackerPathElement * tracker_path_element_property_new (TrackerProperty *prop);
+TrackerPathElement * tracker_path_element_property_new (TrackerPathOperator op,
+ TrackerProperty *prop);
TrackerPathElement * tracker_path_element_operator_new (TrackerPathOperator op,
TrackerPathElement *child1,
TrackerPathElement *child2);
@@ -359,10 +367,10 @@ GType tracker_triple_context_get_type (void) G_GNUC_CONST;
TrackerContext * tracker_triple_context_new (void);
TrackerDataTable * tracker_triple_context_lookup_table (TrackerTripleContext *context,
- const gchar *subject,
+ const gchar *graph,
const gchar *table);
TrackerDataTable * tracker_triple_context_add_table (TrackerTripleContext *context,
- const gchar *subject,
+ const gchar *graph,
const gchar *table);
void tracker_triple_context_add_literal_binding (TrackerTripleContext *context,
TrackerLiteralBinding *binding);
diff --git a/src/libtracker-data/tracker-sparql.c b/src/libtracker-data/tracker-sparql.c
index e36d2341e..6c4b99fbe 100644
--- a/src/libtracker-data/tracker-sparql.c
+++ b/src/libtracker-data/tracker-sparql.c
@@ -29,6 +29,8 @@
#include "tracker-sparql-grammar.h"
#include "tracker-collation.h"
#include "tracker-db-interface-sqlite.h"
+#include "tracker-sparql-query.h"
+#include "tracker-utils.h"
#define TRACKER_NS "http://www.tracker-project.org/ontologies/tracker#"
#define RDF_NS "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
@@ -46,6 +48,12 @@ enum {
TIME_FORMAT_HOURS
};
+enum {
+ GRAPH_OP_DEFAULT,
+ GRAPH_OP_NAMED,
+ GRAPH_OP_ALL
+};
+
static inline gboolean _call_rule_func (TrackerSparql *sparql,
TrackerGrammarNamedRule rule,
GError **error);
@@ -57,7 +65,8 @@ static gboolean helper_translate_date (TrackerSparql *sparql,
static gboolean helper_translate_time (TrackerSparql *sparql,
guint format,
GError **error);
-static TrackerDBStatement * prepare_query (TrackerDBInterface *iface,
+static TrackerDBStatement * prepare_query (TrackerSparql *sparql,
+ TrackerDBInterface *iface,
TrackerStringBuilder *str,
GPtrArray *literals,
GHashTable *parameters,
@@ -65,6 +74,8 @@ static TrackerDBStatement * prepare_query (TrackerDBInterface *iface,
GError **error);
static inline TrackerVariable * _ensure_variable (TrackerSparql *sparql,
const gchar *name);
+static void convert_expression_to_string (TrackerSparql *sparql,
+ TrackerPropertyType type);
#define _raise(v,s,sub) \
G_STMT_START { \
@@ -74,8 +85,6 @@ static inline TrackerVariable * _ensure_variable (TrackerSparql *sparql,
return FALSE; \
} G_STMT_END
-#define _unimplemented(s) _raise(UNSUPPORTED, "Unsupported syntax", s)
-
/* Added for control flow simplicity. All processing will be stopped
* whenever any rule sets an error and returns FALSE.
*/
@@ -94,6 +103,7 @@ enum
TRACKER_SPARQL_TYPE_DELETE,
TRACKER_SPARQL_TYPE_INSERT,
TRACKER_SPARQL_TYPE_UPDATE,
+ TRACKER_SPARQL_TYPE_CONSTRUCT,
};
struct _TrackerSparql
@@ -113,6 +123,7 @@ struct _TrackerSparql
GPtrArray *var_names;
GArray *var_types;
+ GHashTable *cached_bindings;
GVariantBuilder *blank_nodes;
GHashTable *solution_var_map;
@@ -121,12 +132,18 @@ struct _TrackerSparql
gboolean cacheable;
GHashTable *parameters;
+ GHashTable *union_views;
+
+ GPtrArray *anon_graphs;
+ GPtrArray *named_graphs;
+ gchar *base;
struct {
TrackerContext *context;
TrackerContext *select_context;
TrackerStringBuilder *sql;
TrackerStringBuilder *with_clauses;
+ TrackerStringBuilder *construct_query;
TrackerParserNode *node;
TrackerParserNode *prev_node;
@@ -140,10 +157,13 @@ struct _TrackerSparql
TrackerPathElement *path;
GHashTable *blank_node_map;
+ TrackerVariableBinding *as_in_group_by;
const gchar *expression_list_separator;
TrackerPropertyType expression_type;
guint type;
+ guint graph_op;
+ gint values_idx;
gboolean convert_to_string;
} current_state;
@@ -159,6 +179,8 @@ tracker_sparql_finalize (GObject *object)
g_object_unref (sparql->data_manager);
g_hash_table_destroy (sparql->prefix_map);
g_hash_table_destroy (sparql->parameters);
+ g_hash_table_destroy (sparql->cached_bindings);
+ g_clear_pointer (&sparql->union_views, g_hash_table_unref);
if (sparql->sql)
tracker_string_builder_free (sparql->sql);
@@ -172,9 +194,15 @@ tracker_sparql_finalize (GObject *object)
tracker_token_unset (&sparql->current_state.subject);
tracker_token_unset (&sparql->current_state.predicate);
tracker_token_unset (&sparql->current_state.object);
+ g_clear_pointer (&sparql->current_state.construct_query,
+ tracker_string_builder_free);
+ g_clear_object (&sparql->current_state.as_in_group_by);
+ g_ptr_array_unref (sparql->named_graphs);
+ g_ptr_array_unref (sparql->anon_graphs);
g_ptr_array_unref (sparql->var_names);
g_array_unref (sparql->var_types);
+ g_free (sparql->base);
if (sparql->blank_nodes)
g_variant_builder_unref (sparql->blank_nodes);
@@ -234,6 +262,16 @@ tracker_sparql_swap_current_expression_list_separator (TrackerSparql *sparql,
}
static inline gchar *
+tracker_sparql_expand_base (TrackerSparql *sparql,
+ const gchar *term)
+{
+ if (sparql->base)
+ return tracker_resolve_relative_uri (sparql->base, term);
+ else
+ return g_strdup (term);
+}
+
+static inline gchar *
tracker_sparql_expand_prefix (TrackerSparql *sparql,
const gchar *term)
{
@@ -479,6 +517,7 @@ _append_literal_sql (TrackerSparql *sparql,
break;
case TRACKER_PROPERTY_TYPE_DATETIME:
case TRACKER_PROPERTY_TYPE_STRING:
+ case TRACKER_PROPERTY_TYPE_LANGSTRING:
case TRACKER_PROPERTY_TYPE_RESOURCE:
escaped = _escape_sql_string (binding->literal);
_append_string (sparql, escaped);
@@ -506,7 +545,8 @@ _append_literal_sql (TrackerSparql *sparql,
if (TRACKER_BINDING (binding)->data_type == TRACKER_PROPERTY_TYPE_RESOURCE)
_append_string_printf (sparql, "), 0) ");
- if (TRACKER_BINDING (binding)->data_type == TRACKER_PROPERTY_TYPE_STRING)
+ if (TRACKER_BINDING (binding)->data_type == TRACKER_PROPERTY_TYPE_STRING ||
+ TRACKER_BINDING (binding)->data_type == TRACKER_PROPERTY_TYPE_LANGSTRING)
_append_string (sparql, "COLLATE " TRACKER_COLLATION_NAME " ");
}
@@ -514,25 +554,8 @@ static void
_append_variable_sql (TrackerSparql *sparql,
TrackerVariable *variable)
{
- TrackerBinding *binding;
-
- binding = TRACKER_BINDING (tracker_variable_get_sample_binding (variable));
-
- if (binding &&
- binding->data_type == TRACKER_PROPERTY_TYPE_DATETIME) {
- TrackerVariable *local_time;
- gchar *name;
-
- name = g_strdup_printf ("%s:local", variable->name);
- local_time = _ensure_variable (sparql, name);
- g_free (name);
-
- _append_string_printf (sparql, "%s ",
- tracker_variable_get_sql_expression (local_time));
- } else {
- _append_string_printf (sparql, "%s ",
- tracker_variable_get_sql_expression (variable));
- }
+ _append_string_printf (sparql, "%s ",
+ tracker_variable_get_sql_expression (variable));
}
static void
@@ -540,6 +563,7 @@ _prepend_path_element (TrackerSparql *sparql,
TrackerPathElement *path_elem)
{
TrackerStringBuilder *old;
+ gchar *table_name, *graph_column;
old = tracker_sparql_swap_builder (sparql, sparql->current_state.with_clauses);
@@ -551,25 +575,48 @@ _prepend_path_element (TrackerSparql *sparql,
switch (path_elem->op) {
case TRACKER_PATH_OPERATOR_NONE:
/* A simple property */
+ if (tracker_token_is_empty (&sparql->current_state.graph)) {
+ table_name = g_strdup_printf ("\"unionGraph_%s\"",
+ tracker_property_get_table_name (path_elem->data.property));
+ graph_column = g_strdup ("graph");
+
+ if (sparql->union_views) {
+ g_hash_table_add (sparql->union_views,
+ g_strdup (tracker_property_get_table_name (path_elem->data.property)));
+ }
+ } else {
+ const gchar *graph;
+
+ graph = tracker_token_get_idstring (&sparql->current_state.graph);
+ table_name = g_strdup_printf ("\"%s\".\"%s\"", graph,
+ tracker_property_get_table_name (path_elem->data.property));
+ graph_column = g_strdup_printf ("%d",
+ tracker_data_manager_find_graph (sparql->data_manager, graph));
+ }
+
_append_string_printf (sparql,
- "\"%s\" (ID, value, graph) AS "
- "(SELECT ID, \"%s\", \"%s:graph\" FROM \"%s\") ",
+ "\"%s\" (ID, value, graph, ID_type, value_type) AS "
+ "(SELECT ID, \"%s\", %s, %d, %d FROM %s) ",
path_elem->name,
tracker_property_get_name (path_elem->data.property),
- tracker_property_get_name (path_elem->data.property),
- tracker_property_get_table_name (path_elem->data.property));
+ graph_column,
+ TRACKER_PROPERTY_TYPE_RESOURCE,
+ tracker_property_get_data_type (path_elem->data.property),
+ table_name);
+ g_free (table_name);
+ g_free (graph_column);
break;
case TRACKER_PATH_OPERATOR_INVERSE:
_append_string_printf (sparql,
- "\"%s\" (ID, value, graph) AS "
- "(SELECT value, ID, graph FROM \"%s\" WHERE value IS NOT NULL) ",
+ "\"%s\" (ID, value, graph, ID_type, value_type) AS "
+ "(SELECT value, ID, graph, value_type, ID_type FROM \"%s\" WHERE value IS NOT NULL) ",
path_elem->name,
path_elem->data.composite.child1->name);
break;
case TRACKER_PATH_OPERATOR_SEQUENCE:
_append_string_printf (sparql,
- "\"%s\" (ID, value, graph) AS "
- "(SELECT a.ID, b.value, b.graph "
+ "\"%s\" (ID, value, graph, ID_type, value_type) AS "
+ "(SELECT a.ID, b.value, b.graph, a.ID_type, b.value_type "
"FROM \"%s\" AS a, \"%s\" AS b "
"WHERE a.value = b.ID) ",
path_elem->name,
@@ -578,11 +625,11 @@ _prepend_path_element (TrackerSparql *sparql,
break;
case TRACKER_PATH_OPERATOR_ALTERNATIVE:
_append_string_printf (sparql,
- "\"%s\" (ID, value, graph) AS "
- "(SELECT ID, value, graph "
+ "\"%s\" (ID, value, graph, ID_type, value_type) AS "
+ "(SELECT ID, value, graph, ID_type, value_type "
"FROM \"%s\" "
- "UNION ALL "
- "SELECT ID, value, graph "
+ "UNION "
+ "SELECT ID, value, graph, ID_type, value_type "
"FROM \"%s\") ",
path_elem->name,
path_elem->data.composite.child1->name,
@@ -590,11 +637,11 @@ _prepend_path_element (TrackerSparql *sparql,
break;
case TRACKER_PATH_OPERATOR_ZEROORMORE:
_append_string_printf (sparql,
- "\"%s\" (ID, value, graph) AS "
- "(SELECT ID, ID, graph "
+ "\"%s\" (ID, value, graph, ID_type, value_type) AS "
+ "(SELECT ID, ID, graph, ID_type, ID_type "
"FROM \"%s\" "
"UNION "
- "SELECT a.ID, b.value, b.graph "
+ "SELECT a.ID, b.value, b.graph, a.ID_type, b.value_type "
"FROM \"%s\" AS a, \"%s\" AS b "
"WHERE b.ID = a.value) ",
path_elem->name,
@@ -604,11 +651,11 @@ _prepend_path_element (TrackerSparql *sparql,
break;
case TRACKER_PATH_OPERATOR_ONEORMORE:
_append_string_printf (sparql,
- "\"%s\" (ID, value, graph) AS "
- "(SELECT ID, value, graph "
+ "\"%s\" (ID, value, graph, ID_type, value_type) AS "
+ "(SELECT ID, value, graph, ID_type, value_type "
"FROM \"%s\" "
"UNION "
- "SELECT a.ID, b.value, b.graph "
+ "SELECT a.ID, b.value, b.graph, a.ID_type, b.value_type "
"FROM \"%s\" AS a, \"%s\" AS b "
"WHERE b.ID = a.value) ",
path_elem->name,
@@ -618,16 +665,50 @@ _prepend_path_element (TrackerSparql *sparql,
break;
case TRACKER_PATH_OPERATOR_ZEROORONE:
_append_string_printf (sparql,
- "\"%s\" (ID, value, graph) AS "
- "(SELECT ID, ID, graph "
+ "\"%s\" (ID, value, graph, ID_type, value_type) AS "
+ "(SELECT ID, ID, graph, ID_type, ID_type "
"FROM \"%s\" "
- "UNION ALL "
- "SELECT ID, value, graph "
+ "UNION "
+ "SELECT ID, value, graph, ID_type, value_type "
"FROM \"%s\") ",
path_elem->name,
path_elem->data.composite.child1->name,
path_elem->data.composite.child1->name);
break;
+ case TRACKER_PATH_OPERATOR_NEGATED:
+ _append_string_printf (sparql,
+ "\"%s\" (ID, value, graph, ID_type, value_type) AS "
+ "(SELECT subject AS ID, object AS value, graph, %d, object_type "
+ "FROM \"tracker_triples\" "
+ "WHERE predicate != %d ",
+ path_elem->name,
+ TRACKER_PROPERTY_TYPE_RESOURCE,
+ tracker_property_get_id (path_elem->data.property));
+
+ if (!tracker_token_is_empty (&sparql->current_state.graph)) {
+ const gchar *graph;
+
+ graph = tracker_token_get_idstring (&sparql->current_state.graph);
+ _append_string_printf (sparql,
+ "AND graph = %d",
+ tracker_data_manager_find_graph (sparql->data_manager, graph));
+ }
+
+ _append_string (sparql, ") ");
+ g_clear_pointer (&sparql->union_views, g_hash_table_unref);
+ break;
+ case TRACKER_PATH_OPERATOR_INTERSECTION:
+ _append_string_printf (sparql,
+ "\"%s\" (ID, value, graph, ID_type, value_type) AS "
+ "(SELECT ID, value, graph, ID_type, value_type "
+ "FROM \"%s\" "
+ "INTERSECT "
+ "SELECT ID, value, graph, ID_type, value_type "
+ "FROM \"%s\") ",
+ path_elem->name,
+ path_elem->data.composite.child1->name,
+ path_elem->data.composite.child2->name);
+ break;
}
tracker_sparql_swap_builder (sparql, old);
@@ -680,9 +761,17 @@ _extract_node_string (TrackerParserNode *node,
add_start = subtract_end = 3;
compress = TRUE;
break;
- case TERMINAL_TYPE_IRIREF:
+ case TERMINAL_TYPE_IRIREF: {
+ gchar *unexpanded;
+
add_start = subtract_end = 1;
+ unexpanded = g_strndup (terminal_start + add_start,
+ terminal_end - terminal_start -
+ add_start - subtract_end);
+ str = tracker_sparql_expand_base (sparql, unexpanded);
+ g_free (unexpanded);
break;
+ }
case TERMINAL_TYPE_BLANK_NODE_LABEL:
add_start = 2;
break;
@@ -733,21 +822,34 @@ _convert_terminal (TrackerSparql *sparql)
{
const TrackerGrammarRule *rule;
TrackerBinding *binding;
+ gboolean is_parameter;
+ GHashTable *ht;
gchar *str;
str = _dup_last_string (sparql);
g_assert (str != NULL);
rule = tracker_parser_node_get_rule (sparql->current_state.prev_node);
+ is_parameter = tracker_grammar_rule_is_a (rule, RULE_TYPE_TERMINAL,
+ TERMINAL_TYPE_PARAMETERIZED_VAR);
+ ht = is_parameter ? sparql->parameters : sparql->cached_bindings;
+
+ binding = g_hash_table_lookup (ht, str);
+ if (binding)
+ return g_object_ref (binding);
- if (tracker_grammar_rule_is_a (rule, RULE_TYPE_TERMINAL, TERMINAL_TYPE_PARAMETERIZED_VAR)) {
+ if (is_parameter) {
binding = tracker_parameter_binding_new (str, NULL);
} else {
- binding = tracker_literal_binding_new (str, NULL);
+ GBytes *bytes;
+
+ bytes = g_bytes_new (str, strlen (str) + 1);
+ binding = tracker_literal_binding_new (bytes, NULL);
tracker_binding_set_data_type (binding, sparql->current_state.expression_type);
+ g_bytes_unref (bytes);
}
- g_free (str);
+ g_hash_table_insert (ht, str, g_object_ref (binding));
return binding;
}
@@ -834,7 +936,8 @@ _init_token (TrackerToken *token,
if (tracker_grammar_rule_is_a (rule, RULE_TYPE_TERMINAL, TERMINAL_TYPE_VAR1) ||
tracker_grammar_rule_is_a (rule, RULE_TYPE_TERMINAL, TERMINAL_TYPE_VAR2)) {
- if (sparql->current_state.type == TRACKER_SPARQL_TYPE_SELECT) {
+ if (sparql->current_state.type == TRACKER_SPARQL_TYPE_SELECT ||
+ sparql->current_state.type == TRACKER_SPARQL_TYPE_CONSTRUCT) {
var = _ensure_variable (sparql, str);
tracker_token_variable_init (token, var);
} else {
@@ -842,12 +945,12 @@ _init_token (TrackerToken *token,
value = g_hash_table_lookup (sparql->solution_var_map, str);
if (value)
- tracker_token_literal_init (token, value);
+ tracker_token_literal_init (token, value, -1);
}
} else if (tracker_grammar_rule_is_a (rule, RULE_TYPE_TERMINAL, TERMINAL_TYPE_PARAMETERIZED_VAR)) {
tracker_token_parameter_init (token, str);
} else {
- tracker_token_literal_init (token, str);
+ tracker_token_literal_init (token, str, -1);
}
g_free (str);
@@ -1013,6 +1116,37 @@ introspect_fts_snippet (TrackerSparql *sparql,
return TRUE;
}
+static TrackerVariable *
+reserve_subvariable (TrackerSparql *sparql,
+ TrackerVariable *var,
+ const gchar *suffix)
+{
+ TrackerVariable *subvar;
+ gchar *name;
+
+ name = g_strdup_printf ("%s:%s", var->name, suffix);
+ subvar = _ensure_variable (sparql, name);
+ g_free (name);
+
+ return subvar;
+}
+
+static TrackerVariable *
+lookup_subvariable (TrackerSparql *sparql,
+ TrackerVariable *var,
+ const gchar *suffix)
+{
+ TrackerVariable *subvar;
+ gchar *name;
+
+ name = g_strdup_printf ("%s:%s", var->name, suffix);
+ subvar = tracker_select_context_lookup_variable (TRACKER_SELECT_CONTEXT (sparql->context),
+ name);
+ g_free (name);
+
+ return subvar;
+}
+
static gboolean
_add_quad (TrackerSparql *sparql,
TrackerToken *graph,
@@ -1029,34 +1163,38 @@ _add_quad (TrackerSparql *sparql,
TrackerProperty *property = NULL;
TrackerClass *subject_type = NULL;
gboolean new_table = FALSE, is_fts = FALSE, is_rdf_type = FALSE;
+ const gchar *graph_db = NULL;
triple_context = TRACKER_TRIPLE_CONTEXT (sparql->current_state.context);
ontologies = tracker_data_manager_get_ontologies (sparql->data_manager);
+ if (tracker_token_get_literal (graph))
+ graph_db = tracker_token_get_idstring (graph);
+
if (tracker_token_get_literal (predicate)) {
gboolean share_table = TRUE;
const gchar *db_table;
property = tracker_ontologies_get_property_by_uri (ontologies,
- tracker_token_get_literal (predicate));
+ tracker_token_get_idstring(predicate));
- if (tracker_token_is_empty (graph) &&
- !tracker_token_get_variable (object) &&
- g_strcmp0 (tracker_token_get_literal (predicate), RDF_NS "type") == 0) {
+ if (tracker_token_get_literal (object) &&
+ g_strcmp0 (tracker_token_get_idstring (predicate), RDF_NS "type") == 0) {
/* rdf:type query */
subject_type = tracker_ontologies_get_class_by_uri (ontologies,
- tracker_token_get_literal (object));
+ tracker_token_get_idstring (object));
if (!subject_type) {
g_set_error (error, TRACKER_SPARQL_ERROR,
TRACKER_SPARQL_ERROR_UNKNOWN_CLASS,
"Unknown class '%s'",
- tracker_token_get_literal (object));
+ tracker_token_get_idstring (object));
return FALSE;
}
is_rdf_type = TRUE;
db_table = tracker_class_get_name (subject_type);
- } else if (g_strcmp0 (tracker_token_get_literal (predicate), FTS_NS "match") == 0) {
+ share_table = !tracker_token_is_empty (graph);
+ } else if (g_strcmp0 (tracker_token_get_idstring (predicate), FTS_NS "match") == 0) {
db_table = "fts5";
share_table = FALSE;
is_fts = TRUE;
@@ -1099,27 +1237,30 @@ _add_quad (TrackerSparql *sparql,
/* We can never share the table with multiple triples for
* multi value properties as a property may consist of multiple rows.
+ * We can't either do that for graph unrestricted queries
+ * since we need the self-joins to query across all graphs.
*/
- share_table = !tracker_property_get_multiple_values (property);
+ share_table = (!tracker_property_get_multiple_values (property) &&
+ !tracker_token_is_empty (graph));
subject_type = tracker_property_get_domain (property);
} else if (property == NULL) {
g_set_error (error, TRACKER_SPARQL_ERROR,
TRACKER_SPARQL_ERROR_UNKNOWN_PROPERTY,
"Unknown property '%s'",
- tracker_token_get_literal (predicate));
+ tracker_token_get_idstring (predicate));
return FALSE;
}
if (share_table) {
table = tracker_triple_context_lookup_table (triple_context,
- tracker_token_get_idstring (subject),
+ graph_db,
db_table);
}
if (!table) {
table = tracker_triple_context_add_table (triple_context,
- tracker_token_get_idstring (subject),
+ graph_db,
db_table);
new_table = TRUE;
}
@@ -1127,7 +1268,8 @@ _add_quad (TrackerSparql *sparql,
/* Variable in predicate */
variable = tracker_token_get_variable (predicate);
table = tracker_triple_context_add_table (triple_context,
- variable->name, variable->name);
+ graph_db,
+ variable->name);
tracker_data_table_set_predicate_variable (table, TRUE);
new_table = TRUE;
@@ -1137,11 +1279,47 @@ _add_quad (TrackerSparql *sparql,
tracker_binding_set_db_column_name (binding, "predicate");
_add_binding (sparql, binding);
g_object_unref (binding);
+
+ /* If object is variable, add another variable to hold its type */
+ if (tracker_token_get_variable (object)) {
+ TrackerVariable *type_var;
+
+ type_var = reserve_subvariable (sparql, tracker_token_get_variable (object), "type");
+
+ binding = tracker_variable_binding_new (type_var, NULL, table);
+ tracker_binding_set_db_column_name (binding, "object_type");
+ _add_binding (sparql, binding);
+ g_object_unref (binding);
+ }
} else if (tracker_token_get_path (predicate)) {
table = tracker_triple_context_add_table (triple_context,
- "value",
+ graph_db,
tracker_token_get_idstring (predicate));
+ tracker_data_table_set_predicate_path (table, TRUE);
new_table = TRUE;
+
+ /* If subject/object are variable, add variables to hold their type */
+ if (tracker_token_get_variable (subject)) {
+ TrackerVariable *type_var;
+
+ type_var = reserve_subvariable (sparql, tracker_token_get_variable (subject), "type");
+
+ binding = tracker_variable_binding_new (type_var, NULL, table);
+ tracker_binding_set_db_column_name (binding, "ID_type");
+ _add_binding (sparql, binding);
+ g_object_unref (binding);
+ }
+
+ if (tracker_token_get_variable (object)) {
+ TrackerVariable *type_var;
+
+ type_var = reserve_subvariable (sparql, tracker_token_get_variable (object), "type");
+
+ binding = tracker_variable_binding_new (type_var, NULL, table);
+ tracker_binding_set_db_column_name (binding, "value_type");
+ _add_binding (sparql, binding);
+ g_object_unref (binding);
+ }
} else {
/* The parser disallows parameter predicates */
g_assert_not_reached ();
@@ -1167,6 +1345,15 @@ _add_quad (TrackerSparql *sparql,
g_object_unref (binding);
}
+ if (tracker_token_get_variable (graph)) {
+ variable = tracker_token_get_variable (graph);
+ binding = tracker_variable_binding_new (variable, NULL, table);
+ tracker_binding_set_data_type (binding, TRACKER_PROPERTY_TYPE_RESOURCE);
+ tracker_binding_set_db_column_name (binding, "graph");
+ _add_binding (sparql, binding);
+ g_object_unref (binding);
+ }
+
if (is_rdf_type) {
/* The type binding is already implicit in the data table */
return TRUE;
@@ -1177,9 +1364,10 @@ _add_quad (TrackerSparql *sparql,
binding = tracker_variable_binding_new (variable,
property ? tracker_property_get_range (property) : NULL,
table);
+ if (!tracker_variable_has_bindings (variable))
+ tracker_variable_set_sample_binding (variable, TRACKER_VARIABLE_BINDING (binding));
if (tracker_token_get_variable (predicate)) {
- tracker_binding_set_data_type (binding, TRACKER_PROPERTY_TYPE_STRING);
tracker_binding_set_db_column_name (binding, "object");
tracker_variable_binding_set_nullable (TRACKER_VARIABLE_BINDING (binding), TRUE);
} else if (tracker_token_get_path (predicate)) {
@@ -1200,31 +1388,6 @@ _add_quad (TrackerSparql *sparql,
*/
tracker_variable_binding_set_nullable (TRACKER_VARIABLE_BINDING (binding), TRUE);
}
-
- if (tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME) {
- gchar *date_var, *sql_expression, *local_date, *local_time;
- TrackerBinding *local_time_binding;
-
- /* Merge localDate/localTime into $var:local */
- date_var = g_strdup_printf ("%s:local", variable->name);
- variable = _ensure_variable (sparql, date_var);
-
- local_date = tracker_binding_get_extra_sql_expression (binding, "localDate");
- local_time = tracker_binding_get_extra_sql_expression (binding, "localTime");
- sql_expression = g_strdup_printf ("((%s * 24 * 3600) + %s)",
- local_date, local_time);
-
- local_time_binding = tracker_variable_binding_new (variable, NULL, NULL);
- tracker_binding_set_sql_expression (local_time_binding,
- sql_expression);
- _add_binding (sparql, local_time_binding);
- g_object_unref (local_time_binding);
-
- g_free (sql_expression);
- g_free (local_date);
- g_free (local_time);
- g_free (date_var);
- }
}
_add_binding (sparql, binding);
@@ -1304,40 +1467,177 @@ _add_quad (TrackerSparql *sparql,
g_object_unref (binding);
}
- if (!tracker_token_is_empty (graph)) {
- if (tracker_token_get_variable (graph)) {
- variable = tracker_token_get_variable (graph);
- binding = tracker_variable_binding_new (variable, NULL, table);
- tracker_variable_binding_set_nullable (TRACKER_VARIABLE_BINDING (binding), TRUE);
- } else if (tracker_token_get_literal (graph)) {
- binding = tracker_literal_binding_new (tracker_token_get_literal (graph), table);
- } else if (tracker_token_get_parameter (graph)) {
- binding = tracker_parameter_binding_new (tracker_token_get_parameter (graph), table);
- } else {
- g_assert_not_reached ();
- }
+ return TRUE;
+}
- tracker_binding_set_data_type (binding, TRACKER_PROPERTY_TYPE_RESOURCE);
+static void
+add_construct_variable (TrackerSparql *sparql,
+ TrackerVariable *var)
+{
+ TrackerPropertyType prop_type;
+ TrackerStringBuilder *str, *old;
- if (tracker_token_get_variable (predicate) ||
- tracker_token_get_path (predicate)) {
- tracker_binding_set_db_column_name (binding, "graph");
- } else {
- gchar *column_name;
+ str = _append_placeholder (sparql);
+ old = tracker_sparql_swap_builder (sparql, str);
- g_assert (property != NULL);
- column_name = g_strdup_printf ("%s:graph", tracker_property_get_name (property));
- tracker_binding_set_db_column_name (binding, column_name);
- g_free (column_name);
+ _append_variable_sql (sparql, var);
+
+ prop_type = TRACKER_BINDING (tracker_variable_get_sample_binding (var))->data_type;
+ convert_expression_to_string (sparql, prop_type);
+
+ tracker_sparql_swap_builder (sparql, old);
+}
+
+static gboolean
+_construct_clause (TrackerSparql *sparql,
+ TrackerToken *graph,
+ TrackerToken *subject,
+ TrackerToken *predicate,
+ TrackerToken *object,
+ GError **error)
+{
+ gchar *construct_query;
+
+ if (!tracker_string_builder_is_empty (sparql->current_state.sql))
+ _append_string (sparql, "UNION ALL ");
+
+ _append_string (sparql, "SELECT ");
+
+ if (tracker_token_get_variable (subject) &&
+ tracker_variable_get_sample_binding (tracker_token_get_variable (subject)))
+ add_construct_variable (sparql, tracker_token_get_variable (subject));
+ else
+ _append_string_printf (sparql, "\"%s\" ", tracker_token_get_idstring (subject));
+
+ _append_string (sparql, "AS subject, ");
+
+ if (tracker_token_get_variable (predicate) &&
+ tracker_variable_get_sample_binding (tracker_token_get_variable (predicate)))
+ add_construct_variable (sparql, tracker_token_get_variable (predicate));
+ else
+ _append_string_printf (sparql, "\"%s\" ", tracker_token_get_idstring (predicate));
+
+ _append_string (sparql, "AS predicate, ");
+
+ if (tracker_token_get_variable (object) &&
+ tracker_variable_get_sample_binding (tracker_token_get_variable (object)))
+ add_construct_variable (sparql, tracker_token_get_variable (object));
+ else
+ _append_string_printf (sparql, "\"%s\" ", tracker_token_get_idstring (object));
+
+ _append_string (sparql, "AS object ");
+
+ if (tracker_token_get_variable (subject) ||
+ tracker_token_get_variable (predicate) ||
+ tracker_token_get_variable (object)) {
+ gboolean first = TRUE;
+ _append_string (sparql, "FROM (SELECT DISTINCT ");
+
+ if (tracker_token_get_variable (subject)) {
+ _append_variable_sql (sparql, tracker_token_get_variable (subject));
+ first = FALSE;
}
- _add_binding (sparql, binding);
- g_object_unref (binding);
+ if (tracker_token_get_variable (predicate)) {
+ if (!first)
+ _append_string (sparql, ", ");
+ _append_variable_sql (sparql, tracker_token_get_variable (predicate));
+ first = FALSE;
+ }
+
+ if (tracker_token_get_variable (object)) {
+ if (!first)
+ _append_string (sparql, ", ");
+ _append_variable_sql (sparql, tracker_token_get_variable (object));
+ }
+
+ _append_string (sparql, " FROM (");
+
+ construct_query = tracker_string_builder_to_string (sparql->current_state.construct_query);
+ _append_string_printf (sparql, "%s", construct_query);
+ g_free (construct_query);
+
+ _append_string (sparql, ")) ");
}
return TRUE;
}
+static gboolean
+tracker_sparql_apply_quad (TrackerSparql *sparql,
+ GError **error)
+{
+ GError *inner_error = NULL;
+
+ switch (sparql->current_state.type) {
+ case TRACKER_SPARQL_TYPE_SELECT:
+ _add_quad (sparql,
+ &sparql->current_state.graph,
+ &sparql->current_state.subject,
+ &sparql->current_state.predicate,
+ &sparql->current_state.object,
+ &inner_error);
+ break;
+ case TRACKER_SPARQL_TYPE_CONSTRUCT:
+ _construct_clause (sparql,
+ &sparql->current_state.graph,
+ &sparql->current_state.subject,
+ &sparql->current_state.predicate,
+ &sparql->current_state.object,
+ &inner_error);
+ break;
+ case TRACKER_SPARQL_TYPE_INSERT:
+ tracker_data_insert_statement (tracker_data_manager_get_data (sparql->data_manager),
+ tracker_token_get_idstring (&sparql->current_state.graph),
+ tracker_token_get_idstring (&sparql->current_state.subject),
+ tracker_token_get_idstring (&sparql->current_state.predicate),
+ tracker_token_get_literal (&sparql->current_state.object),
+ &inner_error);
+ break;
+ case TRACKER_SPARQL_TYPE_DELETE:
+ tracker_data_delete_statement (tracker_data_manager_get_data (sparql->data_manager),
+ tracker_token_get_idstring (&sparql->current_state.graph),
+ tracker_token_get_idstring (&sparql->current_state.subject),
+ tracker_token_get_idstring (&sparql->current_state.predicate),
+ tracker_token_get_literal (&sparql->current_state.object),
+ &inner_error);
+ break;
+ case TRACKER_SPARQL_TYPE_UPDATE:
+ tracker_data_update_statement (tracker_data_manager_get_data (sparql->data_manager),
+ tracker_token_get_idstring (&sparql->current_state.graph),
+ tracker_token_get_idstring (&sparql->current_state.subject),
+ tracker_token_get_idstring (&sparql->current_state.predicate),
+ tracker_token_get_literal (&sparql->current_state.object),
+ &inner_error);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+tracker_sparql_init_string_builder (TrackerSparql *sparql)
+{
+ TrackerStringBuilder *str;
+
+ g_clear_pointer (&sparql->sql, tracker_string_builder_free);
+ sparql->sql = sparql->current_state.sql = tracker_string_builder_new ();
+ sparql->current_state.with_clauses = _prepend_placeholder (sparql);
+
+ /* Ensure the select clause goes to a different substring than the
+ * WITH clauses, so _prepend_string() works as expected.
+ */
+ str = _append_placeholder (sparql);
+ tracker_sparql_swap_builder (sparql, str);
+}
+
static TrackerParserNode *
_skip_rule (TrackerSparql *sparql,
guint named_rule)
@@ -1362,6 +1662,40 @@ _skip_rule (TrackerSparql *sparql,
return current;
}
+static TrackerPropertyType
+rdf_type_to_property_type (const gchar *type)
+{
+ if (g_str_equal (type, XSD_NS "boolean")) {
+ return TRACKER_PROPERTY_TYPE_BOOLEAN;
+ } else if (g_str_equal (type, XSD_NS "integer") ||
+ g_str_equal (type, XSD_NS "nonPositiveInteger") ||
+ g_str_equal (type, XSD_NS "negativeInteger") ||
+ g_str_equal (type, XSD_NS "long") ||
+ g_str_equal (type, XSD_NS "int") ||
+ g_str_equal (type, XSD_NS "short") ||
+ g_str_equal (type, XSD_NS "byte") ||
+ g_str_equal (type, XSD_NS "nonNegativeInteger") ||
+ g_str_equal (type, XSD_NS "unsignedLong") ||
+ g_str_equal (type, XSD_NS "unsignedInt") ||
+ g_str_equal (type, XSD_NS "unsignedShort") ||
+ g_str_equal (type, XSD_NS "unsignedByte") ||
+ g_str_equal (type, XSD_NS "positiveInteger")) {
+ return TRACKER_PROPERTY_TYPE_INTEGER;
+ } else if (g_str_equal (type, XSD_NS "double")) {
+ return TRACKER_PROPERTY_TYPE_DOUBLE;
+ } else if (g_str_equal (type, XSD_NS "date")) {
+ return TRACKER_PROPERTY_TYPE_DATE;
+ } else if (g_str_equal (type, XSD_NS "dateTime")) {
+ return TRACKER_PROPERTY_TYPE_DATETIME;
+ } else if (g_str_equal (type, XSD_NS "string")) {
+ return TRACKER_PROPERTY_TYPE_STRING;
+ } else if (g_str_equal (type, RDF_NS "langString")) {
+ return TRACKER_PROPERTY_TYPE_LANGSTRING;
+ } else {
+ return TRACKER_PROPERTY_TYPE_UNKNOWN;
+ }
+}
+
static void
convert_expression_to_string (TrackerSparql *sparql,
TrackerPropertyType type)
@@ -1386,13 +1720,15 @@ convert_expression_to_string (TrackerSparql *sparql,
break;
case TRACKER_PROPERTY_TYPE_DATE:
/* ISO 8601 format */
- _prepend_string (sparql, "strftime (\"%Y-%m-%d\", ");
- _append_string (sparql, ", \"unixepoch\") ");
+ _prepend_string (sparql, "strftime (\"%Y-%m-%d\", SparqlTimestamp (");
+ _append_string (sparql, "), \"unixepoch\") ");
break;
case TRACKER_PROPERTY_TYPE_DATETIME:
/* ISO 8601 format */
_prepend_string (sparql, "SparqlFormatTime (");
_append_string (sparql, ") ");
+ break;
+ case TRACKER_PROPERTY_TYPE_LANGSTRING:
default:
/* Let sqlite convert the expression to string */
_prepend_string (sparql, "CAST (");
@@ -1401,6 +1737,34 @@ convert_expression_to_string (TrackerSparql *sparql,
}
}
+static void
+_append_graph_checks (TrackerSparql *sparql,
+ const gchar *column_name,
+ GStrv graphs,
+ gint len)
+{
+ gint i;
+
+ _append_string_printf (sparql,
+ "WHERE (SELECT Uri FROM Resource WHERE ID = %s) ",
+ column_name);
+
+ if (len == 1)
+ _append_string (sparql, "= ");
+ else
+ _append_string (sparql, "IN (");
+
+ for (i = 0; i < len; i++) {
+ _append_string_printf (sparql,
+ "%c \"%s\" ",
+ i == 0 ? ' ' : ',',
+ graphs[i]);
+ }
+
+ if (len > 1)
+ _append_string (sparql, ")");
+}
+
static TrackerContext *
_begin_triples_block (TrackerSparql *sparql)
{
@@ -1473,9 +1837,64 @@ _end_triples_block (TrackerSparql *sparql,
if (table->predicate_variable) {
_append_string (sparql,
"(SELECT subject AS ID, predicate, "
- "object, graph FROM tracker_triples) ");
+ "object, object_type, graph FROM tracker_triples ");
+ g_clear_pointer (&sparql->union_views, g_hash_table_unref);
+
+ if (table->graph) {
+ _append_graph_checks (sparql, "graph",
+ &table->graph, 1);
+ } else if (sparql->anon_graphs->len > 0 &&
+ tracker_token_is_empty (&sparql->current_state.graph)) {
+ _append_graph_checks (sparql, "graph",
+ (GStrv) sparql->anon_graphs->pdata,
+ sparql->anon_graphs->len);
+ } else if (tracker_token_get_variable (&sparql->current_state.graph)) {
+ if (sparql->named_graphs->len > 0) {
+ _append_graph_checks (sparql, "graph",
+ (GStrv) sparql->named_graphs->pdata,
+ sparql->named_graphs->len);
+ } else {
+ _append_string (sparql, "WHERE \"graph\" != 0 ");
+ }
+ }
+
+ _append_string (sparql, ") ");
+ } else if (table->predicate_path) {
+ _append_string_printf (sparql, "\"%s\"", table->sql_db_tablename);
} else {
- _append_string_printf (sparql, "\"%s\" ", table->sql_db_tablename);
+ if (table->graph &&
+ tracker_data_manager_find_graph (sparql->data_manager, table->graph)) {
+ _append_string_printf (sparql, "\"%s\".\"%s\" ",
+ table->graph,
+ table->sql_db_tablename);
+ } else {
+ _append_string_printf (sparql,
+ "(SELECT * FROM \"unionGraph_%s\" ",
+ table->sql_db_tablename);
+
+ if (table->graph) {
+ _append_graph_checks (sparql, "graph",
+ &table->graph, 1);
+ } else if (sparql->anon_graphs->len > 0 &&
+ tracker_token_is_empty (&sparql->current_state.graph)) {
+ _append_graph_checks (sparql, "graph",
+ (GStrv) sparql->anon_graphs->pdata,
+ sparql->anon_graphs->len);
+ } else if (tracker_token_get_variable (&sparql->current_state.graph)) {
+ if (sparql->named_graphs->len > 0) {
+ _append_graph_checks (sparql, "graph",
+ (GStrv) sparql->named_graphs->pdata,
+ sparql->named_graphs->len);
+ } else {
+ _append_string (sparql, "WHERE \"graph\" != 0 ");
+ }
+ }
+
+ _append_string (sparql, ") ");
+
+ if (sparql->union_views)
+ g_hash_table_add (sparql->union_views, g_strdup (table->sql_db_tablename));
+ }
}
_append_string_printf (sparql, "AS \"%s\" ", table->sql_query_tablename);
@@ -1562,8 +1981,14 @@ _end_triples_block (TrackerSparql *sparql,
first = FALSE;
binding = g_ptr_array_index (triple_context->literal_bindings, i);
- _append_string_printf (sparql, "%s = ", tracker_binding_get_sql_expression (binding));
- _append_literal_sql (sparql, TRACKER_LITERAL_BINDING (binding));
+ if (binding->data_type == TRACKER_PROPERTY_TYPE_DATETIME) {
+ _append_string_printf (sparql, "SparqlTimeSort (%s) = SparqlTimeSort (", tracker_binding_get_sql_expression (binding));
+ _append_literal_sql (sparql, TRACKER_LITERAL_BINDING (binding));
+ _append_string (sparql, ") ");
+ } else {
+ _append_string_printf (sparql, "%s = ", tracker_binding_get_sql_expression (binding));
+ _append_literal_sql (sparql, TRACKER_LITERAL_BINDING (binding));
+ }
}
/* If we had any where clauses, prepend the 'WHERE' literal */
@@ -1581,6 +2006,10 @@ translate_Query (TrackerSparql *sparql,
{
TrackerGrammarNamedRule rule;
+ sparql->context = g_object_ref_sink (tracker_select_context_new ());
+ sparql->current_state.select_context = sparql->context;
+ tracker_sparql_push_context (sparql, sparql->context);
+
/* Query ::= Prologue
* ( SelectQuery | ConstructQuery | DescribeQuery | AskQuery )
* ValuesClause
@@ -1602,6 +2031,8 @@ translate_Query (TrackerSparql *sparql,
_call_rule (sparql, NAMED_RULE_ValuesClause, error);
+ tracker_sparql_pop_context (sparql, FALSE);
+
return TRUE;
}
@@ -1695,9 +2126,20 @@ translate_SelectClause (TrackerSparql *sparql,
TrackerVariable *var;
GHashTableIter iter;
+ if (!select_context->variables) {
+ g_set_error (error, TRACKER_SPARQL_ERROR,
+ TRACKER_SPARQL_ERROR_TYPE,
+ "Glob used but no variables defined");
+ return FALSE;
+ }
+
g_hash_table_iter_init (&iter, select_context->variables);
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &var)) {
+ /* Skip our own internal variables */
+ if (strchr (var->name, ':'))
+ continue;
+
if (!first)
_append_string (sparql, ", ");
@@ -1816,11 +2258,15 @@ translate_BaseDecl (TrackerSparql *sparql,
/* BaseDecl ::= 'BASE' IRIREF
*/
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_BASE);
-
- /* FIXME: BASE is unimplemented, and we never raised an error */
-
_expect (sparql, RULE_TYPE_TERMINAL, TERMINAL_TYPE_IRIREF);
+ /* Sparql syntax allows for multiple BaseDecl, but it only makes
+ * sense to keep one. Given that the sparql1.1-query recommendation
+ * does not define the behavior, just pick the first one.
+ */
+ if (!sparql->base)
+ sparql->base = _dup_last_string (sparql);
+
return TRUE;
}
@@ -1898,37 +2344,51 @@ translate_SelectQuery (TrackerSparql *sparql,
GError **error)
{
TrackerParserNode *select_clause;
- TrackerStringBuilder *str;
+ TrackerStringBuilder *select, *str, *old;
/* SelectQuery ::= SelectClause DatasetClause* WhereClause SolutionModifier
*/
- sparql->context = g_object_ref_sink (tracker_select_context_new ());
- sparql->current_state.select_context = sparql->context;
- tracker_sparql_push_context (sparql, sparql->context);
/* Skip select clause here */
+ select = _append_placeholder (sparql);
str = _append_placeholder (sparql);
+ old = tracker_sparql_swap_builder (sparql, str);
select_clause = _skip_rule (sparql, NAMED_RULE_SelectClause);
while (_check_in_rule (sparql, NAMED_RULE_DatasetClause)) {
_call_rule (sparql, NAMED_RULE_DatasetClause, error);
}
+ /* Optimize single graph case, so it always resorts to querying
+ * the specific database.
+ */
+ if (sparql->named_graphs->len + sparql->anon_graphs->len == 1) {
+ const gchar *graph = NULL;
+
+ if (sparql->named_graphs->len > 0)
+ graph = g_ptr_array_index (sparql->named_graphs, 0);
+ else if (sparql->anon_graphs->len > 0)
+ graph = g_ptr_array_index (sparql->anon_graphs, 0);
+
+ if (graph)
+ tracker_token_literal_init (&sparql->current_state.graph, graph, -1);
+ }
+
_call_rule (sparql, NAMED_RULE_WhereClause, error);
if (!_check_undefined_variables (sparql, TRACKER_SELECT_CONTEXT (sparql->context), error))
return FALSE;
+ _call_rule (sparql, NAMED_RULE_SolutionModifier, error);
+
+ tracker_sparql_swap_builder (sparql, old);
+
/* Now that we have all variable/binding information available,
* process the select clause.
*/
- if (!_postprocess_rule (sparql, select_clause, str, error))
+ if (!_postprocess_rule (sparql, select_clause, select, error))
return FALSE;
- _call_rule (sparql, NAMED_RULE_SolutionModifier, error);
-
- tracker_sparql_pop_context (sparql, FALSE);
-
return TRUE;
}
@@ -1937,7 +2397,7 @@ translate_SubSelect (TrackerSparql *sparql,
GError **error)
{
TrackerContext *context, *prev;
- TrackerStringBuilder *str;
+ TrackerStringBuilder *select, *str, *old;
TrackerParserNode *select_clause;
/* SubSelect ::= SelectClause WhereClause SolutionModifier ValuesClause
@@ -1948,20 +2408,25 @@ translate_SubSelect (TrackerSparql *sparql,
tracker_sparql_push_context (sparql, context);
/* Skip select clause here */
+ select = _append_placeholder (sparql);
str = _append_placeholder (sparql);
+ old = tracker_sparql_swap_builder (sparql, str);
+
select_clause = _skip_rule (sparql, NAMED_RULE_SelectClause);
_call_rule (sparql, NAMED_RULE_WhereClause, error);
+ _call_rule (sparql, NAMED_RULE_SolutionModifier, error);
+ _call_rule (sparql, NAMED_RULE_ValuesClause, error);
+
+ tracker_sparql_swap_builder (sparql, old);
+
/* Now that we have all variable/binding information available,
* process the select clause.
*/
- if (!_postprocess_rule (sparql, select_clause, str, error))
+ if (!_postprocess_rule (sparql, select_clause, select, error))
return FALSE;
- _call_rule (sparql, NAMED_RULE_SolutionModifier, error);
- _call_rule (sparql, NAMED_RULE_ValuesClause, error);
-
sparql->current_state.expression_type = TRACKER_SELECT_CONTEXT (context)->type;
tracker_sparql_pop_context (sparql, FALSE);
sparql->current_state.select_context = prev;
@@ -1973,38 +2438,216 @@ static gboolean
translate_ConstructQuery (TrackerSparql *sparql,
GError **error)
{
- _unimplemented ("CONSTRUCT");
+ TrackerParserNode *node = NULL;
+ TrackerStringBuilder *old;
+
+ /* ConstructQuery ::= 'CONSTRUCT' ( ConstructTemplate DatasetClause* WhereClause SolutionModifier |
+ * DatasetClause* 'WHERE' '{' TriplesTemplate? '}' SolutionModifier )
+ */
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CONSTRUCT);
+ sparql->current_state.construct_query = tracker_string_builder_new ();
+
+ if (_current_rule (sparql) == NAMED_RULE_ConstructTemplate) {
+ node = _skip_rule (sparql, NAMED_RULE_ConstructTemplate);
+
+ old = tracker_sparql_swap_builder (sparql, sparql->current_state.construct_query);
+
+ _append_string (sparql, "SELECT * ");
+
+ while (_current_rule (sparql) == NAMED_RULE_DatasetClause)
+ _call_rule (sparql, NAMED_RULE_DatasetClause, error);
+
+ _call_rule (sparql, NAMED_RULE_WhereClause, error);
+ _call_rule (sparql, NAMED_RULE_SolutionModifier, error);
+ tracker_sparql_swap_builder (sparql, old);
+
+ sparql->current_state.type = TRACKER_SPARQL_TYPE_CONSTRUCT;
+ if (!_postprocess_rule (sparql, node, NULL, error))
+ return FALSE;
+ } else {
+ while (_current_rule (sparql) == NAMED_RULE_DatasetClause)
+ _call_rule (sparql, NAMED_RULE_DatasetClause, error);
+
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_WHERE);
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_BRACE);
+
+ if (_current_rule (sparql) == NAMED_RULE_TriplesTemplate) {
+ node = _skip_rule (sparql, NAMED_RULE_TriplesTemplate);
+
+ old = tracker_sparql_swap_builder (sparql, sparql->current_state.construct_query);
+
+ _begin_triples_block (sparql);
+ if (!_postprocess_rule (sparql, node, NULL, error))
+ return FALSE;
+ if (!_end_triples_block (sparql, error))
+ return FALSE;
+
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_BRACE);
+ _call_rule (sparql, NAMED_RULE_SolutionModifier, error);
+ tracker_sparql_swap_builder (sparql, old);
+
+ /* Switch to construct mode, and rerun again the triples template */
+ sparql->current_state.type = TRACKER_SPARQL_TYPE_CONSTRUCT;
+ if (!_postprocess_rule (sparql, node, NULL, error))
+ return FALSE;
+ } else {
+ _append_string (sparql, "SELECT NULL ");
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_BRACE);
+ _call_rule (sparql, NAMED_RULE_SolutionModifier, error);
+ }
+ }
+
+ return TRUE;
}
static gboolean
translate_DescribeQuery (TrackerSparql *sparql,
GError **error)
{
- _unimplemented ("DESCRIBE");
+ TrackerStringBuilder *where_str = NULL;
+ TrackerVariable *variable;
+ TrackerBinding *binding;
+ GList *resources = NULL, *l;
+ gboolean glob = FALSE;
+
+ /* DescribeQuery ::= 'DESCRIBE' ( VarOrIri+ | '*' ) DatasetClause* WhereClause? SolutionModifier
+ */
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_DESCRIBE);
+ _append_string (sparql,
+ "SELECT "
+ " (SELECT Uri FROM Resource WHERE ID = subject),"
+ " (SELECT Uri FROM Resource WHERE ID = predicate),"
+ " object "
+ "FROM tracker_triples "
+ "WHERE object IS NOT NULL AND subject IN (");
+
+ g_clear_pointer (&sparql->union_views, g_hash_table_unref);
+
+ if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_GLOB)) {
+ glob = TRUE;
+ } else {
+ TrackerContext *context;
+
+ context = tracker_triple_context_new ();
+ tracker_sparql_push_context (sparql, context);
+
+ while (_check_in_rule (sparql, NAMED_RULE_VarOrIri)) {
+ TrackerBinding *binding;
+ TrackerToken resource;
+
+ _call_rule (sparql, NAMED_RULE_VarOrIri, error);
+ _init_token (&resource, sparql->current_state.prev_node, sparql);
+
+ if (tracker_token_get_literal (&resource)) {
+ binding = tracker_literal_binding_new (tracker_token_get_literal (&resource),
+ NULL);
+ } else {
+ TrackerVariable *variable;
+
+ variable = tracker_token_get_variable (&resource);
+ binding = tracker_variable_binding_new (variable, NULL, NULL);
+ }
+
+ tracker_binding_set_data_type (binding, TRACKER_PROPERTY_TYPE_RESOURCE);
+ resources = g_list_prepend (resources, binding);
+ tracker_token_unset (&resource);
+ }
+
+ tracker_sparql_pop_context (sparql, FALSE);
+ }
+
+ while (_check_in_rule (sparql, NAMED_RULE_DatasetClause))
+ _call_rule (sparql, NAMED_RULE_DatasetClause, error);
+
+ if (_check_in_rule (sparql, NAMED_RULE_WhereClause)) {
+ TrackerParserNode *where_clause;
+
+ where_str = tracker_string_builder_new ();
+ where_clause = _skip_rule (sparql, NAMED_RULE_WhereClause);
+
+ if (!_postprocess_rule (sparql, where_clause, where_str, error)) {
+ g_list_free_full (resources, g_object_unref);
+ tracker_string_builder_free (where_str);
+ return FALSE;
+ }
+ }
+
+ if (glob && TRACKER_SELECT_CONTEXT (sparql->context)->variables) {
+ GHashTableIter iter;
+
+ g_hash_table_iter_init (&iter, TRACKER_SELECT_CONTEXT (sparql->context)->variables);
+
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &variable)) {
+ binding = tracker_variable_binding_new (variable, NULL, NULL);
+ resources = g_list_prepend (resources, binding);
+ }
+ }
+
+ if (resources == NULL) {
+ g_clear_pointer (&where_str, tracker_string_builder_free);
+ _raise (PARSE, "Use of unprojected variables", "DescribeQuery");
+ }
+
+ for (l = resources; l; l = l->next) {
+ binding = l->data;
+
+ if (l != resources)
+ _append_string (sparql, "UNION ALL ");
+
+ if (TRACKER_IS_LITERAL_BINDING (binding)) {
+ tracker_select_context_add_literal_binding (TRACKER_SELECT_CONTEXT (sparql->context),
+ TRACKER_LITERAL_BINDING (binding));
+ _append_string (sparql, "SELECT ");
+ _append_literal_sql (sparql, TRACKER_LITERAL_BINDING (binding));
+ } else if (TRACKER_IS_VARIABLE_BINDING (binding)) {
+ gchar *str;
+
+ variable = TRACKER_VARIABLE_BINDING (binding)->variable;
+
+ if (!where_str) {
+ g_list_free_full (resources, g_object_unref);
+ _raise (PARSE, "Use of unprojected variable in Describe", variable->name);
+ }
+
+ _append_string (sparql, "SELECT ");
+ _append_variable_sql (sparql, variable);
+
+ str = tracker_string_builder_to_string (where_str);
+ _append_string_printf (sparql, "%s", str);
+ g_free (str);
+ }
+ }
+
+ _call_rule (sparql, NAMED_RULE_SolutionModifier, error);
+ _append_string (sparql, ") ");
+ g_list_free_full (resources, g_object_unref);
+ g_clear_pointer (&where_str, tracker_string_builder_free);
+
+ return TRUE;
}
static gboolean
translate_AskQuery (TrackerSparql *sparql,
GError **error)
{
+ TrackerStringBuilder *str, *old;
+
/* AskQuery ::= 'ASK' DatasetClause* WhereClause SolutionModifier
*/
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_ASK);
- sparql->context = g_object_ref_sink (tracker_select_context_new ());
- sparql->current_state.select_context = sparql->context;
- tracker_sparql_push_context (sparql, sparql->context);
-
_append_string (sparql, "SELECT CASE EXISTS (SELECT 1 ");
while (_check_in_rule (sparql, NAMED_RULE_DatasetClause)) {
_call_rule (sparql, NAMED_RULE_DatasetClause, error);
}
+ str = _append_placeholder (sparql);
+ old = tracker_sparql_swap_builder (sparql, str);
_call_rule (sparql, NAMED_RULE_WhereClause, error);
_call_rule (sparql, NAMED_RULE_SolutionModifier, error);
- tracker_sparql_pop_context (sparql, FALSE);
+ tracker_sparql_swap_builder (sparql, old);
_append_string (sparql, ") WHEN 1 THEN 'true' WHEN 0 THEN 'false' ELSE NULL END");
@@ -2039,11 +2682,15 @@ static gboolean
translate_DefaultGraphClause (TrackerSparql *sparql,
GError **error)
{
+ gchar *graph;
+
/* DefaultGraphClause ::= SourceSelector
*/
_call_rule (sparql, NAMED_RULE_SourceSelector, error);
- /* FIXME: FROM <graph> is unimplemented, and we never raised an error */
+ graph = g_strdup (tracker_token_get_idstring (&sparql->current_state.graph));
+ g_ptr_array_add (sparql->anon_graphs, graph);
+ tracker_token_unset (&sparql->current_state.graph);
return TRUE;
}
@@ -2052,12 +2699,16 @@ static gboolean
translate_NamedGraphClause (TrackerSparql *sparql,
GError **error)
{
+ gchar *graph;
+
/* NamedGraphClause ::= 'NAMED' SourceSelector
*/
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_NAMED);
_call_rule (sparql, NAMED_RULE_SourceSelector, error);
- /* FIXME: FROM NAMED <graph> is unimplemented, and we never raised an error */
+ graph = g_strdup (tracker_token_get_idstring (&sparql->current_state.graph));
+ g_ptr_array_add (sparql->named_graphs, graph);
+ tracker_token_unset (&sparql->current_state.graph);
return TRUE;
}
@@ -2069,6 +2720,8 @@ translate_SourceSelector (TrackerSparql *sparql,
/* SourceSelector ::= iri
*/
_call_rule (sparql, NAMED_RULE_iri, error);
+ _init_token (&sparql->current_state.graph,
+ sparql->current_state.prev_node, sparql);
return TRUE;
}
@@ -2124,16 +2777,87 @@ static gboolean
translate_GroupClause (TrackerSparql *sparql,
GError **error)
{
+ GList *conditions = NULL, *expressions = NULL, *l;
+ gboolean variables_projected = FALSE;
+ TrackerStringBuilder *select, *old;
+ gchar *str;
+
/* GroupClause ::= 'GROUP' 'BY' GroupCondition+
*/
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_GROUP);
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_BY);
- _append_string (sparql, "GROUP BY ");
+ /* As we still may get variables projected in the GroupCondition
+ * clauses, we need to preprocess those so we can do a final
+ * SELECT *, $variables FROM (...) surrounding the resulting
+ * query.
+ */
while (_check_in_rule (sparql, NAMED_RULE_GroupCondition)) {
- _call_rule (sparql, NAMED_RULE_GroupCondition, error);
+ TrackerParserNode *node;
+
+ node = _skip_rule (sparql, NAMED_RULE_GroupCondition);
+ conditions = g_list_prepend (conditions, node);
}
+ for (l = conditions; l; l = l->next) {
+ TrackerStringBuilder *expr;
+
+ expr = tracker_string_builder_new ();
+
+ if (!_postprocess_rule (sparql, l->data, expr, error)) {
+ g_object_unref (expr);
+ g_list_free_full (expressions, g_object_unref);
+ g_list_free (conditions);
+ return FALSE;
+ }
+
+ if (sparql->current_state.as_in_group_by) {
+ TrackerVariableBinding *binding = sparql->current_state.as_in_group_by;
+ TrackerVariable *var = tracker_variable_binding_get_variable (binding);
+
+ if (!variables_projected) {
+ select = _prepend_placeholder (sparql);
+ old = tracker_sparql_swap_builder (sparql, select);
+ variables_projected = TRUE;
+ _append_string (sparql, "FROM (SELECT * ");
+ }
+
+ _append_string (sparql, ", ");
+
+ str = tracker_string_builder_to_string (expr);
+ tracker_string_builder_append (select, str, -1);
+ g_free (str);
+
+ _append_string (sparql, "AS ");
+ _append_variable_sql (sparql, var);
+ expressions = g_list_prepend (expressions,
+ g_strdup (tracker_variable_get_sql_expression (var)));
+ g_clear_object (&sparql->current_state.as_in_group_by);
+ } else {
+ str = tracker_string_builder_to_string (expr);
+ expressions = g_list_prepend (expressions, str);
+ }
+
+ tracker_string_builder_free (expr);
+ }
+
+ if (variables_projected) {
+ tracker_sparql_swap_builder (sparql, old);
+ _append_string (sparql, ") ");
+ }
+
+ _append_string (sparql, "GROUP BY ");
+
+ for (l = expressions; l; l = l->next) {
+ if (l != expressions)
+ _append_string (sparql, ", ");
+
+ _append_string_printf (sparql, "%s ", l->data);
+ }
+
+ g_list_free_full (expressions, g_free);
+ g_list_free (conditions);
+
return TRUE;
}
@@ -2143,11 +2867,25 @@ translate_GroupCondition (TrackerSparql *sparql,
{
/* GroupCondition ::= BuiltInCall | FunctionCall | '(' Expression ( 'AS' Var )? ')' | Var
*/
+ sparql->current_state.as_in_group_by = NULL;
+
if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS)) {
+ TrackerPropertyType expr_type;
_call_rule (sparql, NAMED_RULE_Expression, error);
+ expr_type = sparql->current_state.expression_type;
if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_AS)) {
- _unimplemented ("AS in GROUP BY");
+ TrackerVariable *var;
+ TrackerBinding *binding;
+
+ _call_rule (sparql, NAMED_RULE_Var, error);
+ var = _last_node_variable (sparql);
+
+ binding = tracker_variable_binding_new (var, NULL, NULL);
+ tracker_binding_set_data_type (binding, expr_type);
+ tracker_variable_set_sample_binding (var, TRACKER_VARIABLE_BINDING (binding));
+
+ sparql->current_state.as_in_group_by = TRACKER_VARIABLE_BINDING (binding);
}
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
@@ -2264,7 +3002,8 @@ translate_OrderCondition (TrackerSparql *sparql,
g_assert_not_reached ();
}
- if (sparql->current_state.expression_type == TRACKER_PROPERTY_TYPE_STRING)
+ if (sparql->current_state.expression_type == TRACKER_PROPERTY_TYPE_STRING ||
+ sparql->current_state.expression_type == TRACKER_PROPERTY_TYPE_LANGSTRING)
_append_string (sparql, "COLLATE " TRACKER_COLLATION_NAME " ");
else if (sparql->current_state.expression_type == TRACKER_PROPERTY_TYPE_RESOURCE)
convert_expression_to_string (sparql, sparql->current_state.expression_type);
@@ -2357,7 +3096,10 @@ translate_ValuesClause (TrackerSparql *sparql,
/* ValuesClause ::= ( 'VALUES' DataBlock )?
*/
if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_VALUES)) {
- _unimplemented ("VALUES");
+ _prepend_string (sparql, "SELECT * FROM (");
+ _append_string (sparql, ") NATURAL INNER JOIN (");
+ _call_rule (sparql, NAMED_RULE_DataBlock, error);
+ _append_string (sparql, ") ");
}
return TRUE;
@@ -2406,49 +3148,405 @@ static gboolean
translate_Load (TrackerSparql *sparql,
GError **error)
{
- _unimplemented ("LOAD");
+ TrackerToken resource;
+ gboolean silent = FALSE;
+ GError *inner_error = NULL;
+ const gchar *graph = NULL;
+ GFile *file;
+
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_LOAD);
+
+ if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_SILENT))
+ silent = TRUE;
+
+ _call_rule (sparql, NAMED_RULE_iri, error);
+ _init_token (&resource, sparql->current_state.prev_node, sparql);
+
+ if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_INTO)) {
+ _call_rule (sparql, NAMED_RULE_GraphRefAll, error);
+
+ if (!tracker_token_is_empty (&sparql->current_state.graph))
+ graph = tracker_token_get_idstring (&sparql->current_state.graph);
+ }
+
+ file = g_file_new_for_uri (tracker_token_get_idstring (&resource));
+ tracker_token_unset (&resource);
+
+ tracker_data_load_turtle_file (tracker_data_manager_get_data (sparql->data_manager),
+ file, graph, &inner_error);
+
+ if (inner_error) {
+ g_clear_object (&file);
+
+ if (!silent) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+handle_silent (gboolean silent,
+ GError *error_in,
+ GError **error)
+{
+ if (!error_in) {
+ return TRUE;
+ } else if (silent) {
+ g_error_free (error_in);
+ return TRUE;
+ } else {
+ g_propagate_error (error, error_in);
+ return FALSE;
+ }
}
static gboolean
translate_Clear (TrackerSparql *sparql,
GError **error)
{
- _unimplemented ("CLEAR");
+ gboolean silent = FALSE;
+ GError *inner_error = NULL;
+ GList *graphs = NULL, *l;
+ const gchar *graph;
+
+ /* Clear ::= 'CLEAR' 'SILENT'? GraphRefAll
+ */
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLEAR);
+
+ if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_SILENT))
+ silent = TRUE;
+
+ _call_rule (sparql, NAMED_RULE_GraphRefAll, error);
+
+ if (tracker_token_is_empty (&sparql->current_state.graph)) {
+ if (sparql->current_state.graph_op == GRAPH_OP_DEFAULT ||
+ sparql->current_state.graph_op == GRAPH_OP_ALL) {
+ graphs = g_list_prepend (graphs, "main");
+ }
+
+ if (sparql->current_state.graph_op == GRAPH_OP_ALL ||
+ sparql->current_state.graph_op == GRAPH_OP_NAMED) {
+ GHashTable *ht;
+ GHashTableIter iter;
+
+ ht = tracker_data_manager_get_graphs (sparql->data_manager);
+ g_hash_table_iter_init (&iter, ht);
+
+ while (g_hash_table_iter_next (&iter, (gpointer *) &graph, NULL))
+ graphs = g_list_prepend (graphs, (gpointer) graph);
+ }
+ } else {
+ graph = tracker_token_get_idstring (&sparql->current_state.graph);
+
+ if (tracker_data_manager_find_graph (sparql->data_manager, graph) == 0)
+ _raise (UNKNOWN_GRAPH, "Unknown graph", graph);
+
+ graphs = g_list_prepend (graphs, (gpointer) graph);
+ }
+
+ for (l = graphs; l; l = l->next) {
+ if (!tracker_data_manager_clear_graph (sparql->data_manager,
+ l->data, &inner_error))
+ break;
+ }
+
+ g_list_free (graphs);
+
+ return handle_silent (silent, inner_error, error);
}
static gboolean
translate_Drop (TrackerSparql *sparql,
GError **error)
{
- _unimplemented ("DROP");
+ gboolean silent = FALSE;
+ GError *inner_error = NULL;
+ GList *graphs = NULL, *l;
+ const gchar *graph;
+
+ /* Drop ::= 'DROP' 'SILENT'? GraphRefAll
+ */
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_DROP);
+
+ if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_SILENT))
+ silent = TRUE;
+
+ _call_rule (sparql, NAMED_RULE_GraphRefAll, error);
+
+ if (tracker_token_is_empty (&sparql->current_state.graph)) {
+ if (sparql->current_state.graph_op == GRAPH_OP_DEFAULT ||
+ sparql->current_state.graph_op == GRAPH_OP_ALL) {
+ graphs = g_list_prepend (graphs, NULL);
+ }
+
+ if (sparql->current_state.graph_op == GRAPH_OP_ALL ||
+ sparql->current_state.graph_op == GRAPH_OP_NAMED) {
+ GHashTable *ht;
+ GHashTableIter iter;
+
+ ht = tracker_data_manager_get_graphs (sparql->data_manager);
+ g_hash_table_iter_init (&iter, ht);
+
+ while (g_hash_table_iter_next (&iter, (gpointer *) &graph, NULL))
+ graphs = g_list_prepend (graphs, (gpointer) graph);
+ }
+ } else {
+ graph = tracker_token_get_idstring (&sparql->current_state.graph);
+ graphs = g_list_prepend (graphs, (gpointer) graph);
+ }
+
+ for (l = graphs; l; l = l->next) {
+ if (!tracker_data_manager_drop_graph (sparql->data_manager,
+ l->data, &inner_error))
+ break;
+ }
+
+ g_list_free (graphs);
+
+ return handle_silent (silent, inner_error, error);
}
static gboolean
translate_Create (TrackerSparql *sparql,
GError **error)
{
- _unimplemented ("CREATE");
+ gboolean silent = FALSE;
+ GError *inner_error = NULL;
+ const gchar *graph_name;
+
+ /* Create ::= 'CREATE' 'SILENT'? GraphRef
+ */
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CREATE);
+
+ if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_SILENT))
+ silent = TRUE;
+
+ _call_rule (sparql, NAMED_RULE_GraphRef, error);
+ g_assert (!tracker_token_is_empty (&sparql->current_state.graph));
+
+ graph_name = tracker_token_get_idstring (&sparql->current_state.graph);
+
+ if (tracker_data_manager_find_graph (sparql->data_manager, graph_name) != 0) {
+ inner_error = g_error_new (TRACKER_SPARQL_ERROR,
+ TRACKER_SPARQL_ERROR_CONSTRAINT,
+ "Graph '%s' already exists",
+ graph_name);
+ goto error;
+ }
+
+ if (!tracker_data_manager_create_graph (sparql->data_manager,
+ graph_name,
+ &inner_error))
+ goto error;
+
+ return TRUE;
+
+error:
+ return handle_silent (silent, inner_error, error);
}
static gboolean
translate_Add (TrackerSparql *sparql,
GError **error)
{
- _unimplemented ("ADD");
+ gboolean silent = FALSE;
+ gchar *source, *destination;
+ GError *inner_error = NULL;
+
+ /* Add ::= 'ADD' 'SILENT'? GraphOrDefault 'TO' GraphOrDefault
+ */
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_ADD);
+
+ if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_SILENT))
+ silent = TRUE;
+
+ _call_rule (sparql, NAMED_RULE_GraphOrDefault, error);
+ source = g_strdup (tracker_token_get_idstring (&sparql->current_state.graph));
+
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_TO);
+
+ _call_rule (sparql, NAMED_RULE_GraphOrDefault, error);
+ destination = g_strdup (tracker_token_get_idstring (&sparql->current_state.graph));
+
+ if (g_strcmp0 (source, destination) == 0) {
+ g_free (source);
+ g_free (destination);
+ return TRUE;
+ }
+
+ if (source &&
+ !tracker_data_manager_find_graph (sparql->data_manager, source)) {
+ g_set_error (&inner_error, TRACKER_SPARQL_ERROR,
+ TRACKER_SPARQL_ERROR_UNKNOWN_GRAPH,
+ "Unknown graph '%s'", source);
+ goto error;
+ }
+
+ if (destination &&
+ !tracker_data_manager_find_graph (sparql->data_manager, destination)) {
+ if (!tracker_data_manager_create_graph (sparql->data_manager,
+ destination, &inner_error))
+ goto error;
+ }
+
+ if (!tracker_data_manager_copy_graph (sparql->data_manager,
+ source, destination,
+ &inner_error))
+ goto error;
+
+ g_free (source);
+ g_free (destination);
+
+ return TRUE;
+
+error:
+
+ g_free (source);
+ g_free (destination);
+
+ return handle_silent (silent, inner_error, error);
}
static gboolean
translate_Move (TrackerSparql *sparql,
GError **error)
{
- _unimplemented ("MOVE");
+ gboolean silent = FALSE;
+ gchar *source, *destination;
+ GError *inner_error = NULL;
+
+ /* Move ::= 'MOVE' 'SILENT'? GraphOrDefault 'TO' GraphOrDefault
+ */
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_MOVE);
+
+ if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_SILENT))
+ silent = TRUE;
+
+ _call_rule (sparql, NAMED_RULE_GraphOrDefault, error);
+ source = g_strdup (tracker_token_get_idstring (&sparql->current_state.graph));
+
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_TO);
+
+ _call_rule (sparql, NAMED_RULE_GraphOrDefault, error);
+ destination = g_strdup (tracker_token_get_idstring (&sparql->current_state.graph));
+
+ if (g_strcmp0 (source, destination) == 0) {
+ g_free (source);
+ g_free (destination);
+ return TRUE;
+ }
+
+ if (source &&
+ !tracker_data_manager_find_graph (sparql->data_manager, source)) {
+ g_set_error (&inner_error, TRACKER_SPARQL_ERROR,
+ TRACKER_SPARQL_ERROR_UNKNOWN_GRAPH,
+ "Unknown graph '%s'", source);
+ goto error;
+ }
+
+ if (destination &&
+ !tracker_data_manager_find_graph (sparql->data_manager, destination)) {
+ if (!tracker_data_manager_create_graph (sparql->data_manager,
+ destination, &inner_error))
+ goto error;
+ } else {
+ if (!tracker_data_manager_clear_graph (sparql->data_manager,
+ destination, &inner_error))
+ goto error;
+ }
+
+ if (!tracker_data_manager_copy_graph (sparql->data_manager,
+ source, destination,
+ &inner_error))
+ goto error;
+
+ if (!tracker_data_manager_drop_graph (sparql->data_manager,
+ source,
+ &inner_error))
+ goto error;
+
+ g_free (source);
+ g_free (destination);
+
+ return TRUE;
+
+error:
+ g_free (source);
+ g_free (destination);
+
+ return handle_silent (silent, inner_error, error);
}
static gboolean
translate_Copy (TrackerSparql *sparql,
GError **error)
{
- _unimplemented ("COPY");
+ gboolean silent = FALSE;
+ gchar *source, *destination;
+ GError *inner_error = NULL;
+
+ /* Copy ::= 'COPY' 'SILENT'? GraphOrDefault 'TO' GraphOrDefault
+ */
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_COPY);
+
+ if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_SILENT))
+ silent = TRUE;
+
+ _call_rule (sparql, NAMED_RULE_GraphOrDefault, error);
+ g_assert (!tracker_token_is_empty (&sparql->current_state.graph) ||
+ sparql->current_state.graph_op == GRAPH_OP_DEFAULT);
+ source = g_strdup (tracker_token_get_idstring (&sparql->current_state.graph));
+
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_TO);
+
+ _call_rule (sparql, NAMED_RULE_GraphOrDefault, error);
+ g_assert (!tracker_token_is_empty (&sparql->current_state.graph) ||
+ sparql->current_state.graph_op == GRAPH_OP_DEFAULT);
+ destination = g_strdup (tracker_token_get_idstring (&sparql->current_state.graph));
+
+ if (g_strcmp0 (source, destination) == 0) {
+ g_free (source);
+ g_free (destination);
+ return TRUE;
+ }
+
+ if (source &&
+ !tracker_data_manager_find_graph (sparql->data_manager, source)) {
+ g_set_error (&inner_error, TRACKER_SPARQL_ERROR,
+ TRACKER_SPARQL_ERROR_UNKNOWN_GRAPH,
+ "Unknown graph '%s'", source);
+ goto error;
+ }
+
+ if (destination &&
+ !tracker_data_manager_find_graph (sparql->data_manager, destination)) {
+ if (!tracker_data_manager_create_graph (sparql->data_manager,
+ destination, &inner_error))
+ goto error;
+ } else {
+ if (!tracker_data_manager_clear_graph (sparql->data_manager,
+ destination, &inner_error))
+ goto error;
+ }
+
+ if (!tracker_data_manager_copy_graph (sparql->data_manager,
+ source, destination,
+ &inner_error))
+ goto error;
+
+ g_free (source);
+ g_free (destination);
+
+ return TRUE;
+
+error:
+ g_free (source);
+ g_free (destination);
+
+ return handle_silent (silent, inner_error, error);
}
static gboolean
@@ -2571,10 +3669,7 @@ get_solution_for_pattern (TrackerSparql *sparql,
sparql->current_state.select_context = sparql->context;
tracker_sparql_push_context (sparql, sparql->context);
- g_clear_pointer (&sparql->sql, tracker_string_builder_free);
- sparql->sql = tracker_string_builder_new ();
- tracker_sparql_swap_builder (sparql, sparql->sql);
- sparql->current_state.with_clauses = _prepend_placeholder (sparql);
+ tracker_sparql_init_string_builder (sparql);
retval = prepare_solution_select (sparql, pattern, error);
tracker_sparql_pop_context (sparql, FALSE);
@@ -2585,9 +3680,9 @@ get_solution_for_pattern (TrackerSparql *sparql,
}
iface = tracker_data_manager_get_writable_db_interface (sparql->data_manager);
- stmt = prepare_query (iface, sparql->sql,
+ stmt = prepare_query (sparql, iface, sparql->sql,
TRACKER_SELECT_CONTEXT (sparql->context)->literal_bindings,
- NULL, FALSE,
+ NULL, TRUE,
error);
g_clear_object (&sparql->context);
@@ -2792,6 +3887,7 @@ translate_InsertClause (TrackerSparql *sparql,
GError **error)
{
TrackerToken old_graph;
+ gboolean into = FALSE;
/* InsertClause ::= 'INSERT' QuadPattern
*
@@ -2806,8 +3902,6 @@ translate_InsertClause (TrackerSparql *sparql,
sparql->current_state.blank_node_map =
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
- old_graph = sparql->current_state.graph;
-
sparql->current_state.type = TRACKER_SPARQL_TYPE_INSERT;
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_INSERT);
@@ -2821,15 +3915,19 @@ translate_InsertClause (TrackerSparql *sparql,
sparql->silent = _accept (sparql, RULE_TYPE_LITERAL, LITERAL_SILENT);
if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_INTO)) {
+ old_graph = sparql->current_state.graph;
_call_rule (sparql, NAMED_RULE_iri, error);
_init_token (&sparql->current_state.graph,
sparql->current_state.prev_node, sparql);
+ into = TRUE;
}
_call_rule (sparql, NAMED_RULE_QuadPattern, error);
- tracker_token_unset (&sparql->current_state.graph);
- sparql->current_state.graph = old_graph;
+ if (into) {
+ tracker_token_unset (&sparql->current_state.graph);
+ sparql->current_state.graph = old_graph;
+ }
if (sparql->blank_nodes) {
g_variant_builder_close (sparql->blank_nodes);
@@ -2845,14 +3943,28 @@ static gboolean
translate_UsingClause (TrackerSparql *sparql,
GError **error)
{
+ gboolean named = FALSE;
+ gchar *graph;
+
/* UsingClause ::= 'USING' ( iri | 'NAMED' iri )
*/
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_USING);
- if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_NAMED)) {
- }
+ if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_NAMED))
+ named = TRUE;
_call_rule (sparql, NAMED_RULE_iri, error);
+ _init_token (&sparql->current_state.graph,
+ sparql->current_state.prev_node, sparql);
+ graph = g_strdup (tracker_token_get_idstring (&sparql->current_state.graph));
+
+ if (named)
+ g_ptr_array_add (sparql->named_graphs, graph);
+ else
+ g_ptr_array_add (sparql->anon_graphs, graph);
+
+ tracker_token_unset (&sparql->current_state.graph);
+ g_free (graph);
return TRUE;
}
@@ -2864,10 +3976,13 @@ translate_GraphOrDefault (TrackerSparql *sparql,
/* GraphOrDefault ::= 'DEFAULT' | 'GRAPH'? iri
*/
if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_DEFAULT)) {
-
+ tracker_token_unset (&sparql->current_state.graph);
+ sparql->current_state.graph_op = GRAPH_OP_DEFAULT;
} else {
_accept (sparql, RULE_TYPE_LITERAL, LITERAL_GRAPH);
_call_rule (sparql, NAMED_RULE_iri, error);
+ _init_token (&sparql->current_state.graph,
+ sparql->current_state.prev_node, sparql);
}
return TRUE;
@@ -2879,9 +3994,12 @@ translate_GraphRefAll (TrackerSparql *sparql,
{
/* GraphRefAll ::= GraphRef | 'DEFAULT' | 'NAMED' | 'ALL'
*/
- if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_DEFAULT) ||
- _accept (sparql, RULE_TYPE_LITERAL, LITERAL_NAMED) ||
- _accept (sparql, RULE_TYPE_LITERAL, LITERAL_ALL)) {
+ if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_DEFAULT)) {
+ sparql->current_state.graph_op = GRAPH_OP_DEFAULT;
+ } else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_NAMED)) {
+ sparql->current_state.graph_op = GRAPH_OP_NAMED;
+ } else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_ALL)) {
+ sparql->current_state.graph_op = GRAPH_OP_ALL;
} else {
_call_rule (sparql, NAMED_RULE_GraphRef, error);
}
@@ -2897,6 +4015,8 @@ translate_GraphRef (TrackerSparql *sparql,
*/
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_GRAPH);
_call_rule (sparql, NAMED_RULE_iri, error);
+ _init_token (&sparql->current_state.graph,
+ sparql->current_state.prev_node, sparql);
return TRUE;
}
@@ -3214,9 +4334,95 @@ static gboolean
translate_ServiceGraphPattern (TrackerSparql *sparql,
GError **error)
{
+ gssize pattern_start, pattern_end;
+ TrackerParserNode *pattern, *node;
+ gchar *pattern_str, *escaped_str, *var_str;
+ TrackerContext *context;
+ GList *variables = NULL;
+ TrackerToken service;
+ GString *service_sparql;
+ gboolean silent = FALSE, do_join;
+ gint i = 0;
+
/* ServiceGraphPattern ::= 'SERVICE' 'SILENT'? VarOrIri GroupGraphPattern
*/
- _unimplemented ("SERVICE");
+ do_join = !tracker_string_builder_is_empty (sparql->current_state.sql);
+
+ if (do_join) {
+ _prepend_string (sparql, "SELECT * FROM (");
+ _append_string (sparql, ") NATURAL INNER JOIN (");
+ }
+
+ context = tracker_triple_context_new ();
+ tracker_sparql_push_context (sparql, context);
+
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_SERVICE);
+
+ if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_SILENT))
+ silent = TRUE;
+
+ _call_rule (sparql, NAMED_RULE_VarOrIri, error);
+ _init_token (&service, sparql->current_state.prev_node, sparql);
+
+ pattern = _skip_rule (sparql, NAMED_RULE_GroupGraphPattern);
+ _append_string (sparql, "SELECT ");
+ service_sparql = g_string_new ("SELECT ");
+
+ for (node = tracker_sparql_parser_tree_find_first (pattern, TRUE);
+ node;
+ node = tracker_sparql_parser_tree_find_next (node, TRUE)) {
+ const TrackerGrammarRule *rule;
+ TrackerBinding *binding;
+ TrackerVariable *var;
+
+ if (!g_node_is_ancestor ((GNode *) pattern, (GNode *) node))
+ break;
+
+ rule = tracker_parser_node_get_rule (node);
+
+ if (!tracker_grammar_rule_is_a (rule, RULE_TYPE_TERMINAL,
+ TERMINAL_TYPE_VAR1) &&
+ !tracker_grammar_rule_is_a (rule, RULE_TYPE_TERMINAL,
+ TERMINAL_TYPE_VAR2))
+ continue;
+
+ if (i > 0)
+ _append_string (sparql, ", ");
+
+ var_str = _extract_node_string (node, sparql);
+ var = _extract_node_variable (node, sparql);
+ variables = g_list_prepend (variables, var);
+ binding = tracker_variable_binding_new (var, NULL, NULL);
+ _add_binding (sparql, binding);
+
+
+ _append_string_printf (sparql, "col%d AS %s ",
+ i, tracker_variable_get_sql_expression (var));
+ g_string_append_printf (service_sparql, "?%s ", var_str);
+ g_free (var_str);
+ i++;
+ }
+
+ tracker_parser_node_get_extents (pattern, &pattern_start, &pattern_end);
+ pattern_str = g_strndup (&sparql->sparql[pattern_start], pattern_end - pattern_start);
+ escaped_str = _escape_sql_string (pattern_str);
+ g_string_append (service_sparql, escaped_str);
+ g_free (pattern_str);
+ g_free (escaped_str);
+
+ _append_string_printf (sparql, "FROM tracker_service WHERE service=\"%s\" AND query=\"%s\" AND silent=%d ",
+ tracker_token_get_idstring (&service),
+ service_sparql->str,
+ silent);
+
+ tracker_token_unset (&service);
+ tracker_sparql_pop_context (sparql, TRUE);
+ g_string_free (service_sparql, TRUE);
+
+ if (do_join)
+ _append_string (sparql, ") ");
+
+ return TRUE;
}
static gboolean
@@ -3279,9 +4485,24 @@ static gboolean
translate_InlineData (TrackerSparql *sparql,
GError **error)
{
+ gboolean do_join;
+
/* InlineData ::= 'VALUES' DataBlock
*/
- _unimplemented ("VALUES");
+ do_join = !tracker_string_builder_is_empty (sparql->current_state.sql);
+
+ if (do_join) {
+ _prepend_string (sparql, "SELECT * FROM (");
+ _append_string (sparql, ") NATURAL INNER JOIN (");
+ }
+
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_VALUES);
+ _call_rule (sparql, NAMED_RULE_DataBlock, error);
+
+ if (do_join)
+ _append_string (sparql, ")");
+
+ return TRUE;
}
static gboolean
@@ -3289,9 +4510,20 @@ translate_DataBlock (TrackerSparql *sparql,
GError **error)
{
TrackerGrammarNamedRule rule;
+ TrackerStringBuilder *old;
/* DataBlock ::= InlineDataOneVar | InlineDataFull
*/
+ old = tracker_sparql_swap_builder (sparql, sparql->current_state.with_clauses);
+
+ if (tracker_string_builder_is_empty (sparql->current_state.with_clauses))
+ _append_string (sparql, "WITH ");
+ else
+ _append_string (sparql, ", ");
+
+ sparql->current_state.values_idx++;
+ _append_string_printf (sparql, "\"dataBlock%d\"",
+ sparql->current_state.values_idx);
rule = _current_rule (sparql);
switch (rule) {
@@ -3303,6 +4535,11 @@ translate_DataBlock (TrackerSparql *sparql,
g_assert_not_reached ();
}
+ tracker_sparql_swap_builder (sparql, old);
+
+ _append_string_printf (sparql, "SELECT * FROM \"dataBlock%d\"",
+ sparql->current_state.values_idx);
+
return TRUE;
}
@@ -3310,17 +4547,41 @@ static gboolean
translate_InlineDataOneVar (TrackerSparql *sparql,
GError **error)
{
+ TrackerVariable *var;
+ TrackerBinding *binding;
+ gint n_values = 0;
+
/* InlineDataOneVar ::= Var '{' DataBlockValue* '}'
*/
_call_rule (sparql, NAMED_RULE_Var, error);
+ var = _last_node_variable (sparql);
+ binding = tracker_variable_binding_new (var, NULL, NULL);
+ tracker_variable_set_sample_binding (var, TRACKER_VARIABLE_BINDING (binding));
+
+ _append_string (sparql, "(");
+ _append_variable_sql (sparql, var);
+ _append_string (sparql, ") AS ( ");
+
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_BRACE);
while (_check_in_rule (sparql, NAMED_RULE_DataBlockValue)) {
+ if (n_values == 0)
+ _append_string (sparql, "VALUES ");
+ else
+ _append_string (sparql, ", ");
+
+ _append_string (sparql, "(");
_call_rule (sparql, NAMED_RULE_DataBlockValue, error);
+ _append_string (sparql, ") ");
+ n_values++;
}
+ if (n_values == 0)
+ _append_string (sparql, "SELECT NULL WHERE FALSE");
+
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_BRACE);
+ _append_string (sparql, ") ");
return TRUE;
}
@@ -3329,13 +4590,30 @@ static gboolean
translate_InlineDataFull (TrackerSparql *sparql,
GError **error)
{
+ TrackerVariable *var;
+ TrackerBinding *binding;
+ gint n_params, n_args = 0, n_values = 0;
+
/* InlineDataFull ::= ( NIL | '(' Var* ')' ) '{' ( '(' DataBlockValue* ')' | NIL )* '}'
*/
- if (_accept (sparql, RULE_TYPE_TERMINAL, TERMINAL_TYPE_NIL)) {
+ _append_string (sparql, "(");
+ if (_accept (sparql, RULE_TYPE_TERMINAL, TERMINAL_TYPE_NIL)) {
+ _append_string (sparql, "NONE");
+ n_args = 0;
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS)) {
while (_check_in_rule (sparql, NAMED_RULE_Var)) {
+ if (n_args != 0)
+ _append_string (sparql, ", ");
+
_call_rule (sparql, NAMED_RULE_Var, error);
+
+ var = _last_node_variable (sparql);
+ binding = tracker_variable_binding_new (var, NULL, NULL);
+ tracker_variable_set_sample_binding (var, TRACKER_VARIABLE_BINDING (binding));
+ n_args++;
+
+ _append_variable_sql (sparql, var);
}
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
@@ -3343,21 +4621,58 @@ translate_InlineDataFull (TrackerSparql *sparql,
g_assert_not_reached ();
}
+ _append_string (sparql, ") AS ( ");
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_BRACE);
do {
- if (_accept (sparql, RULE_TYPE_TERMINAL, TERMINAL_TYPE_NIL)) {
- } else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS)) {
+ gboolean is_nil = FALSE;
+
+ if (_accept (sparql, RULE_TYPE_TERMINAL, TERMINAL_TYPE_NIL))
+ is_nil = TRUE;
+ else if (!_accept (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS))
+ break;
+
+ if (n_values == 0)
+ _append_string (sparql, "VALUES ");
+ else
+ _append_string (sparql, ", ");
+
+ if (is_nil) {
+ _append_string (sparql, "(NULL)");
+ n_params = 0;
+ } else {
+ n_params = 0;
+
+ _append_string (sparql, "(");
+
while (_check_in_rule (sparql, NAMED_RULE_DataBlockValue)) {
+ if (n_params != 0)
+ _append_string (sparql, ", ");
+
_call_rule (sparql, NAMED_RULE_DataBlockValue, error);
+ n_params++;
}
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
- } else {
- break;
+ _append_string (sparql, ") ");
}
+
+ if (n_params != n_args) {
+ g_set_error (error, TRACKER_SPARQL_ERROR,
+ TRACKER_SPARQL_ERROR_PARSE,
+ "VALUES defined %d arguments but set has %d parameters",
+ n_args, n_params);
+ return FALSE;
+ }
+
+ n_values++;
} while (TRUE);
+ if (n_values == 0)
+ _append_string (sparql, "SELECT NULL WHERE FALSE");
+
+ _append_string (sparql, ") ");
+
return TRUE;
}
@@ -3365,22 +4680,36 @@ static gboolean
translate_DataBlockValue (TrackerSparql *sparql,
GError **error)
{
+ TrackerSelectContext *select_context;
TrackerGrammarNamedRule rule;
+ TrackerBinding *binding;
/* DataBlockValue ::= iri | RDFLiteral | NumericLiteral | BooleanLiteral | 'UNDEF'
*/
if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_UNDEF)) {
+ _append_string (sparql, "NULL ");
return TRUE;
}
+ select_context = TRACKER_SELECT_CONTEXT (sparql->current_state.select_context);
rule = _current_rule (sparql);
switch (rule) {
- case NAMED_RULE_iri:
case NAMED_RULE_RDFLiteral:
+ _call_rule (sparql, rule, error);
+ binding = g_ptr_array_index (select_context->literal_bindings,
+ select_context->literal_bindings->len - 1);
+ _append_literal_sql (sparql, TRACKER_LITERAL_BINDING (binding));
+ break;
+ case NAMED_RULE_iri:
case NAMED_RULE_NumericLiteral:
case NAMED_RULE_BooleanLiteral:
_call_rule (sparql, rule, error);
+ binding = _convert_terminal (sparql);
+ tracker_select_context_add_literal_binding (select_context,
+ TRACKER_LITERAL_BINDING (binding));
+ _append_literal_sql (sparql, TRACKER_LITERAL_BINDING (binding));
+ g_object_unref (binding);
break;
default:
g_assert_not_reached ();
@@ -3563,7 +4892,13 @@ translate_ArgList (TrackerSparql *sparql,
_raise (PARSE, "Recursive ArgList is not allowed", "ArgList");
if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_DISTINCT)) {
- _unimplemented ("DISTINCT in ArgList");
+ /* This path is only for custom aggregate function, as per
+ * the SPARQL recommendation, note 15 in grammar section:
+ * "Only custom aggregate functions use the DISTINCT keyword in a function call."
+ *
+ * But we have none, so it's fine to bail out here.
+ */
+ _raise (PARSE, "DISTINCT is not allowed in non-aggregate function", "ArgList");
}
_call_rule (sparql, NAMED_RULE_Expression, error);
@@ -3902,7 +5237,7 @@ translate_VerbPath (TrackerSparql *sparql,
prop = tracker_sparql_parser_tree_find_first (sparql->current_state.node, TRUE);
str = _extract_node_string (prop, sparql);
- tracker_token_literal_init (&sparql->current_state.predicate, str);
+ tracker_token_literal_init (&sparql->current_state.predicate, str, -1);
g_free (str);
_skip_rule (sparql, NAMED_RULE_Path);
@@ -4172,7 +5507,7 @@ translate_PathPrimary (TrackerSparql *sparql,
prop);
if (!path_elem) {
- path_elem = tracker_path_element_property_new (prop);
+ path_elem = tracker_path_element_property_new (TRACKER_PATH_OPERATOR_NONE, prop);
tracker_select_context_add_path_element (TRACKER_SELECT_CONTEXT (sparql->context),
path_elem);
_prepend_path_element (sparql, path_elem);
@@ -4191,19 +5526,118 @@ static gboolean
translate_PathNegatedPropertySet (TrackerSparql *sparql,
GError **error)
{
+ TrackerPathElement *path_elem;
+
/* PathNegatedPropertySet ::= PathOneInPropertySet | '(' ( PathOneInPropertySet ( '|' PathOneInPropertySet )* )? ')'
*/
- _unimplemented ("Negated property set in property paths");
- return FALSE;
+ if (_check_in_rule (sparql, NAMED_RULE_PathOneInPropertySet))
+ _call_rule (sparql, NAMED_RULE_PathOneInPropertySet, error);
+ else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS)) {
+ GPtrArray *path_elems;
+
+ path_elems = g_ptr_array_new ();
+
+ _call_rule (sparql, NAMED_RULE_PathEltOrInverse, error);
+ g_ptr_array_add (path_elems, sparql->current_state.path);
+
+ while (_check_in_rule (sparql, NAMED_RULE_PathOneInPropertySet)) {
+ _call_rule (sparql, NAMED_RULE_PathOneInPropertySet, error);
+ g_ptr_array_add (path_elems, sparql->current_state.path);
+ }
+
+ if (path_elems->len > 1) {
+ gint i;
+
+ path_elem = tracker_path_element_operator_new (TRACKER_PATH_OPERATOR_INTERSECTION,
+ g_ptr_array_index (path_elems, 0),
+ g_ptr_array_index (path_elems, 1));
+ tracker_select_context_add_path_element (TRACKER_SELECT_CONTEXT (sparql->context),
+ path_elem);
+ _prepend_path_element (sparql, path_elem);
+
+ for (i = 2; i < path_elems->len; i++) {
+ TrackerPathElement *child;
+
+ child = g_ptr_array_index (path_elems, i);
+ path_elem = tracker_path_element_operator_new (TRACKER_PATH_OPERATOR_INTERSECTION,
+ child, path_elem);
+ tracker_select_context_add_path_element (TRACKER_SELECT_CONTEXT (sparql->context),
+ path_elem);
+ _prepend_path_element (sparql, path_elem);
+ }
+
+ sparql->current_state.path = path_elem;
+ }
+
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
+ } else {
+ g_assert_not_reached ();
+ }
+
+ return TRUE;
}
static gboolean
translate_PathOneInPropertySet (TrackerSparql *sparql,
GError **error)
{
+ TrackerPathElement *path_elem;
+ gboolean inverse = FALSE;
+
/* PathOneInPropertySet ::= iri | 'a' | '^' ( iri | 'a' )
*/
- return FALSE;
+ if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_PATH_INVERSE))
+ inverse = TRUE;
+
+ if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_A) ||
+ _check_in_rule (sparql, NAMED_RULE_iri)) {
+ TrackerOntologies *ontologies;
+ TrackerProperty *prop;
+ gchar *str;
+
+ if (_check_in_rule (sparql, NAMED_RULE_iri))
+ _call_rule (sparql, NAMED_RULE_iri, error);
+
+ str = _dup_last_string (sparql);
+ ontologies = tracker_data_manager_get_ontologies (sparql->data_manager);
+ prop = tracker_ontologies_get_property_by_uri (ontologies, str);
+
+ if (!prop) {
+ g_set_error (error, TRACKER_SPARQL_ERROR,
+ TRACKER_SPARQL_ERROR_UNKNOWN_PROPERTY,
+ "Unknown property '%s'", str);
+ g_free (str);
+ return FALSE;
+ }
+
+ path_elem =
+ tracker_select_context_lookup_path_element_for_property (TRACKER_SELECT_CONTEXT (sparql->context),
+ prop);
+
+ if (!path_elem) {
+ path_elem = tracker_path_element_property_new (TRACKER_PATH_OPERATOR_NEGATED, prop);
+ tracker_select_context_add_path_element (TRACKER_SELECT_CONTEXT (sparql->context),
+ path_elem);
+ _prepend_path_element (sparql, path_elem);
+ }
+
+ sparql->current_state.path = path_elem;
+ g_free (str);
+ } else {
+ g_assert_not_reached ();
+ }
+
+ if (inverse) {
+ path_elem = tracker_path_element_operator_new (TRACKER_PATH_OPERATOR_INVERSE,
+ sparql->current_state.path,
+ NULL);
+ tracker_select_context_add_path_element (TRACKER_SELECT_CONTEXT (sparql->context),
+ path_elem);
+ _prepend_path_element (sparql, path_elem);
+ sparql->current_state.path = path_elem;
+ }
+
+ return TRUE;
}
static gboolean
@@ -4260,7 +5694,7 @@ translate_BlankNodePropertyList (TrackerSparql *sparql,
iface = tracker_data_manager_get_writable_db_interface (sparql->data_manager);
bnode_id = tracker_data_query_unused_uuid (sparql->data_manager, iface);
- tracker_token_literal_init (&sparql->current_state.subject, bnode_id);
+ tracker_token_literal_init (&sparql->current_state.subject, bnode_id, -1);
g_free (bnode_id);
}
@@ -4333,18 +5767,213 @@ static gboolean
translate_Collection (TrackerSparql *sparql,
GError **error)
{
+ TrackerToken old_subject, old_predicate, old_object, *old_token, *cur;
+ GArray *elems;
+ gint i;
+
/* Collection ::= '(' GraphNode+ ')'
*/
- _unimplemented ("Collections are not supported");
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
+
+ old_subject = sparql->current_state.subject;
+ old_predicate = sparql->current_state.predicate;
+ old_object = sparql->current_state.object;
+ old_token = sparql->current_state.token;
+
+ elems = g_array_new (FALSE, TRUE, sizeof (TrackerToken));
+
+ while (_check_in_rule (sparql, NAMED_RULE_GraphNode)) {
+ if (elems->len > 0) {
+ cur = &g_array_index (elems, TrackerToken, elems->len - 1);
+ } else {
+ g_array_set_size (elems, elems->len + 1);
+ cur = &g_array_index (elems, TrackerToken, 0);
+
+ if (sparql->current_state.type == TRACKER_SPARQL_TYPE_SELECT) {
+ TrackerVariable *var;
+ var = tracker_select_context_add_generated_variable (TRACKER_SELECT_CONTEXT (sparql->context));
+ tracker_token_variable_init (cur, var);
+ } else {
+ TrackerDBInterface *iface;
+ gchar *bnode_id;
+
+ iface = tracker_data_manager_get_writable_db_interface (sparql->data_manager);
+ bnode_id = tracker_data_query_unused_uuid (sparql->data_manager, iface);
+ tracker_token_literal_init (cur, bnode_id, -1);
+ g_free (bnode_id);
+ }
+ }
+
+ sparql->current_state.subject = *cur;
+
+ /* Add "_:elem a rdf:List" first */
+ tracker_token_literal_init (&sparql->current_state.predicate,
+ RDF_NS "type", -1);
+ tracker_token_literal_init (&sparql->current_state.object,
+ RDF_NS "List", -1);
+
+ if (!tracker_sparql_apply_quad (sparql, error)) {
+ tracker_token_unset (&sparql->current_state.predicate);
+ tracker_token_unset (&sparql->current_state.object);
+ goto error;
+ }
+
+ /* rdf:first */
+ tracker_token_literal_init (&sparql->current_state.predicate,
+ RDF_NS "first", -1);
+ sparql->current_state.token = &sparql->current_state.object;
+ _call_rule (sparql, NAMED_RULE_GraphNode, error);
+ sparql->current_state.token = NULL;
+ tracker_token_unset (&sparql->current_state.predicate);
+
+ /* rdf:rest */
+ tracker_token_literal_init (&sparql->current_state.predicate,
+ RDF_NS "rest", -1);
+
+ if (_check_in_rule (sparql, NAMED_RULE_GraphNode)) {
+ /* Generate variable for next element */
+ g_array_set_size (elems, elems->len + 1);
+ cur = &g_array_index (elems, TrackerToken, elems->len - 1);
+
+ if (sparql->current_state.type == TRACKER_SPARQL_TYPE_SELECT) {
+ TrackerVariable *var;
+ var = tracker_select_context_add_generated_variable (TRACKER_SELECT_CONTEXT (sparql->context));
+ tracker_token_variable_init (cur, var);
+ } else {
+ TrackerDBInterface *iface;
+ gchar *bnode_id;
+
+ iface = tracker_data_manager_get_writable_db_interface (sparql->data_manager);
+ bnode_id = tracker_data_query_unused_uuid (sparql->data_manager, iface);
+ tracker_token_literal_init (cur, bnode_id, -1);
+ g_free (bnode_id);
+ }
+
+ sparql->current_state.object = *cur;
+
+ if (!tracker_sparql_apply_quad (sparql, error)) {
+ tracker_token_unset (&sparql->current_state.predicate);
+ goto error;
+ }
+ } else {
+ /* Make last element point to rdf:nil */
+ tracker_token_literal_init (&sparql->current_state.object,
+ RDF_NS "nil", -1);
+
+ if (!tracker_sparql_apply_quad (sparql, error)) {
+ tracker_token_unset (&sparql->current_state.predicate);
+ tracker_token_unset (&sparql->current_state.object);
+ goto error;
+ }
+ }
+
+ tracker_token_unset (&sparql->current_state.predicate);
+ }
+
+ sparql->current_state.subject = old_subject;
+ sparql->current_state.predicate = old_predicate;
+ sparql->current_state.object = old_object;
+ sparql->current_state.token = old_token;
+
+ *sparql->current_state.token = g_array_index (elems, TrackerToken, 0);
+
+ for (i = 1; i < elems->len; i++) {
+ cur = &g_array_index (elems, TrackerToken, i);
+ tracker_token_unset (cur);
+ }
+
+ g_array_unref (elems);
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
+
+ return TRUE;
+
+error:
+ sparql->current_state.subject = old_subject;
+ sparql->current_state.predicate = old_predicate;
+ sparql->current_state.object = old_object;
+ sparql->current_state.token = old_token;
+
+ for (i = 0; i < elems->len; i++) {
+ cur = &g_array_index (elems, TrackerToken, i);
+ tracker_token_unset (cur);
+ }
+
+ g_array_unref (elems);
+
+ return FALSE;
}
static gboolean
translate_CollectionPath (TrackerSparql *sparql,
GError **error)
{
+ TrackerToken old_subject, old_predicate, old_object, *old_token;
+ TrackerVariable *cur, *first = NULL, *rest = NULL;
+
+ old_subject = sparql->current_state.subject;
+ old_predicate = sparql->current_state.predicate;
+ old_object = sparql->current_state.object;
+ old_token = sparql->current_state.token;
+
/* CollectionPath ::= '(' GraphNodePath+ ')'
*/
- _unimplemented ("Collections are not supported");
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
+ while (_check_in_rule (sparql, NAMED_RULE_GraphNodePath)) {
+ if (rest) {
+ cur = rest;
+ rest = NULL;
+ } else {
+ cur = tracker_select_context_add_generated_variable (TRACKER_SELECT_CONTEXT (sparql->context));
+ first = cur;
+ }
+
+ tracker_token_variable_init (&sparql->current_state.subject, cur);
+
+ /* rdf:first */
+ tracker_token_literal_init (&sparql->current_state.predicate,
+ RDF_NS "first", -1);
+ sparql->current_state.token = &sparql->current_state.object;
+ _call_rule (sparql, NAMED_RULE_GraphNodePath, error);
+ sparql->current_state.token = NULL;
+ tracker_token_unset (&sparql->current_state.predicate);
+
+ /* rdf:rest */
+ tracker_token_literal_init (&sparql->current_state.predicate,
+ RDF_NS "rest", -1);
+
+ if (_check_in_rule (sparql, NAMED_RULE_GraphNodePath)) {
+ /* Generate variable for next element */
+ rest = tracker_select_context_add_generated_variable (TRACKER_SELECT_CONTEXT (sparql->context));
+ tracker_token_variable_init (&sparql->current_state.object, rest);
+ } else {
+ /* Make last element point to rdf:nil */
+ tracker_token_literal_init (&sparql->current_state.object,
+ RDF_NS "nil", -1);
+ }
+
+ if (!_add_quad (sparql,
+ &sparql->current_state.graph,
+ &sparql->current_state.subject,
+ &sparql->current_state.predicate,
+ &sparql->current_state.object,
+ error))
+ return FALSE;
+
+ tracker_token_unset (&sparql->current_state.object);
+ tracker_token_unset (&sparql->current_state.predicate);
+
+ tracker_token_unset (&sparql->current_state.subject);
+ }
+
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
+
+ sparql->current_state.subject = old_subject;
+ sparql->current_state.predicate = old_predicate;
+ sparql->current_state.object = old_object;
+ sparql->current_state.token = old_token;
+ tracker_token_variable_init (sparql->current_state.token, first);
+
+ return TRUE;
}
static gboolean
@@ -4386,42 +6015,8 @@ translate_GraphNode (TrackerSparql *sparql,
sparql->current_state.type != TRACKER_SPARQL_TYPE_UPDATE))
return TRUE;
- switch (sparql->current_state.type) {
- case TRACKER_SPARQL_TYPE_SELECT:
- _add_quad (sparql,
- &sparql->current_state.graph,
- &sparql->current_state.subject,
- &sparql->current_state.predicate,
- &sparql->current_state.object,
- &inner_error);
- break;
- case TRACKER_SPARQL_TYPE_INSERT:
- tracker_data_insert_statement (tracker_data_manager_get_data (sparql->data_manager),
- tracker_token_get_idstring (&sparql->current_state.graph),
- tracker_token_get_idstring (&sparql->current_state.subject),
- tracker_token_get_idstring (&sparql->current_state.predicate),
- tracker_token_get_idstring (&sparql->current_state.object),
- &inner_error);
- break;
- case TRACKER_SPARQL_TYPE_DELETE:
- tracker_data_delete_statement (tracker_data_manager_get_data (sparql->data_manager),
- tracker_token_get_idstring (&sparql->current_state.graph),
- tracker_token_get_idstring (&sparql->current_state.subject),
- tracker_token_get_idstring (&sparql->current_state.predicate),
- tracker_token_get_idstring (&sparql->current_state.object),
- &inner_error);
- break;
- case TRACKER_SPARQL_TYPE_UPDATE:
- tracker_data_update_statement (tracker_data_manager_get_data (sparql->data_manager),
- tracker_token_get_idstring (&sparql->current_state.graph),
- tracker_token_get_idstring (&sparql->current_state.subject),
- tracker_token_get_idstring (&sparql->current_state.predicate),
- tracker_token_get_idstring (&sparql->current_state.object),
- &inner_error);
- break;
- default:
- g_assert_not_reached ();
- }
+ if (!tracker_sparql_apply_quad (sparql, error))
+ return FALSE;
tracker_token_unset (&sparql->current_state.object);
@@ -4476,6 +6071,7 @@ translate_VarOrTerm (TrackerSparql *sparql,
switch (rule) {
case NAMED_RULE_Var:
if (sparql->current_state.type != TRACKER_SPARQL_TYPE_SELECT &&
+ sparql->current_state.type != TRACKER_SPARQL_TYPE_CONSTRUCT &&
!sparql->solution_var_map) {
TrackerParserNode *node = sparql->current_state.node;
const gchar *str = "Unknown";
@@ -4546,7 +6142,8 @@ translate_Var (TrackerSparql *sparql,
if (_accept (sparql, RULE_TYPE_TERMINAL, TERMINAL_TYPE_VAR1) ||
_accept (sparql, RULE_TYPE_TERMINAL, TERMINAL_TYPE_VAR2)) {
- if (sparql->current_state.type == TRACKER_SPARQL_TYPE_SELECT) {
+ if (sparql->current_state.type == TRACKER_SPARQL_TYPE_SELECT ||
+ sparql->current_state.type == TRACKER_SPARQL_TYPE_CONSTRUCT) {
TrackerVariableBinding *binding;
TrackerVariable *var;
@@ -4581,8 +6178,10 @@ translate_GraphTerm (TrackerSparql *sparql,
rule = _current_rule (sparql);
switch (rule) {
- case NAMED_RULE_iri:
case NAMED_RULE_RDFLiteral:
+ _call_rule (sparql, rule, error);
+ break;
+ case NAMED_RULE_iri:
case NAMED_RULE_NumericLiteral:
case NAMED_RULE_BooleanLiteral:
_call_rule (sparql, rule, error);
@@ -4923,8 +6522,32 @@ handle_property_function (TrackerSparql *sparql,
TrackerProperty *property,
GError **error)
{
- if (tracker_property_get_multiple_values (property)) {
+ TrackerPropertyType type;
+
+ /* As of SQLite 3.26.0, performing an aggregate function (or anything
+ * that requires scanning all results, like distinct) on a unionGraph
+ * view results in full table scans on all unioned selects.
+ *
+ * This quickly gets far too expensive on property functions, as they
+ * are normally used in the SelectClause, so they get to run once per
+ * result. This makes queries like:
+ *
+ * SELECT rdf:type(?u) { ?u a rdfs:Resource }
+ *
+ * prohibitive. The solution is to perform the union inline, and
+ * evaluate the ArgList once on each of the unioned selects. This again
+ * hits table search, and hopefully through an index, so it gets fast
+ * again.
+ */
+ if (tracker_property_get_multiple_values (property) &&
+ tracker_token_is_empty (&sparql->current_state.graph)) {
TrackerStringBuilder *str, *old;
+ TrackerParserNode *arg;
+ GHashTable *ht;
+ GHashTableIter iter;
+ gpointer graph;
+
+ arg = _skip_rule (sparql, NAMED_RULE_ArgList);
_append_string (sparql, "(SELECT GROUP_CONCAT (");
str = _append_placeholder (sparql);
@@ -4933,23 +6556,73 @@ handle_property_function (TrackerSparql *sparql,
convert_expression_to_string (sparql, tracker_property_get_data_type (property));
tracker_sparql_swap_builder (sparql, old);
- _append_string_printf (sparql, ", ',') FROM \"%s\" WHERE ID = ",
- tracker_property_get_table_name (property));
+ _append_string (sparql, ", ',') ");
+
+ _append_string_printf (sparql, "FROM (SELECT \"%s\" FROM \"main\".\"%s\" WHERE ID = ",
+ tracker_property_get_name (property),
+ tracker_property_get_table_name (property));
+ if (!_postprocess_rule (sparql, arg, NULL, error))
+ return FALSE;
+
+ ht = tracker_data_manager_get_graphs (sparql->data_manager);
+ g_hash_table_iter_init (&iter, ht);
+
+ while (g_hash_table_iter_next (&iter, (gpointer *) &graph, NULL)) {
+ _append_string_printf (sparql, "UNION ALL SELECT \"%s\" FROM \"%s\".\"%s\" WHERE ID = ",
+ tracker_property_get_name (property),
+ (gchar *) graph,
+ tracker_property_get_table_name (property));
+
+ if (!_postprocess_rule (sparql, arg, NULL, error))
+ return FALSE;
+ }
+
+ _append_string (sparql, ")) ");
- _call_rule (sparql, NAMED_RULE_ArgList, error);
sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_STRING;
+ return TRUE;
+ }
+
+ if (tracker_property_get_multiple_values (property)) {
+ TrackerStringBuilder *str, *old;
+
+ _append_string (sparql, "(SELECT GROUP_CONCAT (");
+ str = _append_placeholder (sparql);
+ old = tracker_sparql_swap_builder (sparql, str);
+ _append_string_printf (sparql, "\"%s\"", tracker_property_get_name (property));
+ convert_expression_to_string (sparql, tracker_property_get_data_type (property));
+ tracker_sparql_swap_builder (sparql, old);
+
+ _append_string (sparql, ", ',') ");
+
+ type = TRACKER_PROPERTY_TYPE_STRING;
} else {
_append_string_printf (sparql,
- "(SELECT \"%s\" FROM \"%s\" WHERE ID = ",
- tracker_property_get_name (property),
- tracker_property_get_table_name (property));
+ "(SELECT \"%s\" ",
+ tracker_property_get_name (property));
- _call_rule (sparql, NAMED_RULE_ArgList, error);
- sparql->current_state.expression_type = tracker_property_get_data_type (property);
+ type = tracker_property_get_data_type (property);
}
+ if (tracker_token_is_empty (&sparql->current_state.graph)) {
+ _append_string_printf (sparql, "FROM \"unionGraph_%s\" ",
+ tracker_property_get_table_name (property));
+ if (sparql->union_views) {
+ g_hash_table_add (sparql->union_views,
+ g_strdup (tracker_property_get_table_name (property)));
+ }
+ } else {
+ _append_string_printf (sparql, "FROM \"%s\".\"%s\" ",
+ tracker_token_get_idstring (&sparql->current_state.graph),
+ tracker_property_get_table_name (property));
+ }
+
+ _append_string (sparql, "WHERE ID = ");
+ _call_rule (sparql, NAMED_RULE_ArgList, error);
_append_string (sparql, ") ");
+ sparql->current_state.expression_type = type;
+
return TRUE;
}
@@ -4965,6 +6638,11 @@ handle_type_cast (TrackerSparql *sparql,
_call_rule (sparql, NAMED_RULE_ArgList, error);
_append_string (sparql, "AS TEXT) ");
sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_STRING;
+ } else if (g_str_equal (function, RDF_NS "langString")) {
+ _append_string (sparql, "CAST (");
+ _call_rule (sparql, NAMED_RULE_ArgList, error);
+ _append_string (sparql, "AS BLOB) ");
+ sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_LANGSTRING;
} else if (g_str_equal (function, XSD_NS "integer")) {
_append_string (sparql, "CAST (");
_call_rule (sparql, NAMED_RULE_ArgList, error);
@@ -5121,21 +6799,11 @@ handle_xpath_function (TrackerSparql *sparql,
return FALSE;
sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_INTEGER;
} else if (g_str_equal (function, FN_NS "timezone-from-dateTime")) {
- TrackerVariable *variable;
-
_step (sparql);
- _append_string (sparql, "( ");
+ _append_string (sparql, "SparqlTimezoneDuration( ");
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
_call_rule (sparql, NAMED_RULE_Expression, error);
- variable = _last_node_variable (sparql);
-
- if (!variable) {
- _raise (PARSE, "Expected variable", "fn:timezone-from-dateTime");
- } else {
- _append_string_printf (sparql, " - %s ",
- tracker_variable_get_sql_expression (variable));
- }
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
_append_string (sparql, ") ");
@@ -5294,7 +6962,8 @@ handle_function_call (TrackerSparql *sparql,
convert_to_string = sparql->current_state.convert_to_string;
sparql->current_state.convert_to_string = FALSE;
- if (g_str_has_prefix (function, XSD_NS)) {
+ if (g_str_has_prefix (function, XSD_NS) ||
+ strcmp (function, RDF_NS "langString") == 0) {
handled = handle_type_cast (sparql, function, error);
} else if (g_str_has_prefix (function, FN_NS)) {
handled = handle_xpath_function (sparql, function, error);
@@ -5380,12 +7049,12 @@ helper_translate_date (TrackerSparql *sparql,
GError **error)
{
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
- _append_string_printf (sparql, "strftime (\"%s\", ", format);
+ _append_string_printf (sparql, "strftime (\"%s\", SparqlTimestamp (", format);
_call_rule (sparql, NAMED_RULE_Expression, error);
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
- _append_string (sparql, ", \"unixepoch\") ");
+ _append_string (sparql, "), \"unixepoch\") ");
return TRUE;
}
@@ -5395,20 +7064,20 @@ helper_translate_time (TrackerSparql *sparql,
guint format,
GError **error)
{
+ _append_string (sparql, "CAST (SparqlTimestamp (");
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
_call_rule (sparql, NAMED_RULE_Expression, error);
-
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
switch (format) {
case TIME_FORMAT_SECONDS:
- _append_string (sparql, " % 60 ");
+ _append_string (sparql, ") AS INTEGER) % 60 ");
break;
case TIME_FORMAT_MINUTES:
- _append_string (sparql, " / 60 % 60 ");
+ _append_string (sparql, ") AS INTEGER) / 60 % 60 ");
break;
case TIME_FORMAT_HOURS:
- _append_string (sparql, " / 3600 % 24 ");
+ _append_string (sparql, ") AS INTEGER) / 3600 % 24 ");
break;
default:
g_assert_not_reached ();
@@ -5418,9 +7087,54 @@ helper_translate_time (TrackerSparql *sparql,
}
static gboolean
+helper_datatype (TrackerSparql *sparql,
+ TrackerParserNode *node,
+ GError **error)
+{
+ TrackerStringBuilder *dummy;
+ gboolean retval;
+
+ _append_string (sparql, "SparqlDataType (");
+
+ if (g_node_n_nodes ((GNode *) node, G_TRAVERSE_LEAVES) == 1) {
+ TrackerVariable *var, *type_var;
+ TrackerParserNode *arg;
+
+ arg = tracker_sparql_parser_tree_find_next (node, TRUE);
+ var = _extract_node_variable (arg, sparql);
+
+ if (var) {
+ /* This is a simple is*(?u) statement, check if the variable type is known */
+ type_var = lookup_subvariable (sparql, var, "type");
+
+ if (type_var && tracker_variable_has_bindings (type_var)) {
+ /* Exists and is known, use it */
+ _append_variable_sql (sparql, type_var);
+ _append_string (sparql, ") ");
+
+ return TRUE;
+ }
+ }
+ }
+
+ /* Redirect output to a dummy string, we just care of the parsed expression type */
+ dummy = tracker_string_builder_new ();
+ retval = _postprocess_rule (sparql, node, dummy, error);
+ tracker_string_builder_free (dummy);
+
+ if (!retval)
+ return retval;
+
+ _append_string_printf (sparql, "%d) ", sparql->current_state.expression_type);
+
+ return TRUE;
+}
+
+static gboolean
translate_BuiltInCall (TrackerSparql *sparql,
GError **error)
{
+ TrackerParserNode *node;
gboolean convert_to_string;
const gchar *old_sep;
@@ -5452,11 +7166,25 @@ translate_BuiltInCall (TrackerSparql *sparql,
sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_STRING;
tracker_sparql_swap_builder (sparql, old);
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_DATATYPE)) {
- _unimplemented ("DATATYPE");
- } else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_IRI)) {
- _unimplemented ("IRI");
- } else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_URI)) {
- _unimplemented ("URI");
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
+ node = _skip_rule (sparql, NAMED_RULE_Expression);
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
+
+ _append_string (sparql, "NULLIF (");
+ if (!helper_datatype (sparql, node, error))
+ return FALSE;
+
+ _append_string (sparql, ", \"" RDFS_NS "Resource\") ");
+ sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_STRING;
+ } else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_URI) ||
+ _accept (sparql, RULE_TYPE_LITERAL, LITERAL_IRI)) {
+ sparql->current_state.convert_to_string = TRUE;
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
+ _append_string (sparql, "SparqlUri (");
+ _call_rule (sparql, NAMED_RULE_Expression, error);
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
+ _append_string (sparql, ") ");
+ sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_STRING;
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_ABS)) {
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
_append_string (sparql, "ABS (");
@@ -5535,9 +7263,21 @@ translate_BuiltInCall (TrackerSparql *sparql,
return FALSE;
sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_DOUBLE;
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_TIMEZONE)) {
- _unimplemented ("TIMEZONE");
+ sparql->current_state.convert_to_string = TRUE;
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
+ _append_string (sparql, "SparqlTimezone (");
+ _call_rule (sparql, NAMED_RULE_Expression, error);
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
+ _append_string (sparql, ") ");
+ sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_STRING;
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_TZ)) {
- _unimplemented ("TZ");
+ sparql->current_state.convert_to_string = TRUE;
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
+ _append_string (sparql, "SparqlTimezoneString (");
+ _call_rule (sparql, NAMED_RULE_Expression, error);
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
+ _append_string (sparql, ") ");
+ sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_STRING;
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_MD5)) {
sparql->current_state.convert_to_string = TRUE;
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
@@ -5580,30 +7320,63 @@ translate_BuiltInCall (TrackerSparql *sparql,
sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_STRING;
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_ISIRI) ||
_accept (sparql, RULE_TYPE_LITERAL, LITERAL_ISURI)) {
- TrackerBinding *binding;
- const gchar *str;
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
+ node = _skip_rule (sparql, NAMED_RULE_Expression);
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
+
+ if (!helper_datatype (sparql, node, error))
+ return FALSE;
+ _append_string (sparql, "== \"" RDFS_NS "Resource\" ");
+ sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_BOOLEAN;
+ } else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_ISBLANK)) {
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
+ node = _skip_rule (sparql, NAMED_RULE_Expression);
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
- _call_rule (sparql, NAMED_RULE_Expression, error);
+ _append_string (sparql, "CASE ");
- str = (sparql->current_state.expression_type == TRACKER_PROPERTY_TYPE_RESOURCE) ? "1" : "0";
+ if (!helper_datatype (sparql, node, error))
+ return FALSE;
- binding = tracker_literal_binding_new (str, NULL);
- tracker_select_context_add_literal_binding (TRACKER_SELECT_CONTEXT (sparql->context),
- TRACKER_LITERAL_BINDING (binding));
- _append_literal_sql (sparql, TRACKER_LITERAL_BINDING (binding));
+ _append_string (sparql, "== \"" RDFS_NS "Resource\" ");
+ _append_string (sparql, "WHEN 1 THEN (SELECT BlankNode FROM Resource WHERE ID = ");
- _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
+ if (!_postprocess_rule (sparql, node, NULL, error))
+ return FALSE;
+
+ _append_string (sparql, ") ELSE NULL END ");
sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_BOOLEAN;
- } else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_ISBLANK)) {
- _unimplemented ("ISBLANK");
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_ISLITERAL)) {
- _unimplemented ("ISLITERAL");
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
+ node = _skip_rule (sparql, NAMED_RULE_Expression);
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
+
+ if (!helper_datatype (sparql, node, error))
+ return FALSE;
+
+ _append_string (sparql, "!= \"" RDFS_NS "Resource\" ");
+ sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_BOOLEAN;
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_ISNUMERIC)) {
- _unimplemented ("ISNUMERIC");
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
+ node = _skip_rule (sparql, NAMED_RULE_Expression);
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
+
+ if (!helper_datatype (sparql, node, error))
+ return FALSE;
+
+ _append_string (sparql, "IN (\"" XSD_NS "integer\", \"" XSD_NS "double\")");
+ sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_BOOLEAN;
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_LANGMATCHES)) {
- _unimplemented ("LANGMATCHES");
+ _append_string (sparql, "SparqlLangMatches (");
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
+ _call_rule (sparql, NAMED_RULE_Expression, error);
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_COMMA);
+ _append_string (sparql, ", ");
+ _call_rule (sparql, NAMED_RULE_Expression, error);
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
+ _append_string (sparql, ") ");
+ sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_BOOLEAN;
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_CONTAINS)) {
/* contains('A','B') => 'A' GLOB '*B*' */
sparql->current_state.convert_to_string = TRUE;
@@ -5675,9 +7448,67 @@ translate_BuiltInCall (TrackerSparql *sparql,
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
_append_string (sparql, ") ");
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_STRLANG)) {
- _unimplemented ("STRLANG");
+ _append_string (sparql, "SparqlStrLang (");
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
+ _call_rule (sparql, NAMED_RULE_Expression, error);
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_COMMA);
+ _append_string (sparql, ", ");
+ _call_rule (sparql, NAMED_RULE_Expression, error);
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
+ _append_string (sparql, ") ");
+ sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_LANGSTRING;
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_STRDT)) {
- _unimplemented ("STRDT");
+ TrackerParserNode *expr, *node, *iri_node = NULL;
+ TrackerPropertyType type;
+ gchar *type_iri;
+ gboolean retval = TRUE;
+
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
+ expr = _skip_rule (sparql, NAMED_RULE_Expression);
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_COMMA);
+ node = _skip_rule (sparql, NAMED_RULE_Expression);
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
+
+ if (g_node_n_nodes ((GNode *) node, G_TRAVERSE_LEAVES) == 1)
+ iri_node = tracker_sparql_parser_tree_find_first (node, TRUE);
+
+ if (!iri_node)
+ _raise (PARSE, "Second argument must be IRI", "STRDT");
+
+ type_iri = _extract_node_string (iri_node, sparql);
+ type = rdf_type_to_property_type (type_iri);
+ g_free (type_iri);
+
+ switch (type) {
+ case TRACKER_PROPERTY_TYPE_UNKNOWN:
+ case TRACKER_PROPERTY_TYPE_STRING:
+ case TRACKER_PROPERTY_TYPE_LANGSTRING:
+ case TRACKER_PROPERTY_TYPE_RESOURCE:
+ retval = _postprocess_rule (sparql, expr, NULL, error);
+ break;
+ case TRACKER_PROPERTY_TYPE_BOOLEAN:
+ retval = _postprocess_rule (sparql, expr, NULL, error);
+ break;
+ case TRACKER_PROPERTY_TYPE_INTEGER:
+ _append_string (sparql, "CAST (");
+ retval = _postprocess_rule (sparql, expr, NULL, error);
+ _append_string (sparql, "AS INTEGER) ");
+ break;
+ case TRACKER_PROPERTY_TYPE_DOUBLE:
+ _append_string (sparql, "CAST (");
+ retval = _postprocess_rule (sparql, expr, NULL, error);
+ _append_string (sparql, "AS REAL) ");
+ break;
+ case TRACKER_PROPERTY_TYPE_DATE:
+ case TRACKER_PROPERTY_TYPE_DATETIME:
+ retval = _postprocess_rule (sparql, expr, NULL, error);
+ break;
+ }
+
+ if (!retval)
+ return FALSE;
+
+ sparql->current_state.expression_type = type;
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_SAMETERM)) {
_expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
_append_string (sparql, " ( ");
@@ -5711,7 +7542,17 @@ translate_BuiltInCall (TrackerSparql *sparql,
_append_string (sparql, "IS NOT NULL) ");
sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_BOOLEAN;
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_BNODE)) {
- _unimplemented ("BNODE");
+ if (_accept (sparql, RULE_TYPE_TERMINAL, TERMINAL_TYPE_NIL)) {
+ _append_string (sparql, "SparqlUUID(\"urn:bnode\") ");
+ } else {
+ _append_string (sparql, "SparqlBNODE(");
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
+ _call_rule (sparql, NAMED_RULE_Expression, error);
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
+ _append_string (sparql, ") ");
+ }
+
+ sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_STRING;
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_RAND)) {
_expect (sparql, RULE_TYPE_TERMINAL, TERMINAL_TYPE_NIL);
_append_string (sparql, "SparqlRand() ");
@@ -5722,10 +7563,12 @@ translate_BuiltInCall (TrackerSparql *sparql,
sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_DATETIME;
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_UUID)) {
_expect (sparql, RULE_TYPE_TERMINAL, TERMINAL_TYPE_NIL);
- _unimplemented ("UUID");
+ _append_string (sparql, "SparqlUUID(\"urn:uuid\") ");
+ sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_STRING;
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_STRUUID)) {
_expect (sparql, RULE_TYPE_TERMINAL, TERMINAL_TYPE_NIL);
- _unimplemented ("STRUUID");
+ _append_string (sparql, "SparqlUUID() ");
+ sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_STRING;
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_CONCAT)) {
sparql->current_state.convert_to_string = TRUE;
old_sep = tracker_sparql_swap_current_expression_list_separator (sparql, " || ");
@@ -5968,7 +7811,13 @@ translate_Aggregate (TrackerSparql *sparql,
_append_string (sparql, ") ");
sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_STRING;
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_SAMPLE)) {
- _unimplemented ("SAMPLE");
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_OPEN_PARENS);
+
+ if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_DISTINCT))
+ _append_string (sparql, "DISTINCT ");
+
+ _call_rule (sparql, NAMED_RULE_Expression, error);
+ _expect (sparql, RULE_TYPE_LITERAL, LITERAL_CLOSE_PARENS);
} else {
g_assert_not_reached ();
}
@@ -5980,59 +7829,94 @@ static gboolean
translate_RDFLiteral (TrackerSparql *sparql,
GError **error)
{
+ TrackerParserNode *node;
TrackerBinding *binding;
+ gchar *str, *langtag = NULL, *cast = NULL;
+ gboolean is_parameter;
+ const TrackerGrammarRule *rule;
+ TrackerPropertyType type;
/* RDFLiteral ::= String ( LANGTAG | ( '^^' iri ) )?
*/
_call_rule (sparql, NAMED_RULE_String, error);
- binding = _convert_terminal (sparql);
+ node = sparql->current_state.prev_node;
+ str = _extract_node_string (node, sparql);
+ rule = tracker_parser_node_get_rule (node);
+ is_parameter = tracker_grammar_rule_is_a (rule, RULE_TYPE_TERMINAL,
+ TERMINAL_TYPE_PARAMETERIZED_VAR);
if (_accept (sparql, RULE_TYPE_TERMINAL, TERMINAL_TYPE_LANGTAG)) {
- g_object_unref (binding);
- _unimplemented ("LANGTAG");
+ langtag = _dup_last_string (sparql);
+ sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_LANGSTRING;
} else if (_accept (sparql, RULE_TYPE_LITERAL, LITERAL_DOUBLE_CIRCUMFLEX)) {
- gchar *cast;
-
_call_rule (sparql, NAMED_RULE_iri, error);
cast = _dup_last_string (sparql);
+ }
- if (g_str_equal (cast, XSD_NS "boolean")) {
- sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_BOOLEAN;
- } else if (g_str_equal (cast, XSD_NS "integer") ||
- g_str_equal (cast, XSD_NS "nonPositiveInteger") ||
- g_str_equal (cast, XSD_NS "negativeInteger") ||
- g_str_equal (cast, XSD_NS "long") ||
- g_str_equal (cast, XSD_NS "int") ||
- g_str_equal (cast, XSD_NS "short") ||
- g_str_equal (cast, XSD_NS "byte") ||
- g_str_equal (cast, XSD_NS "nonNegativeInteger") ||
- g_str_equal (cast, XSD_NS "unsignedLong") ||
- g_str_equal (cast, XSD_NS "unsignedInt") ||
- g_str_equal (cast, XSD_NS "unsignedShort") ||
- g_str_equal (cast, XSD_NS "unsignedByte") ||
- g_str_equal (cast, XSD_NS "positiveInteger")) {
- sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_INTEGER;
- } else if (g_str_equal (cast, XSD_NS "double")) {
- sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_DOUBLE;
- } else if (g_str_equal (cast, XSD_NS "date")) {
- sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_DATE;
- } else if (g_str_equal (cast, XSD_NS "dateTime")) {
- sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_DATETIME;
- } else {
- sparql->current_state.expression_type = TRACKER_PROPERTY_TYPE_STRING;
+ if (is_parameter && (langtag || cast)) {
+ g_free (str);
+ g_free (langtag);
+ g_free (cast);
+ _raise (PARSE, "Parameter cannot have LANGTAG/^^ modifiers", "RDFLiteral");
+ }
+
+ if (is_parameter) {
+ binding = tracker_parameter_binding_new (str, NULL);
+ } else {
+ GString *langstr;
+ GBytes *bytes;
+
+ langstr = g_string_new (str);
+
+ if (langtag) {
+ g_string_append_c (langstr, '\0');
+ g_string_append_printf (langstr, "%s", &langtag[1]);
}
- g_free (cast);
+ bytes = g_bytes_new_take (langstr->str,
+ langstr->len + 1);
+ g_string_free (langstr, FALSE);
+
+ binding = tracker_literal_binding_new (bytes, NULL);
+ g_bytes_unref (bytes);
}
- tracker_binding_set_data_type (binding, sparql->current_state.expression_type);
+ if (cast) {
+ type = rdf_type_to_property_type (cast);
+ } else if (langtag) {
+ type = TRACKER_PROPERTY_TYPE_LANGSTRING;
+ } else {
+ type = TRACKER_PROPERTY_TYPE_STRING;
+ }
- if (sparql->current_state.type == TRACKER_SPARQL_TYPE_SELECT) {
+ sparql->current_state.expression_type = type;
+ tracker_binding_set_data_type (binding, type);
+
+ if (!is_parameter &&
+ (sparql->current_state.type == TRACKER_SPARQL_TYPE_SELECT ||
+ sparql->current_state.type == TRACKER_SPARQL_TYPE_CONSTRUCT)) {
tracker_select_context_add_literal_binding (TRACKER_SELECT_CONTEXT (sparql->context),
TRACKER_LITERAL_BINDING (binding));
}
+ if (sparql->current_state.token) {
+ if (is_parameter) {
+ tracker_token_parameter_init (sparql->current_state.token,
+ TRACKER_PARAMETER_BINDING (binding)->name);
+ } else {
+ gconstpointer data;
+ gsize len;
+
+ data = g_bytes_get_data (TRACKER_LITERAL_BINDING (binding)->bytes, &len);
+ tracker_token_literal_init (sparql->current_state.token,
+ data, len);
+ }
+ }
+
g_object_unref (binding);
+ g_free (langtag);
+ g_free (cast);
+ g_free (str);
return TRUE;
}
@@ -6222,10 +8106,11 @@ translate_BlankNode (TrackerSparql *sparql,
iface = tracker_data_manager_get_writable_db_interface (sparql->data_manager);
- if (sparql->current_state.type != TRACKER_SPARQL_TYPE_SELECT) {
+ if (sparql->current_state.type != TRACKER_SPARQL_TYPE_SELECT &&
+ sparql->current_state.type != TRACKER_SPARQL_TYPE_CONSTRUCT) {
if (_accept (sparql, RULE_TYPE_TERMINAL, TERMINAL_TYPE_ANON)) {
bnode_id = tracker_data_query_unused_uuid (sparql->data_manager, iface);
- tracker_token_literal_init (sparql->current_state.token, bnode_id);
+ tracker_token_literal_init (sparql->current_state.token, bnode_id, -1);
g_free (bnode_id);
} else if (_accept (sparql, RULE_TYPE_TERMINAL, TERMINAL_TYPE_BLANK_NODE_LABEL)) {
gchar *str;
@@ -6243,9 +8128,9 @@ translate_BlankNode (TrackerSparql *sparql,
g_variant_builder_add (sparql->blank_nodes, "{ss}", str, bnode_id);
}
- tracker_token_literal_init (sparql->current_state.token, bnode_id);
+ tracker_token_literal_init (sparql->current_state.token, bnode_id, -1);
} else {
- tracker_token_literal_init (sparql->current_state.token, str);
+ tracker_token_literal_init (sparql->current_state.token, str, -1);
}
g_free (str);
@@ -6467,9 +8352,16 @@ tracker_sparql_init (TrackerSparql *sparql)
{
sparql->prefix_map = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, g_free);
- sparql->parameters = g_hash_table_new (g_str_hash, g_str_equal);
+ sparql->cached_bindings = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_object_unref);
+ sparql->parameters = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_object_unref);
+ sparql->union_views = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, NULL);
sparql->var_names = g_ptr_array_new_with_free_func (g_free);
sparql->var_types = g_array_new (FALSE, FALSE, sizeof (TrackerPropertyType));
+ sparql->anon_graphs = g_ptr_array_new_with_free_func (g_free);
+ sparql->named_graphs = g_ptr_array_new_with_free_func (g_free);
sparql->cacheable = TRUE;
}
@@ -6491,18 +8383,16 @@ tracker_sparql_new (TrackerDataManager *manager,
&sparql->parser_error);
if (tree) {
sparql->tree = tree;
- sparql->sql = tracker_string_builder_new ();
-
sparql->current_state.node = tracker_node_tree_get_root (sparql->tree);
- sparql->current_state.sql = sparql->sql;
- sparql->current_state.with_clauses = _prepend_placeholder (sparql);
+ tracker_sparql_init_string_builder (sparql);
}
return sparql;
}
static TrackerDBStatement *
-prepare_query (TrackerDBInterface *iface,
+prepare_query (TrackerSparql *sparql,
+ TrackerDBInterface *iface,
TrackerStringBuilder *str,
GPtrArray *literals,
GHashTable *parameters,
@@ -6513,6 +8403,10 @@ prepare_query (TrackerDBInterface *iface,
gchar *query;
guint i;
+ if (!tracker_data_manager_update_union_views (sparql->data_manager, iface,
+ sparql->union_views, error))
+ return NULL;
+
query = tracker_string_builder_to_string (str);
stmt = tracker_db_interface_create_statement (iface,
cached ?
@@ -6588,6 +8482,9 @@ prepare_query (TrackerDBInterface *iface,
tracker_db_statement_bind_double (stmt, i, datetime);
} else if (prop_type == TRACKER_PROPERTY_TYPE_INTEGER) {
tracker_db_statement_bind_int (stmt, i, atoi (binding->literal));
+ } else if (prop_type == TRACKER_PROPERTY_TYPE_LANGSTRING &&
+ g_bytes_get_size (binding->bytes) > strlen (binding->literal) + 1) {
+ tracker_db_statement_bind_bytes (stmt, i, binding->bytes);
} else {
tracker_db_statement_bind_text (stmt, i, binding->literal);
}
@@ -6617,7 +8514,7 @@ tracker_sparql_execute_cursor (TrackerSparql *sparql,
return NULL;
iface = tracker_data_manager_get_db_interface (sparql->data_manager);
- stmt = prepare_query (iface, sparql->sql,
+ stmt = prepare_query (sparql, iface, sparql->sql,
TRACKER_SELECT_CONTEXT (sparql->context)->literal_bindings,
parameters,
sparql->cacheable,
@@ -6669,11 +8566,8 @@ tracker_sparql_new_update (TrackerDataManager *manager,
if (tree) {
sparql->tree = tree;
- sparql->sql = tracker_string_builder_new ();
-
sparql->current_state.node = tracker_node_tree_get_root (sparql->tree);
- sparql->current_state.sql = sparql->sql;
- sparql->current_state.with_clauses = _prepend_placeholder (sparql);
+ tracker_sparql_init_string_builder (sparql);
}
return sparql;
diff --git a/src/libtracker-data/tracker-turtle-reader.vala b/src/libtracker-data/tracker-turtle-reader.vala
index 662ce8f43..788eb571d 100644
--- a/src/libtracker-data/tracker-turtle-reader.vala
+++ b/src/libtracker-data/tracker-turtle-reader.vala
@@ -381,30 +381,6 @@ public class Tracker.TurtleReader : Object {
}
}
- public static void load (File file, Data.Update data) throws Error, FileError, Sparql.Error, DateError, DBInterfaceError {
- try {
- data.begin_transaction ();
-
- var reader = new TurtleReader (file);
- while (reader.next ()) {
- if (reader.object_is_uri) {
- data.insert_statement_with_uri (reader.graph, reader.subject, reader.predicate, reader.object);
- } else {
- data.insert_statement_with_string (reader.graph, reader.subject, reader.predicate, reader.object);
- }
- data.update_buffer_might_flush ();
- }
-
- data.commit_transaction ();
- } catch (Sparql.Error e) {
- data.rollback_transaction ();
- throw e;
- } catch (DBInterfaceError e) {
- data.rollback_transaction ();
- throw e;
- }
- }
-
[CCode (cname = "uuid_generate")]
public extern static void uuid_generate ([CCode (array_length = false)] uchar[] uuid);
}
diff --git a/src/libtracker-data/tracker-uuid.c b/src/libtracker-data/tracker-uuid.c
index e7962bcfd..f2b18967a 100644
--- a/src/libtracker-data/tracker-uuid.c
+++ b/src/libtracker-data/tracker-uuid.c
@@ -21,26 +21,34 @@
#include "config.h"
#include "tracker-uuid.h"
+#define DEFAULT_PREFIX "urn:uuid"
+
#if ! GLIB_CHECK_VERSION (2, 52, 0)
#include <uuid/uuid.h>
#endif
gchar *
-tracker_generate_uuid (void)
+tracker_generate_uuid (const gchar *uri_prefix)
{
gchar *result;
#if GLIB_CHECK_VERSION (2, 52, 0)
- gchar *uuid = g_uuid_string_random ();
- result = g_strdup_printf ("urn:uuid:%s", uuid);
- g_free (uuid);
+ result = g_uuid_string_random ();
#else
uuid_t base = { 0, };
gchar uuid[37];
uuid_generate (base);
uuid_unparse_lower (base, uuid);
- result = g_strdup_printf ("urn:uuid:%s", uuid);
+ result = g_strdup_printf (uuid);
#endif
- return result;
+ if (uri_prefix) {
+ gchar *uri;
+
+ uri = g_strdup_printf ("%s:%s", uri_prefix, result);
+ g_free (result);
+ return uri;
+ } else {
+ return result;
+ }
}
diff --git a/src/libtracker-data/tracker-uuid.h b/src/libtracker-data/tracker-uuid.h
index 744171e32..0393b7a5f 100644
--- a/src/libtracker-data/tracker-uuid.h
+++ b/src/libtracker-data/tracker-uuid.h
@@ -23,6 +23,6 @@
#include <glib.h>
-gchar * tracker_generate_uuid (void);
+gchar * tracker_generate_uuid (const gchar *uri_prefix);
#endif /* __TRACKER_UUID_H__ */
diff --git a/src/libtracker-data/tracker-vtab-service.c b/src/libtracker-data/tracker-vtab-service.c
new file mode 100644
index 000000000..5f93711cb
--- /dev/null
+++ b/src/libtracker-data/tracker-vtab-service.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2019, Red Hat Inc.
+ *
+ * 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.
+ *
+ * Author: Carlos Garnacho <carlosg@gnome.org>
+ */
+#include "config.h"
+
+#include "tracker-vtab-service.h"
+#include <libtracker-sparql/tracker-sparql.h>
+
+#define N_VIRTUAL_COLUMNS 100
+#define COL_SERVICE 101
+#define COL_QUERY 102
+#define COL_SILENT 103
+
+typedef struct {
+ sqlite3 *db;
+} TrackerServiceModule;
+
+typedef struct {
+ struct sqlite3_vtab parent;
+ TrackerServiceModule *module;
+ GList *cursors;
+} TrackerServiceVTab;
+
+typedef struct {
+ struct sqlite3_vtab_cursor parent;
+ TrackerServiceVTab *vtab;
+ TrackerSparqlConnection *conn;
+ TrackerSparqlCursor *sparql_cursor;
+ gchar *service;
+ gchar *query;
+ guint64 rowid;
+ guint silent : 1;
+ guint finished : 1;
+} TrackerServiceCursor;
+
+typedef struct {
+ int column;
+ int op;
+} ConstraintData;
+
+static void
+tracker_service_module_free (gpointer data)
+{
+ TrackerServiceModule *module = data;
+
+ g_free (module);
+}
+
+static void
+tracker_service_vtab_free (gpointer data)
+{
+ TrackerServiceVTab *vtab = data;
+
+ g_list_free (vtab->cursors);
+ g_free (vtab);
+}
+
+static void
+tracker_service_cursor_free (gpointer data)
+{
+ TrackerServiceCursor *cursor = data;
+
+ g_free (cursor->service);
+ g_free (cursor->query);
+ g_clear_object (&cursor->conn);
+ g_clear_object (&cursor->sparql_cursor);
+
+ g_free (cursor);
+}
+
+static int
+service_create (sqlite3 *db,
+ gpointer data,
+ int argc,
+ const char *const *argv,
+ sqlite3_vtab **vtab_out,
+ char **err_out)
+{
+ TrackerServiceModule *module = data;
+ TrackerServiceVTab *vtab;
+ GString *str;
+ gint i, rc;
+
+ vtab = g_new0 (TrackerServiceVTab, 1);
+ vtab->module = module;
+
+ str = g_string_new ("CREATE TABLE x(\n");
+
+ for (i = 0; i <= N_VIRTUAL_COLUMNS; i++)
+ g_string_append_printf (str, "col%d TEXT, ", i);
+
+ g_string_append (str,
+ "service TEXT HIDDEN, "
+ "query TEXT HIDDEN, "
+ "silent INTEGER HIDDEN)");
+ rc = sqlite3_declare_vtab (module->db, str->str);
+ g_string_free (str, TRUE);
+
+ if (rc == SQLITE_OK)
+ *vtab_out = &vtab->parent;
+ else
+ g_free (vtab);
+
+ return rc;
+}
+
+static int
+service_best_index (sqlite3_vtab *vtab,
+ sqlite3_index_info *info)
+{
+ int i, argv_idx = 1;
+ ConstraintData *data;
+
+ data = sqlite3_malloc (sizeof (ConstraintData) * info->nConstraint);
+ bzero (data, sizeof (ConstraintData) * info->nConstraint);
+
+ for (i = 0; i < info->nConstraint; i++) {
+ if (!info->aConstraint[i].usable)
+ continue;
+
+ if (info->aConstraint[i].iColumn != COL_SERVICE &&
+ info->aConstraint[i].iColumn != COL_QUERY &&
+ info->aConstraint[i].iColumn != COL_SILENT) {
+ info->aConstraintUsage[i].argvIndex = -1;
+ continue;
+ }
+
+ if (info->aConstraint[i].op != SQLITE_INDEX_CONSTRAINT_EQ)
+ goto error;
+
+ data[i].column = info->aConstraint[i].iColumn;
+ data[i].op = info->aConstraint[i].op;
+
+ info->aConstraintUsage[i].argvIndex = argv_idx;
+ info->aConstraintUsage[i].omit = FALSE;
+ argv_idx++;
+ }
+
+ info->orderByConsumed = FALSE;
+ info->idxStr = (char *) data;
+ info->needToFreeIdxStr = TRUE;
+
+ return SQLITE_OK;
+
+error:
+ sqlite3_free (data);
+ return SQLITE_ERROR;
+}
+
+static int
+service_destroy (sqlite3_vtab *vtab)
+{
+ tracker_service_vtab_free (vtab);
+ return SQLITE_OK;
+}
+
+static int
+service_open (sqlite3_vtab *vtab_sqlite,
+ sqlite3_vtab_cursor **cursor_ret)
+{
+ TrackerServiceVTab *vtab = (TrackerServiceVTab *) vtab_sqlite;
+ TrackerServiceCursor *cursor;
+
+ cursor = g_new0 (TrackerServiceCursor, 1);
+ cursor->vtab = vtab;
+
+ vtab->cursors = g_list_prepend (vtab->cursors, cursor);
+ *cursor_ret = (sqlite3_vtab_cursor *) cursor;
+
+ return SQLITE_OK;
+}
+
+static int
+service_close (sqlite3_vtab_cursor *vtab_cursor)
+{
+ TrackerServiceCursor *cursor = (TrackerServiceCursor *) vtab_cursor;
+ TrackerServiceVTab *vtab = cursor->vtab;
+
+ vtab->cursors = g_list_remove (vtab->cursors, cursor);
+ tracker_service_cursor_free (cursor);
+ return SQLITE_OK;
+}
+
+static int
+service_filter (sqlite3_vtab_cursor *vtab_cursor,
+ int idx,
+ const char *idx_str,
+ int argc,
+ sqlite3_value **argv)
+{
+ TrackerServiceCursor *cursor = (TrackerServiceCursor *) vtab_cursor;
+ const ConstraintData *constraints = (const ConstraintData *) idx_str;
+ gchar *uri_scheme = NULL;
+ GError *error = NULL;
+ gint i;
+
+ cursor->finished = FALSE;
+ cursor->rowid = 0;
+
+ for (i = 0; i < argc; i++) {
+ if (constraints[i].column == COL_SERVICE)
+ cursor->service = g_strdup (sqlite3_value_text (argv[i]));
+ if (constraints[i].column == COL_QUERY)
+ cursor->query = g_strdup (sqlite3_value_text (argv[i]));
+ if (constraints[i].column == COL_SILENT)
+ cursor->silent = !!sqlite3_value_int (argv[i]);
+ }
+
+ if (!cursor->service || !cursor->query)
+ return SQLITE_ERROR;
+
+ uri_scheme = g_uri_parse_scheme (cursor->service);
+ if (g_strcmp0 (uri_scheme, "dbus") == 0) {
+ const gchar *bus_name = &cursor->service[strlen (uri_scheme) + 1];
+
+ cursor->conn = tracker_sparql_connection_bus_new (bus_name, NULL, &error);
+ if (!cursor->conn)
+ goto fail;
+ } else if (g_strcmp0 (uri_scheme, "http") == 0) {
+ cursor->conn = tracker_sparql_connection_remote_new (cursor->service);
+ }
+
+ if (!cursor->conn) {
+ g_set_error (&error,
+ TRACKER_SPARQL_ERROR,
+ TRACKER_SPARQL_ERROR_UNSUPPORTED,
+ "Unsupported uri '%s'",
+ cursor->service);
+ goto fail;
+ }
+
+ cursor->sparql_cursor = tracker_sparql_connection_query (cursor->conn,
+ cursor->query,
+ NULL, &error);
+ if (error)
+ goto fail;
+
+ cursor->finished =
+ !tracker_sparql_cursor_next (cursor->sparql_cursor, NULL, &error);
+
+ if (error)
+ goto fail;
+
+ g_free (uri_scheme);
+
+ return SQLITE_OK;
+
+fail:
+ g_free (uri_scheme);
+
+ if (cursor->silent) {
+ cursor->finished = TRUE;
+ g_error_free (error);
+ return SQLITE_OK;
+ } else {
+ g_warning ("Could not create remote cursor: %s\n", error->message);
+ g_error_free (error);
+ return SQLITE_ERROR;
+ }
+}
+
+static int
+service_next (sqlite3_vtab_cursor *vtab_cursor)
+{
+ TrackerServiceCursor *cursor = (TrackerServiceCursor *) vtab_cursor;
+
+ if (!cursor->sparql_cursor)
+ return SQLITE_ERROR;
+
+ cursor->finished =
+ !tracker_sparql_cursor_next (cursor->sparql_cursor, NULL, NULL);
+
+ cursor->rowid++;
+ return SQLITE_OK;
+}
+
+static int
+service_eof (sqlite3_vtab_cursor *vtab_cursor)
+{
+ TrackerServiceCursor *cursor = (TrackerServiceCursor *) vtab_cursor;
+
+ return cursor->finished;
+}
+
+static int
+service_column (sqlite3_vtab_cursor *vtab_cursor,
+ sqlite3_context *context,
+ int n_col)
+{
+ TrackerServiceCursor *cursor = (TrackerServiceCursor *) vtab_cursor;
+ const gchar *str;
+
+ if (n_col == COL_SERVICE) {
+ sqlite3_result_text (context, cursor->service, -1, NULL);
+ } else if (n_col == COL_QUERY) {
+ sqlite3_result_text (context, cursor->query, -1, NULL);
+ } else if (n_col == COL_SILENT) {
+ sqlite3_result_int (context, cursor->silent);
+ } else if (n_col < tracker_sparql_cursor_get_n_columns (cursor->sparql_cursor)) {
+ /* FIXME: Handle other types better */
+ str = tracker_sparql_cursor_get_string (cursor->sparql_cursor, n_col, NULL);
+ sqlite3_result_text (context, g_strdup (str), -1, g_free);
+ } else {
+ sqlite3_result_null (context);
+ }
+
+ return SQLITE_OK;
+}
+
+static int
+service_rowid (sqlite3_vtab_cursor *vtab_cursor,
+ sqlite_int64 *rowid_out)
+{
+ TrackerServiceCursor *cursor = (TrackerServiceCursor *) vtab_cursor;
+
+ *rowid_out = cursor->rowid;
+ return SQLITE_OK;
+}
+
+void
+tracker_vtab_service_init (sqlite3 *db,
+ TrackerOntologies *ontologies)
+{
+ TrackerServiceModule *module;
+ static const sqlite3_module service_module = {
+ 2, /* version */
+ service_create,
+ service_create,
+ service_best_index,
+ service_destroy,
+ service_destroy,
+ service_open,
+ service_close,
+ service_filter,
+ service_next,
+ service_eof,
+ service_column,
+ service_rowid,
+ NULL, /* update */
+ NULL, /* begin */
+ NULL, /* sync */
+ NULL, /* commit */
+ NULL, /* rollback */
+ NULL, /* find function */
+ NULL, /* rename */
+ NULL, /* savepoint */
+ NULL, /* release */
+ NULL, /* rollback to */
+ };
+
+ module = g_new0 (TrackerServiceModule, 1);
+ module->db = db;
+ sqlite3_create_module_v2 (db, "tracker_service", &service_module,
+ module, tracker_service_module_free);
+}
diff --git a/src/libtracker-data/tracker-vtab-service.h b/src/libtracker-data/tracker-vtab-service.h
new file mode 100644
index 000000000..9b85f29c7
--- /dev/null
+++ b/src/libtracker-data/tracker-vtab-service.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019, Red Hat Inc.
+ *
+ * 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.
+ *
+ * Author: Carlos Garnacho <carlosg@gnome.org>
+ */
+#include <sqlite3.h>
+#include "tracker-ontologies.h"
+
+#ifndef __TRACKER_VTAB_SERVICE_H__
+#define __TRACKER_VTAB_SERVICE_H__
+
+void tracker_vtab_service_init (sqlite3 *db,
+ TrackerOntologies *ontologies);
+
+#endif /* __TRACKER_VTAB_SERVICE_H__ */
diff --git a/src/libtracker-data/tracker-vtab-triples.c b/src/libtracker-data/tracker-vtab-triples.c
index 775e95b32..c1f7227f3 100644
--- a/src/libtracker-data/tracker-vtab-triples.c
+++ b/src/libtracker-data/tracker-vtab-triples.c
@@ -37,6 +37,7 @@ enum {
COL_SUBJECT,
COL_PREDICATE,
COL_OBJECT,
+ COL_OBJECT_TYPE,
N_COLS
};
@@ -140,7 +141,8 @@ triples_connect (sqlite3 *db,
" graph INTEGER,"
" subject INTEGER, "
" predicate INTEGER, "
- " object INTEGER"
+ " object INTEGER, "
+ " object_type INTEGER"
")");
if (rc == SQLITE_OK) {
@@ -180,7 +182,8 @@ triples_best_index (sqlite3_vtab *vtab,
/* We let object be matched in upper layers, where proper
* translation to strings can be done.
*/
- if (info->aConstraint[i].iColumn == COL_OBJECT)
+ if (info->aConstraint[i].iColumn == COL_OBJECT ||
+ info->aConstraint[i].iColumn == COL_OBJECT_TYPE)
continue;
if (info->aConstraint[i].iColumn == COL_ROWID)
@@ -284,6 +287,7 @@ convert_to_string (const gchar *table_name,
{
switch (type) {
case TRACKER_PROPERTY_TYPE_STRING:
+ case TRACKER_PROPERTY_TYPE_LANGSTRING:
case TRACKER_PROPERTY_TYPE_INTEGER:
return g_strdup_printf ("t.\"%s\"", table_name);
case TRACKER_PROPERTY_TYPE_RESOURCE:
@@ -362,20 +366,19 @@ init_stmt (TrackerTriplesCursor *cursor)
sql = g_string_new (NULL);
g_string_append_printf (sql,
- "SELECT t.\"%s:graph\", t.ID, "
+ "SELECT t.graph, t.ID, "
" (SELECT ID From Resource WHERE Uri = \"%s\"), "
- " %s "
- "FROM \"%s\" AS t "
+ " %s, "
+ " %d "
+ "FROM \"unionGraph_%s\" AS t "
"WHERE 1 ",
- tracker_property_get_name (property),
tracker_property_get_uri (property),
string_expr,
+ tracker_property_get_data_type (property),
tracker_property_get_table_name (property));
if (cursor->match.graph) {
- g_string_append_printf (sql,
- "AND t.\"%s:graph\" ",
- tracker_property_get_name (property));
+ g_string_append (sql, "AND t.graph ");
add_arg_check (sql, cursor->match.graph,
!!(cursor->match.idxFlags & IDX_MATCH_GRAPH_NEG),
"@g");
diff --git a/src/libtracker-direct/tracker-direct.c b/src/libtracker-direct/tracker-direct.c
index b9443900f..3452579c7 100644
--- a/src/libtracker-direct/tracker-direct.c
+++ b/src/libtracker-direct/tracker-direct.c
@@ -151,7 +151,7 @@ update_thread_func (gpointer data,
destroy_notify = (GDestroyNotify) g_variant_unref;
break;
case TASK_TYPE_TURTLE:
- tracker_data_load_turtle_file (tracker_data, task_data->data.turtle_file, &error);
+ tracker_data_load_turtle_file (tracker_data, task_data->data.turtle_file, NULL, &error);
break;
}
@@ -261,7 +261,7 @@ tracker_direct_connection_initable_init (GInitable *initable,
priv->data_manager = tracker_data_manager_new (db_flags | default_flags, priv->store,
priv->journal, priv->ontology,
- FALSE, FALSE, 100, 100);
+ FALSE, 100, 100);
if (!g_initable_init (G_INITABLE (priv->data_manager), cancellable, error)) {
g_clear_object (&priv->data_manager);
return FALSE;
@@ -350,8 +350,10 @@ tracker_direct_connection_finalize (GObject *object)
if (priv->select_pool)
g_thread_pool_free (priv->select_pool, TRUE, FALSE);
- if (priv->data_manager)
+ if (priv->data_manager) {
tracker_data_manager_shutdown (priv->data_manager);
+ g_clear_object (&priv->data_manager);
+ }
g_clear_object (&priv->store);
g_clear_object (&priv->journal);
@@ -574,9 +576,12 @@ update_array_async_thread_func (GTask *task,
if (!error) {
g_task_return_pointer (task, errors,
(GDestroyNotify) g_ptr_array_unref);
+ g_object_unref (task);
return;
}
+ g_error_free (error);
+
/* Slow path, perform updates one by one */
for (i = 0; updates[i]; i++) {
GError **err = NULL;
@@ -589,6 +594,7 @@ update_array_async_thread_func (GTask *task,
g_task_return_pointer (task, errors,
(GDestroyNotify) g_ptr_array_unref);
+ g_object_unref (task);
}
static void
@@ -696,7 +702,7 @@ tracker_direct_connection_load (TrackerSparqlConnection *self,
g_mutex_lock (&priv->mutex);
data = tracker_data_manager_get_data (priv->data_manager);
- tracker_data_load_turtle_file (data, file, error);
+ tracker_data_load_turtle_file (data, file, NULL, error);
g_mutex_unlock (&priv->mutex);
}
diff --git a/src/libtracker-direct/tracker-direct.vapi b/src/libtracker-direct/tracker-direct.vapi
index df15c5890..8839eafae 100644
--- a/src/libtracker-direct/tracker-direct.vapi
+++ b/src/libtracker-direct/tracker-direct.vapi
@@ -4,7 +4,7 @@ namespace Tracker {
[CCode (cheader_filename = "libtracker-direct/tracker-direct.h")]
public class Connection : Tracker.Sparql.Connection, GLib.Initable, GLib.AsyncInitable {
public Connection (Tracker.Sparql.ConnectionFlags connection_flags, GLib.File loc, GLib.File? journal, GLib.File? ontology) throws Tracker.Sparql.Error, GLib.IOError, GLib.DBusError;
- public Tracker.Data.Manager get_data_manager ();
+ public unowned Tracker.Data.Manager get_data_manager ();
public void sync ();
public static void set_default_flags (Tracker.DBManagerFlags flags);
}
diff --git a/src/libtracker-fts/tracker-fts.c b/src/libtracker-fts/tracker-fts.c
index 49ba4d362..4abc14121 100644
--- a/src/libtracker-fts/tracker-fts.c
+++ b/src/libtracker-fts/tracker-fts.c
@@ -40,17 +40,20 @@ get_fts_properties (GHashTable *tables)
{
GList *table_columns, *columns;
gchar **property_names;
- GHashTableIter iter;
+ GList *keys, *l;
columns = NULL;
- g_hash_table_iter_init (&iter, tables);
+ keys = g_hash_table_get_keys (tables);
+ keys = g_list_sort (keys, (GCompareFunc) strcmp);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &table_columns)) {
+ for (l = keys; l; l = l->next) {
+ table_columns = g_hash_table_lookup (tables, l->data);
columns = g_list_concat (columns, g_list_copy (table_columns));
}
property_names = tracker_glist_to_string_list (columns);
g_list_free (columns);
+ g_list_free (keys);
return property_names;
}
@@ -79,31 +82,37 @@ tracker_fts_init_db (sqlite3 *db,
}
gboolean
-tracker_fts_create_table (sqlite3 *db,
- gchar *table_name,
- GHashTable *tables,
- GHashTable *grouped_columns)
+tracker_fts_create_table (sqlite3 *db,
+ const gchar *database,
+ gchar *table_name,
+ GHashTable *tables,
+ GHashTable *grouped_columns)
{
GString *str, *from, *fts;
- GHashTableIter iter;
gchar *index_table;
- GList *columns;
+ GList *columns, *keys, *l;
gint rc;
if (g_hash_table_size (tables) == 0)
return TRUE;
/* Create view on tables/columns marked as FTS-indexed */
- g_hash_table_iter_init (&iter, tables);
- str = g_string_new ("CREATE VIEW fts_view AS SELECT Resource.ID as rowid ");
- from = g_string_new ("FROM Resource ");
+ str = g_string_new ("CREATE VIEW ");
+ g_string_append_printf (str, "\"%s\".fts_view AS SELECT \"rdfs:Resource\".ID as rowid ",
+ database);
+ from = g_string_new ("FROM \"rdfs:Resource\" ");
fts = g_string_new ("CREATE VIRTUAL TABLE ");
- g_string_append_printf (fts, "%s USING fts5(content=\"fts_view\", ",
- table_name);
+ g_string_append_printf (fts, "\"%s\".%s USING fts5(content=\"fts_view\", ",
+ database, table_name);
+
+ keys = g_hash_table_get_keys (tables);
+ keys = g_list_sort (keys, (GCompareFunc) strcmp);
+
+ for (l = keys; l; l = l->next) {
+ index_table = l->data;
+ columns = g_hash_table_lookup (tables, l->data);
- while (g_hash_table_iter_next (&iter, (gpointer *) &index_table,
- (gpointer *) &columns)) {
while (columns) {
if (grouped_columns &&
g_hash_table_lookup (grouped_columns, columns->data)) {
@@ -124,11 +133,13 @@ tracker_fts_create_table (sqlite3 *db,
columns = columns->next;
}
- g_string_append_printf (from, "LEFT OUTER JOIN \"%s\" ON "
- " Resource.ID = \"%s\".ID ",
- index_table, index_table);
+ g_string_append_printf (from, "LEFT OUTER JOIN \"%s\".\"%s\" ON "
+ " \"rdfs:Resource\".ID = \"%s\".ID ",
+ database, index_table, index_table);
}
+ g_list_free (keys);
+
g_string_append (str, from->str);
g_string_free (from, TRUE);
@@ -158,8 +169,9 @@ tracker_fts_create_table (sqlite3 *db,
}
gboolean
-tracker_fts_delete_table (sqlite3 *db,
- gchar *table_name)
+tracker_fts_delete_table (sqlite3 *db,
+ const gchar *database,
+ gchar *table_name)
{
gchar *query;
int rc;
@@ -169,7 +181,8 @@ tracker_fts_delete_table (sqlite3 *db,
g_free (query);
if (rc == SQLITE_OK) {
- query = g_strdup_printf ("DROP TABLE %s", table_name);
+ query = g_strdup_printf ("DROP TABLE \"%s\".%s",
+ database, table_name);
sqlite3_exec (db, query, NULL, NULL, NULL);
g_free (query);
}
@@ -178,23 +191,24 @@ tracker_fts_delete_table (sqlite3 *db,
}
gboolean
-tracker_fts_alter_table (sqlite3 *db,
- gchar *table_name,
- GHashTable *tables,
- GHashTable *grouped_columns)
+tracker_fts_alter_table (sqlite3 *db,
+ const gchar *database,
+ gchar *table_name,
+ GHashTable *tables,
+ GHashTable *grouped_columns)
{
gchar *query, *tmp_name;
int rc;
tmp_name = g_strdup_printf ("%s_TMP", table_name);
- if (!tracker_fts_create_table (db, tmp_name, tables, grouped_columns)) {
+ if (!tracker_fts_create_table (db, database, tmp_name, tables, grouped_columns)) {
g_free (tmp_name);
return FALSE;
}
- query = g_strdup_printf ("INSERT INTO %s (rowid) SELECT rowid FROM fts_view",
- tmp_name);
+ query = g_strdup_printf ("INSERT INTO \"%s\".%s (rowid) SELECT rowid FROM fts_view",
+ database, tmp_name);
rc = sqlite3_exec (db, query, NULL, NULL, NULL);
g_free (query);
@@ -203,8 +217,8 @@ tracker_fts_alter_table (sqlite3 *db,
return FALSE;
}
- query = g_strdup_printf ("INSERT INTO %s(%s) VALUES('rebuild')",
- tmp_name, tmp_name);
+ query = g_strdup_printf ("INSERT INTO \"%s\".%s(%s) VALUES('rebuild')",
+ database, tmp_name, tmp_name);
rc = sqlite3_exec (db, query, NULL, NULL, NULL);
g_free (query);
@@ -213,8 +227,8 @@ tracker_fts_alter_table (sqlite3 *db,
return FALSE;
}
- query = g_strdup_printf ("ALTER TABLE %s RENAME TO %s",
- tmp_name, table_name);
+ query = g_strdup_printf ("ALTER TABLE \"%s\".%s RENAME TO %s",
+ database, tmp_name, table_name);
rc = sqlite3_exec (db, query, NULL, NULL, NULL);
g_free (query);
g_free (tmp_name);
@@ -224,13 +238,14 @@ tracker_fts_alter_table (sqlite3 *db,
void
tracker_fts_rebuild_tokens (sqlite3 *db,
+ const gchar *database,
const gchar *table_name)
{
gchar *query;
/* This special query rebuilds the tokens in the given FTS table */
- query = g_strdup_printf ("INSERT INTO %s(%s) VALUES('rebuild')",
- table_name, table_name);
+ query = g_strdup_printf ("INSERT INTO \"%s\".%s(%s) VALUES('rebuild')",
+ database, table_name, table_name);
sqlite3_exec(db, query, NULL, NULL, NULL);
g_free (query);
}
diff --git a/src/libtracker-fts/tracker-fts.h b/src/libtracker-fts/tracker-fts.h
index 3bda8cf98..e8d3a91f9 100644
--- a/src/libtracker-fts/tracker-fts.h
+++ b/src/libtracker-fts/tracker-fts.h
@@ -32,17 +32,21 @@ G_BEGIN_DECLS
gboolean tracker_fts_init_db (sqlite3 *db,
TrackerDBInterface *interface,
GHashTable *tables);
-gboolean tracker_fts_create_table (sqlite3 *db,
- gchar *table_name,
- GHashTable *tables,
- GHashTable *grouped_columns);
-gboolean tracker_fts_delete_table (sqlite3 *db,
- gchar *table_name);
-gboolean tracker_fts_alter_table (sqlite3 *db,
- gchar *table_name,
- GHashTable *tables,
- GHashTable *grouped_columns);
+gboolean tracker_fts_create_table (sqlite3 *db,
+ const gchar *database,
+ gchar *table_name,
+ GHashTable *tables,
+ GHashTable *grouped_columns);
+gboolean tracker_fts_delete_table (sqlite3 *db,
+ const gchar *database,
+ gchar *table_name);
+gboolean tracker_fts_alter_table (sqlite3 *db,
+ const gchar *database,
+ gchar *table_name,
+ GHashTable *tables,
+ GHashTable *grouped_columns);
void tracker_fts_rebuild_tokens (sqlite3 *db,
+ const gchar *database,
const gchar *table_name);
G_END_DECLS
diff --git a/src/libtracker-miner/meson.build b/src/libtracker-miner/meson.build
index a3bd2d9d9..53e230013 100644
--- a/src/libtracker-miner/meson.build
+++ b/src/libtracker-miner/meson.build
@@ -101,12 +101,14 @@ gnome.generate_vapi(
install : true,
)
-configure_file(
- input: 'tracker-miner.pc.in',
- output: 'tracker-miner-@0@.pc'.format(tracker_api_version),
- configuration: conf,
- install: true,
- install_dir: join_paths(get_option('prefix'), get_option('libdir'), 'pkgconfig'))
+pkg.generate(libtracker_miner,
+ description: 'A library to develop tracker data miners',
+ requires: [libtracker_sparql],
+ subdirs: 'tracker-' + tracker_api_version,
+ variables: [
+ 'exec_prefix=${prefix}'
+ ],
+)
install_headers(miner_headers, subdir: 'tracker-@0@/libtracker-miner'.format(tracker_api_version))
diff --git a/src/libtracker-miner/tracker-file-notifier.c b/src/libtracker-miner/tracker-file-notifier.c
index ca915c63b..d1070c14d 100644
--- a/src/libtracker-miner/tracker-file-notifier.c
+++ b/src/libtracker-miner/tracker-file-notifier.c
@@ -792,10 +792,15 @@ sparql_contents_compose_query (GFile *directory)
gchar *sparql, *uri;
uri = g_file_get_uri (directory);
- sparql = g_strdup_printf ("SELECT nie:url(?u) ?u nfo:fileLastModified(?u) "
- " IF (nie:mimeType(?u) = \"inode/directory\", true, false) {"
- " ?u nfo:belongsToContainer ?f . ?f nie:url ?url ."
- " FILTER (?url = \"%s\")"
+ sparql = g_strdup_printf ("SELECT ?url ?u ?lastModified ?isFolder "
+ "FROM <" TRACKER_OWN_GRAPH_URN "> {"
+ " ?u nfo:belongsToContainer ?f ;"
+ " nfo:fileLastModified ?lastModified ;"
+ " nie:url ?url ."
+ " OPTIONAL { ?u nie:mimeType ?mimeType . "
+ " BIND (IF (?mimeType = \"inode/directory\", true, false) AS ?isFolder) } ."
+ " ?f nie:url ?folderUrl ."
+ " FILTER (?folderUrl = \"%s\")"
"}", uri);
g_free (uri);
@@ -885,9 +890,12 @@ sparql_files_compose_query (GFile **files,
gchar *uri;
gint i = 0;
- str = g_string_new ("SELECT ?url ?u nfo:fileLastModified(?u) {"
- " ?u a rdfs:Resource ; nie:url ?url . "
- "FILTER (?url IN (");
+ str = g_string_new ("SELECT ?url ?u ?lastModified "
+ "FROM <" TRACKER_OWN_GRAPH_URN "> {"
+ " ?u a rdfs:Resource ;"
+ " nfo:fileLastModified ?lastModified ;"
+ " nie:url ?url . "
+ " FILTER (?url IN (");
for (i = 0; i < n_files; i++) {
if (i != 0)
g_string_append_c (str, ',');
diff --git a/src/libtracker-miner/tracker-miner-fs.c b/src/libtracker-miner/tracker-miner-fs.c
index c896c5d8d..51f435fb1 100644
--- a/src/libtracker-miner/tracker-miner-fs.c
+++ b/src/libtracker-miner/tracker-miner-fs.c
@@ -30,56 +30,30 @@
#include "tracker-sparql-buffer.h"
#include "tracker-file-notifier.h"
-/* If defined will print the tree from GNode while running */
-#ifdef CRAWLED_TREE_ENABLE_TRACE
-#warning Tree debugging traces enabled
-#endif /* CRAWLED_TREE_ENABLE_TRACE */
-
/* If defined will print push/pop actions on queues */
#ifdef EVENT_QUEUE_ENABLE_TRACE
#warning Event Queue traces enabled
#define EVENT_QUEUE_LOG_PREFIX "[Event Queues] "
#define EVENT_QUEUE_STATUS_TIMEOUT_SECS 30
-#define trace_eq(message, ...) g_debug (EVENT_QUEUE_LOG_PREFIX message, ##__VA_ARGS__)
-#define trace_eq_action(pushed, queue_name, position, gfile1, gfile2, reason) \
+#define trace_eq(message, ...) g_message (EVENT_QUEUE_LOG_PREFIX message, ##__VA_ARGS__)
+#define trace_eq_event(event) \
do { \
- gchar *uri1 = g_file_get_uri (gfile1); \
- gchar *uri2 = gfile2 ? g_file_get_uri (gfile2) : NULL; \
- g_debug ("%s%s '%s%s%s' %s %s of queue '%s'%s%s", \
- EVENT_QUEUE_LOG_PREFIX, \
- pushed ? "Pushed" : "Popped", \
- uri1, \
- uri2 ? "->" : "", \
- uri2 ? uri2 : "", \
- pushed ? "to" : "from", \
- position, \
- queue_name, \
- reason ? ": " : "", \
- reason ? reason : ""); \
+ const gchar *event_type_name[] = { "CREATED", "UPDATED", "DELETED", "MOVED" }; \
+ gchar *uri1 = g_file_get_uri (event->file); \
+ gchar *uri2 = event->dest_file ? g_file_get_uri (event->dest_file) : NULL; \
+ g_message ("%s New %s event: %s%s%s%s", \
+ EVENT_QUEUE_LOG_PREFIX, \
+ event_type_name[event->type], \
+ event->attributes_update ? "(attributes only) " : "", \
+ uri1, \
+ uri2 ? "->" : "", \
+ uri2 ? uri2 : ""); \
g_free (uri1); \
g_free (uri2); \
} while (0)
-#define trace_eq_push_tail(queue_name, gfile, reason) \
- trace_eq_action (TRUE, queue_name, "tail", gfile, NULL, reason)
-#define trace_eq_push_head(queue_name, gfile, reason) \
- trace_eq_action (TRUE, queue_name, "head", gfile, NULL, reason)
-#define trace_eq_push_tail_2(queue_name, gfile1, gfile2, reason) \
- trace_eq_action (TRUE, queue_name, "tail", gfile1, gfile2, reason)
-#define trace_eq_push_head_2(queue_name, gfile1, gfile2, reason) \
- trace_eq_action (TRUE, queue_name, "head", gfile1, gfile2, reason)
-#define trace_eq_pop_head(queue_name, gfile) \
- trace_eq_action (FALSE, queue_name, "head", gfile, NULL, NULL)
-#define trace_eq_pop_head_2(queue_name, gfile1, gfile2) \
- trace_eq_action (FALSE, queue_name, "head", gfile1, gfile2, NULL)
-static gboolean miner_fs_queues_status_trace_timeout_cb (gpointer data);
#else
#define trace_eq(...)
-#define trace_eq_push_tail(...)
-#define trace_eq_push_head(...)
-#define trace_eq_push_tail_2(...)
-#define trace_eq_push_head_2(...)
-#define trace_eq_pop_head(...)
-#define trace_eq_pop_head_2(...)
+#define trace_eq_event(...)
#endif /* EVENT_QUEUE_ENABLE_TRACE */
/* Default processing pool limits to be set */
@@ -158,10 +132,6 @@ struct _TrackerMinerFSPrivate {
guint item_queues_handler_id;
GFile *item_queue_blocker;
-#ifdef EVENT_QUEUE_ENABLE_TRACE
- guint queue_status_timeout_id;
-#endif /* EVENT_QUEUE_ENABLE_TRACE */
-
/* Root / tree / index */
GFile *root;
TrackerIndexingTree *indexing_tree;
@@ -590,12 +560,6 @@ tracker_miner_fs_init (TrackerMinerFS *object)
priv->items = tracker_priority_queue_new ();
-#ifdef EVENT_QUEUE_ENABLE_TRACE
- priv->queue_status_timeout_id = g_timeout_add_seconds (EVENT_QUEUE_STATUS_TIMEOUT_SECS,
- miner_fs_queues_status_trace_timeout_cb,
- object);
-#endif /* PROCESSING_POOL_ENABLE_TRACE */
-
/* Create processing pools */
priv->task_pool = tracker_task_pool_new (DEFAULT_WAIT_POOL_LIMIT);
g_signal_connect (priv->task_pool, "notify::limit-reached",
@@ -895,11 +859,6 @@ fs_finalize (GObject *object)
priv->roots_to_notify = NULL;
}
-#ifdef EVENT_QUEUE_ENABLE_TRACE
- if (priv->queue_status_timeout_id)
- g_source_remove (priv->queue_status_timeout_id);
-#endif /* PROCESSING_POOL_ENABLE_TRACE */
-
G_OBJECT_CLASS (tracker_miner_fs_parent_class)->finalize (object);
}
@@ -2125,6 +2084,8 @@ miner_fs_queue_event (TrackerMinerFS *fs,
tracker_file_notifier_get_file_iri (fs->priv->file_notifier,
event->file, TRUE);
+ trace_eq_event (event);
+
link = tracker_priority_queue_add (fs->priv->items, event, priority);
queue_event_save_node (event, link);
item_queue_handlers_set_up (fs);
@@ -2314,31 +2275,6 @@ file_notifier_finished (TrackerFileNotifier *notifier,
}
}
-
-#ifdef CRAWLED_TREE_ENABLE_TRACE
-
-static gboolean
-print_file_tree (GNode *node,
- gpointer user_data)
-{
- gchar *name;
- gint i;
-
- name = g_file_get_basename (node->data);
-
- /* Indentation */
- for (i = g_node_depth (node) - 1; i > 0; i--) {
- g_print (" ");
- }
-
- g_print ("%s\n", name);
- g_free (name);
-
- return FALSE;
-}
-
-#endif /* CRAWLED_TREE_ENABLE_TRACE */
-
static void
task_pool_cancel_foreach (gpointer data,
gpointer user_data)
@@ -2474,11 +2410,11 @@ tracker_miner_fs_check_file (TrackerMinerFS *fs,
return;
}
- trace_eq_push_tail ("UPDATED", file, "Requested by application");
tracker_file_notifier_get_file_iri (fs->priv->file_notifier,
file, TRUE);
event = queue_event_new (TRACKER_MINER_FS_EVENT_UPDATED, file);
+ trace_eq_event (event);
miner_fs_queue_event (fs, event, priority);
}
@@ -2721,41 +2657,3 @@ tracker_miner_fs_get_data_provider (TrackerMinerFS *fs)
return fs->priv->data_provider;
}
-
-#ifdef EVENT_QUEUE_ENABLE_TRACE
-
-static void
-trace_events_foreach (gpointer data,
- gpointer fs)
-{
- QueueEvent *event = data;
- gchar *uri, *dest_uri = NULL;
-
- uri = g_file_get_uri (event->file);
- if (event->dest_file)
- dest_uri = g_file_get_uri (event->dest_file);
-
- trace_eq ("(%d) '%s' '%s'",
- event->type, uri, dest_uri);
-
- g_free (dest_uri);
- g_free (uri);
-}
-
-static gboolean
-miner_fs_queues_status_trace_timeout_cb (gpointer data)
-{
- TrackerMinerFS *fs = data;
-
- trace_eq ("(%s) Queue '%s' has %u elements:",
- G_OBJECT_TYPE_NAME (fs),
- queue_name,
- tracker_priority_queue_get_length (queue));
- tracker_priority_queue_foreach (queue,
- trace_events_foreach,
- fs);
-
- return G_SOURCE_CONTINUE;
-}
-
-#endif /* EVENT_QUEUE_ENABLE_TRACE */
diff --git a/src/libtracker-miner/tracker-miner-object.c b/src/libtracker-miner/tracker-miner-object.c
index dd6bc0d18..dfaf2dc20 100644
--- a/src/libtracker-miner/tracker-miner-object.c
+++ b/src/libtracker-miner/tracker-miner-object.c
@@ -495,10 +495,11 @@ void
tracker_miner_start (TrackerMiner *miner)
{
g_return_if_fail (TRACKER_IS_MINER (miner));
- g_return_if_fail (miner->priv->started == FALSE);
- miner->priv->started = TRUE;
- g_signal_emit (miner, signals[STARTED], 0);
+ if (miner->priv->started == FALSE) {
+ miner->priv->started = TRUE;
+ g_signal_emit (miner, signals[STARTED], 0);
+ }
}
/**
@@ -513,10 +514,11 @@ void
tracker_miner_stop (TrackerMiner *miner)
{
g_return_if_fail (TRACKER_IS_MINER (miner));
- g_return_if_fail (miner->priv->started == TRUE);
- miner->priv->started = FALSE;
- g_signal_emit (miner, signals[STOPPED], 0);
+ if (miner->priv->started == TRUE) {
+ miner->priv->started = FALSE;
+ g_signal_emit (miner, signals[STOPPED], 0);
+ }
}
/**
diff --git a/src/libtracker-miner/tracker-miner.pc.in b/src/libtracker-miner/tracker-miner.pc.in
deleted file mode 100644
index 36a8a3248..000000000
--- a/src/libtracker-miner/tracker-miner.pc.in
+++ /dev/null
@@ -1,11 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-
-Name: tracker-miner
-Description: A library to develop tracker data miners
-Version: @VERSION@
-Requires: glib-2.0 gio-2.0 tracker-sparql-@TRACKER_API_VERSION@
-Libs: -L${libdir} -ltracker-miner-@TRACKER_API_VERSION@
-Cflags: -I${includedir}/tracker-@TRACKER_API_VERSION@
diff --git a/src/libtracker-miner/tracker-sparql-buffer.c b/src/libtracker-miner/tracker-sparql-buffer.c
index 25564ff54..997d4265e 100644
--- a/src/libtracker-miner/tracker-sparql-buffer.c
+++ b/src/libtracker-miner/tracker-sparql-buffer.c
@@ -72,6 +72,8 @@ tracker_sparql_buffer_finalize (GObject *object)
priv = tracker_sparql_buffer_get_instance_private (TRACKER_SPARQL_BUFFER (object));
+ g_object_unref (priv->connection);
+
if (priv->flush_timeout_id != 0) {
g_source_remove (priv->flush_timeout_id);
}
diff --git a/src/libtracker-remote/meson.build b/src/libtracker-remote/meson.build
index 3735bdb78..82e8853fb 100644
--- a/src/libtracker-remote/meson.build
+++ b/src/libtracker-remote/meson.build
@@ -9,7 +9,9 @@ sources = [
libtracker_remote = static_library('tracker-remote', sources,
dependencies: tracker_remote_dependencies + [tracker_common_dep, tracker_sparql_intermediate_dep],
- c_args: tracker_c_args,
+ c_args: tracker_c_args + [
+ '-include', 'config.h'
+ ],
vala_args: [
'--debug',
'--pkg', 'posix',
diff --git a/src/libtracker-remote/tracker-remote.vala b/src/libtracker-remote/tracker-remote.vala
index b71ac079f..e5025f6b0 100644
--- a/src/libtracker-remote/tracker-remote.vala
+++ b/src/libtracker-remote/tracker-remote.vala
@@ -18,13 +18,17 @@
*
* Author: Carlos Garnacho <carlosg@gnome.org>
*/
+[CCode (cname = "PACKAGE_VERSION")]
+extern const string PACKAGE_VERSION;
public class Tracker.Remote.Connection : Tracker.Sparql.Connection {
+
internal Soup.Session _session;
internal string _base_uri;
const string XML_TYPE = "application/sparql-results+xml";
const string JSON_TYPE = "application/sparql-results+json";
+ const string USER_AGENT = "Tracker/" + PACKAGE_VERSION + " (https://gitlab.gnome.org/GNOME/tracker/issues/; tracker-list@lists.gnome.org) Tracker/" + PACKAGE_VERSION;
public Connection (string base_uri) {
_base_uri = base_uri;
@@ -32,10 +36,11 @@ public class Tracker.Remote.Connection : Tracker.Sparql.Connection {
}
private Soup.Message create_request (string sparql) {
- var uri = _base_uri + sparql;
+ var uri = _base_uri + "?query=" + sparql;
var message = new Soup.Message ("GET", uri);
var headers = message.request_headers;
+ headers.append ("User-Agent", USER_AGENT);
headers.append ("Accept", JSON_TYPE);
headers.append ("Accept", XML_TYPE);
diff --git a/src/libtracker-sparql-backend/meson.build b/src/libtracker-sparql-backend/meson.build
index c30d182d5..633941e31 100644
--- a/src/libtracker-sparql-backend/meson.build
+++ b/src/libtracker-sparql-backend/meson.build
@@ -25,6 +25,25 @@ tracker_sparql_dep = declare_dependency(
dependencies: [tracker_common_dep],
)
+pkg.generate(libtracker_sparql,
+ description: 'Tracker : A library to perform SPARQL queries and updates in the Tracker Store',
+ requires: [glib, gio, gobject, gmodule],
+ subdirs: [
+ 'tracker-' + tracker_api_version,
+ 'tracker-' + tracker_api_version / 'libtracker-sparql',
+ ],
+ variables: [
+ 'exec_prefix=${prefix}',
+ 'libexecdir=${prefix}' / get_option('libexecdir'),
+ 'tracker_store=${libexecdir}' / 'tracker-store',
+ 'datadir=${prefix}' / get_option('datadir'),
+ 'tracker_datadir=${datadir}' / 'tracker',
+ 'ontologies_dir=' + tracker_ontologies_dir,
+ 'domain_ontologies_dir=${tracker_datadir}' / 'domain-ontologies',
+ 'dbus_services_dir=' + dbus_services_dir,
+ ],
+)
+
# The introspection generation for libtracker-sparql is awkward because we have
# both C and Vala code, and we have to generate an introspection repo for each
# one separately and then combine them together manually.
diff --git a/src/libtracker-sparql-backend/tracker-backend.vala b/src/libtracker-sparql-backend/tracker-backend.vala
index db822dc51..462091b98 100644
--- a/src/libtracker-sparql-backend/tracker-backend.vala
+++ b/src/libtracker-sparql-backend/tracker-backend.vala
@@ -358,6 +358,10 @@ public static Tracker.Sparql.Connection tracker_sparql_connection_remote_new (st
return new Tracker.Remote.Connection (url_base);
}
+public static Tracker.Sparql.Connection tracker_sparql_connection_bus_new (string service, DBusConnection? conn) throws Tracker.Sparql.Error, IOError, DBusError, GLib.Error {
+ return new Tracker.Bus.Connection (service, conn, true);
+}
+
public static Tracker.Sparql.Connection tracker_sparql_connection_local_new (Tracker.Sparql.ConnectionFlags flags, File store, File? journal, File? ontology, Cancellable? cancellable = null) throws GLib.Error, Tracker.Sparql.Error, IOError {
var conn = new Tracker.Direct.Connection (flags, store, journal, ontology);
conn.init (cancellable);
diff --git a/src/libtracker-sparql-backend/tracker-sparql-2.map b/src/libtracker-sparql-backend/tracker-sparql-2.map
index 3dee56514..336778ae2 100644
--- a/src/libtracker-sparql-backend/tracker-sparql-2.map
+++ b/src/libtracker-sparql-backend/tracker-sparql-2.map
@@ -11,6 +11,8 @@ global:
tracker_namespace_manager_*;
tracker_resource_*;
tracker_notifier_*;
+ tracker_endpoint_*;
+ tracker_check_version;
local:
*;
};
diff --git a/src/libtracker-sparql/libtracker-sparql-intermediate-c.vapi b/src/libtracker-sparql/libtracker-sparql-intermediate-c.vapi
index 25800f0cf..0c18473c2 100644
--- a/src/libtracker-sparql/libtracker-sparql-intermediate-c.vapi
+++ b/src/libtracker-sparql/libtracker-sparql-intermediate-c.vapi
@@ -100,4 +100,14 @@ namespace Tracker {
public string get_location ();
}
}
+
+ [CCode (cheader_filename = "libtracker-sparql/tracker-endpoint.h")]
+ public class Endpoint : GLib.Object {
+ Sparql.Connection get_sparql_connection ();
+ }
+
+ [CCode (cheader_filename = "libtracker-sparql/tracker-endpoint-dbus.h")]
+ public class EndpointDBus : GLib.Object, GLib.Initable {
+ public EndpointDBus (Sparql.Connection sparql_conn, GLib.DBusConnection? dbus_conn, string object_path, GLib.Cancellable? cancellable) throws GLib.Error;
+ }
}
diff --git a/src/libtracker-sparql/meson.build b/src/libtracker-sparql/meson.build
index 20fa8c35c..2bd750f36 100644
--- a/src/libtracker-sparql/meson.build
+++ b/src/libtracker-sparql/meson.build
@@ -55,6 +55,8 @@ tracker_sparql_generated_header = custom_target('tracker-sparql-generated-header
# Now build the C parts ...
libtracker_sparql_c_sources = files(
+ 'tracker-endpoint.c',
+ 'tracker-endpoint-dbus.c',
'tracker-namespace-manager.c',
'tracker-notifier.c',
'tracker-resource.c',
@@ -63,6 +65,8 @@ libtracker_sparql_c_sources = files(
)
libtracker_sparql_c_public_headers = files(
+ 'tracker-endpoint.h',
+ 'tracker-endpoint-dbus.h',
'tracker-namespace-manager.h',
'tracker-notifier.h',
'tracker-resource.h',
@@ -70,7 +74,7 @@ libtracker_sparql_c_public_headers = files(
)
libtracker_sparql_intermediate_c = static_library('tracker-sparql-intermediate-c',
- enums_c, enums_h,
+ enums_c, enums_h, tracker_sparql_generated_header[0],
libtracker_sparql_c_sources,
dependencies: [tracker_common_dep, json_glib],
link_with: libtracker_sparql_intermediate_vala,
@@ -85,13 +89,6 @@ tracker_sparql_intermediate_dep = declare_dependency(
dependencies: [ tracker_sparql_intermediate_dependencies, libtracker_sparql_c_vapi_dep ],
)
-configure_file(
- input: 'tracker-sparql.pc.in',
- output: 'tracker-sparql-@0@.pc'.format(tracker_api_version),
- configuration: conf,
- install: true,
- install_dir: join_paths(get_option('prefix'), get_option('libdir'), 'pkgconfig'))
-
install_headers(
libtracker_sparql_c_public_headers,
'tracker-ontologies.h',
diff --git a/src/libtracker-sparql/tracker-connection.vala b/src/libtracker-sparql/tracker-connection.vala
index d0e60afda..e0e0e04dd 100644
--- a/src/libtracker-sparql/tracker-connection.vala
+++ b/src/libtracker-sparql/tracker-connection.vala
@@ -55,6 +55,7 @@ namespace Tracker {
* @TRACKER_SPARQL_NO_SPACE: There was no disk space available to perform the request.
* @TRACKER_SPARQL_INTERNAL: Internal error.
* @TRACKER_SPARQL_UNSUPPORTED: Unsupported feature or method.
+ * @TRACKER_SPARQL_UNKNOWN_GRAPH: Unknown graph.
*
* Error domain for Tracker Sparql. Errors in this domain will be from the
* #TrackerSparqlError enumeration. See #GError for more information on error
@@ -71,7 +72,8 @@ public errordomain Tracker.Sparql.Error {
CONSTRAINT,
NO_SPACE,
INTERNAL,
- UNSUPPORTED
+ UNSUPPORTED,
+ UNKNOWN_GRAPH
}
public enum Tracker.Sparql.ConnectionFlags {
@@ -237,6 +239,8 @@ public abstract class Tracker.Sparql.Connection : Object {
*/
public extern async static new Connection local_new_async (Tracker.Sparql.ConnectionFlags flags, File store, File? journal, File? ontology, Cancellable? cancellable = null) throws Sparql.Error, IOError;
+ public extern static new Connection bus_new (string service_name, DBusConnection? dbus_connection = null) throws Sparql.Error, IOError, DBusError, GLib.Error;
+
/**
* tracker_sparql_connection_query:
* @self: a #TrackerSparqlConnection
diff --git a/src/libtracker-sparql/tracker-endpoint-dbus.c b/src/libtracker-sparql/tracker-endpoint-dbus.c
new file mode 100644
index 000000000..975e5baed
--- /dev/null
+++ b/src/libtracker-sparql/tracker-endpoint-dbus.c
@@ -0,0 +1,610 @@
+/*
+ * Copyright (C) 2019, Red Hat, Inc
+ *
+ * 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.
+ *
+ * Author: Carlos Garnacho <carlosg@gnome.org>
+ */
+
+#include "config.h"
+
+#include "tracker-endpoint-dbus.h"
+
+#include <gio/gio.h>
+#include <gio/gunixinputstream.h>
+#include <gio/gunixoutputstream.h>
+#include <gio/gunixfdlist.h>
+
+static const gchar introspection_xml[] =
+ "<node>"
+ " <interface name='org.freedesktop.Tracker1.Endpoint'>"
+ " <method name='Query'>"
+ " <arg type='as' name='anon_graphs' direction='in' />"
+ " <arg type='as' name='named_graphs' direction='in' />"
+ " <arg type='s' name='query' direction='in' />"
+ " <arg type='h' name='output_stream' direction='in' />"
+ " <arg type='as' name='result' direction='out' />"
+ " </method>"
+ " <method name='Update'>"
+ " <arg type='as' name='anon_graphs' direction='in' />"
+ " <arg type='as' name='named_graphs' direction='in' />"
+ " <arg type='h' name='input_stream' direction='in' />"
+ " </method>"
+ " <method name='UpdateArray'>"
+ " <arg type='as' name='anon_graphs' direction='in' />"
+ " <arg type='as' name='named_graphs' direction='in' />"
+ " <arg type='h' name='input_stream' direction='in' />"
+ " </method>"
+ " <method name='UpdateBlank'>"
+ " <arg type='as' name='anon_graphs' direction='in' />"
+ " <arg type='as' name='named_graphs' direction='in' />"
+ " <arg type='h' name='input_stream' direction='in' />"
+ " <arg type='aaa{ss}' name='result' direction='out' />"
+ " </method>"
+ " <signal name='GraphUpdate'>"
+ " <arg type='a(ii)' name='updates' />"
+ " </signal>"
+ " </interface>"
+ "</node>";
+
+enum {
+ PROP_0,
+ PROP_DBUS_CONNECTION,
+ PROP_OBJECT_PATH,
+ N_PROPS
+};
+
+struct _TrackerEndpointDBus {
+ TrackerEndpoint parent_instance;
+ GDBusConnection *dbus_connection;
+ gchar *object_path;
+ guint register_id;
+ GDBusNodeInfo *node_info;
+ GCancellable *cancellable;
+};
+
+typedef struct {
+ TrackerEndpointDBus *endpoint;
+ GDBusMethodInvocation *invocation;
+ GDataOutputStream *data_stream;
+} QueryRequest;
+
+typedef struct {
+ TrackerEndpointDBus *endpoint;
+ GDBusMethodInvocation *invocation;
+ GDataInputStream *input_stream;
+ GPtrArray *queries;
+ gboolean array_update;
+ gint num_queries;
+ gint cur_query;
+} UpdateRequest;
+
+GParamSpec *props[N_PROPS] = { 0 };
+
+static void tracker_endpoint_dbus_initable_iface_init (GInitableIface *iface);
+
+static void read_update_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data);
+static void read_update_blank_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data);
+
+G_DEFINE_TYPE_WITH_CODE (TrackerEndpointDBus, tracker_endpoint_dbus, TRACKER_TYPE_ENDPOINT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, tracker_endpoint_dbus_initable_iface_init))
+
+static QueryRequest *
+query_request_new (TrackerEndpointDBus *endpoint,
+ GDBusMethodInvocation *invocation,
+ int fd)
+{
+ GOutputStream *stream, *buffered_stream;
+ QueryRequest *request;
+
+ request = g_new0 (QueryRequest, 1);
+ request->invocation = g_object_ref (invocation);
+ request->endpoint = endpoint;
+
+ stream = g_unix_output_stream_new (fd, TRUE);
+ buffered_stream = g_buffered_output_stream_new_sized (stream,
+ getpagesize ());
+
+ request->data_stream = g_data_output_stream_new (buffered_stream);
+ g_data_output_stream_set_byte_order (request->data_stream,
+ G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN);
+
+ g_object_unref (buffered_stream);
+ g_object_unref (stream);
+
+ return request;
+}
+
+static void
+query_request_free (QueryRequest *request)
+{
+ g_output_stream_close (G_OUTPUT_STREAM (request->data_stream),
+ NULL, NULL);
+
+ g_object_unref (request->invocation);
+ g_object_unref (request->data_stream);
+ g_free (request);
+}
+
+static UpdateRequest *
+update_request_new (TrackerEndpointDBus *endpoint,
+ GDBusMethodInvocation *invocation,
+ gboolean array_update,
+ int input)
+{
+ UpdateRequest *request;
+ GInputStream *stream;
+
+ request = g_new0 (UpdateRequest, 1);
+ request->invocation = g_object_ref (invocation);
+ request->endpoint = endpoint;
+ request->cur_query = 0;
+ request->array_update = array_update;
+ request->queries = g_ptr_array_new_with_free_func (g_free);
+
+ stream = g_unix_input_stream_new (input, TRUE);
+ request->input_stream = g_data_input_stream_new (stream);
+ g_buffered_input_stream_set_buffer_size (G_BUFFERED_INPUT_STREAM (request->input_stream),
+ getpagesize ());
+ g_data_input_stream_set_byte_order (request->input_stream,
+ G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN);
+ g_object_unref (stream);
+
+ if (array_update)
+ request->num_queries = g_data_input_stream_read_int32 (request->input_stream, NULL, NULL);
+ else
+ request->num_queries = 1;
+
+ return request;
+}
+
+static gboolean
+update_request_read_next (UpdateRequest *request,
+ GAsyncReadyCallback cb)
+{
+ gchar *buffer;
+ gint buffer_size;
+
+ if (request->cur_query >= request->num_queries)
+ return FALSE;
+
+ request->cur_query++;
+ buffer_size = g_data_input_stream_read_int32 (request->input_stream, NULL, NULL);
+ buffer = g_new0 (char, buffer_size + 1);
+ g_ptr_array_add (request->queries, buffer);
+
+ g_input_stream_read_all_async (G_INPUT_STREAM (request->input_stream),
+ buffer,
+ buffer_size,
+ G_PRIORITY_DEFAULT,
+ request->endpoint->cancellable,
+ cb, request);
+ return TRUE;
+}
+
+static void
+update_request_free (UpdateRequest *request)
+{
+ g_input_stream_close (G_INPUT_STREAM (request->input_stream),
+ NULL, NULL);
+
+ g_ptr_array_unref (request->queries);
+ g_object_unref (request->invocation);
+ g_object_unref (request->input_stream);
+ g_free (request);
+}
+
+static void
+query_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ QueryRequest *request = user_data;
+ TrackerSparqlCursor *cursor;
+ GError *error = NULL;
+ const gchar **values = NULL;
+ const gchar **variable_names = NULL;
+ glong *offsets = NULL;
+ gint i, n_columns = 0;
+
+ cursor = tracker_sparql_connection_query_finish (TRACKER_SPARQL_CONNECTION (object),
+ res, &error);
+ if (!cursor)
+ goto error;
+
+ n_columns = tracker_sparql_cursor_get_n_columns (cursor);
+ variable_names = g_new0 (const gchar *, n_columns + 1);
+ values = g_new0 (const char *, n_columns);
+ offsets = g_new0 (glong, n_columns);
+
+ for (i = 0; i < n_columns; i++)
+ variable_names[i] = tracker_sparql_cursor_get_variable_name (cursor, i);
+
+ while (tracker_sparql_cursor_next (cursor, NULL, &error)) {
+ glong cur_offset = -1;
+
+ g_data_output_stream_put_int32 (request->data_stream, n_columns, NULL, NULL);
+
+ for (i = 0; i < n_columns; i++) {
+ glong len;
+
+ g_data_output_stream_put_int32 (request->data_stream,
+ tracker_sparql_cursor_get_value_type (cursor, i),
+ NULL, NULL);
+ values[i] = tracker_sparql_cursor_get_string (cursor, i, &len);
+ len++;
+ cur_offset += len;
+ offsets[i] = cur_offset;
+ }
+
+ for (i = 0; i < n_columns; i++) {
+ g_data_output_stream_put_int32 (request->data_stream,
+ offsets[i], NULL, NULL);
+ }
+
+ for (i = 0; i < n_columns; i++) {
+ g_data_output_stream_put_string (request->data_stream,
+ values[i] ? values[i] : "",
+ NULL, NULL);
+ g_data_output_stream_put_byte (request->data_stream, 0, NULL, NULL);
+ }
+ }
+
+error:
+ if (error)
+ g_dbus_method_invocation_return_gerror (request->invocation, error);
+ else
+ g_dbus_method_invocation_return_value (request->invocation, g_variant_new ("(^as)", variable_names));
+
+ g_free (variable_names);
+ g_free (values);
+ g_free (offsets);
+ g_clear_object (&cursor);
+
+ query_request_free (request);
+}
+
+static void
+update_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ UpdateRequest *request = user_data;
+ GError *error = NULL;
+
+ tracker_sparql_connection_update_array_finish (TRACKER_SPARQL_CONNECTION (object),
+ res, &error);
+ if (error) {
+ g_dbus_method_invocation_return_gerror (request->invocation, error);
+ } else {
+ g_dbus_method_invocation_return_value (request->invocation, NULL);
+ }
+
+ update_request_free (request);
+}
+
+static void
+read_update_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ TrackerSparqlConnection *conn;
+ UpdateRequest *request = user_data;
+ GError *error = NULL;
+
+ if (!g_input_stream_read_all_finish (G_INPUT_STREAM (object),
+ res, NULL, &error)) {
+ g_dbus_method_invocation_return_gerror (request->invocation, error);
+ update_request_free (request);
+ return;
+ }
+
+ if (!update_request_read_next (request, read_update_cb)) {
+ conn = tracker_endpoint_get_sparql_connection (TRACKER_ENDPOINT (request->endpoint));
+ tracker_sparql_connection_update_array_async (conn,
+ (gchar **) request->queries->pdata,
+ request->queries->len,
+ G_PRIORITY_DEFAULT,
+ request->endpoint->cancellable,
+ update_cb,
+ request);
+ }
+}
+
+static void
+update_blank_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ UpdateRequest *request = user_data;
+ GError *error = NULL;
+ GVariant *results;
+
+ results = tracker_sparql_connection_update_blank_finish (TRACKER_SPARQL_CONNECTION (object),
+ res, &error);
+ if (results)
+ g_dbus_method_invocation_return_value (request->invocation, results);
+ else
+ g_dbus_method_invocation_return_gerror (request->invocation, error);
+
+ update_request_free (request);
+}
+
+static void
+read_update_blank_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ TrackerSparqlConnection *conn;
+ UpdateRequest *request = user_data;
+ GError *error = NULL;
+
+ if (!g_input_stream_read_all_finish (G_INPUT_STREAM (object),
+ res, NULL, &error)) {
+ g_dbus_method_invocation_return_gerror (request->invocation, error);
+ update_request_free (request);
+ return;
+ }
+
+ conn = tracker_endpoint_get_sparql_connection (TRACKER_ENDPOINT (request->endpoint));
+ tracker_sparql_connection_update_blank_async (conn,
+ g_ptr_array_index (request->queries, 0),
+ G_PRIORITY_DEFAULT,
+ request->endpoint->cancellable,
+ update_blank_cb,
+ request);
+}
+
+static void
+endpoint_dbus_iface_method_call (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ TrackerEndpointDBus *endpoint_dbus = user_data;
+ TrackerSparqlConnection *conn;
+ GUnixFDList *fd_list;
+ GError *error = NULL;
+ gchar *query;
+ gint handle, fd = -1;
+
+ conn = tracker_endpoint_get_sparql_connection (TRACKER_ENDPOINT (endpoint_dbus));
+ fd_list = g_dbus_message_get_unix_fd_list (g_dbus_method_invocation_get_message (invocation));
+
+ if (g_strcmp0 (method_name, "Query") == 0) {
+ /* FIXME: Anon/named graphs are ignored ATM */
+ g_variant_get (parameters, "(asassh)", NULL, NULL, &query, &handle);
+
+ if (fd_list)
+ fd = g_unix_fd_list_get (fd_list, handle, &error);
+
+ if (fd < 0) {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Did not get a file descriptor");
+ } else {
+ QueryRequest *request;
+
+ request = query_request_new (endpoint_dbus, invocation, fd);
+ tracker_sparql_connection_query_async (conn,
+ query,
+ endpoint_dbus->cancellable,
+ query_cb,
+ request);
+ }
+
+ g_free (query);
+ } else if (g_strcmp0 (method_name, "Update") == 0 ||
+ g_strcmp0 (method_name, "UpdateArray") == 0) {
+ /* FIXME: Anon/named graphs are ignored ATM */
+ g_variant_get (parameters, "(asash)", NULL, NULL, &handle);
+
+ if (fd_list)
+ fd = g_unix_fd_list_get (fd_list, handle, &error);
+
+ if (fd < 0) {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Did not get a file descriptor");
+ } else {
+ UpdateRequest *request;
+
+ request = update_request_new (endpoint_dbus, invocation,
+ g_strcmp0 (method_name, "UpdateArray") == 0,
+ fd);
+ update_request_read_next (request, read_update_cb);
+ }
+ } else if (g_strcmp0 (method_name, "UpdateBlank") == 0) {
+ /* FIXME: Anon/named graphs are ignored ATM */
+ g_variant_get (parameters, "(asash)", NULL, NULL, &handle);
+
+ if (fd_list)
+ fd = g_unix_fd_list_get (fd_list, handle, &error);
+
+ if (fd < 0) {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Did not get a file descriptor");
+ } else {
+ UpdateRequest *request;
+
+ request = update_request_new (endpoint_dbus, invocation, FALSE, fd);
+ update_request_read_next (request, read_update_blank_cb);
+ }
+ } else {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_UNKNOWN_METHOD,
+ "Unknown method '%s'", method_name);
+ }
+}
+
+static gboolean
+tracker_endpoint_dbus_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ TrackerEndpointDBus *endpoint_dbus = TRACKER_ENDPOINT_DBUS (initable);
+
+ endpoint_dbus->node_info = g_dbus_node_info_new_for_xml (introspection_xml,
+ error);
+ if (!endpoint_dbus->node_info)
+ return FALSE;
+
+ endpoint_dbus->register_id =
+ g_dbus_connection_register_object (endpoint_dbus->dbus_connection,
+ endpoint_dbus->object_path,
+ endpoint_dbus->node_info->interfaces[0],
+ &(GDBusInterfaceVTable) {
+ endpoint_dbus_iface_method_call,
+ NULL,
+ NULL
+ },
+ endpoint_dbus,
+ NULL,
+ error);
+ return TRUE;
+}
+
+static void
+tracker_endpoint_dbus_initable_iface_init (GInitableIface *iface)
+{
+ iface->init = tracker_endpoint_dbus_initable_init;
+}
+
+static void
+tracker_endpoint_dbus_finalize (GObject *object)
+{
+ TrackerEndpointDBus *endpoint_dbus = TRACKER_ENDPOINT_DBUS (object);
+
+ g_cancellable_cancel (endpoint_dbus->cancellable);
+
+ if (endpoint_dbus->register_id != 0) {
+ g_dbus_connection_unregister_object (endpoint_dbus->dbus_connection,
+ endpoint_dbus->register_id);
+ endpoint_dbus->register_id = 0;
+ }
+
+ g_clear_object (&endpoint_dbus->cancellable);
+ g_clear_object (&endpoint_dbus->dbus_connection);
+ g_clear_pointer (&endpoint_dbus->object_path, g_free);
+ g_clear_pointer (&endpoint_dbus->node_info,
+ g_dbus_node_info_unref);
+
+ G_OBJECT_CLASS (tracker_endpoint_dbus_parent_class)->finalize (object);
+}
+
+static void
+tracker_endpoint_dbus_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ TrackerEndpointDBus *endpoint_dbus = TRACKER_ENDPOINT_DBUS (object);
+
+ switch (prop_id) {
+ case PROP_DBUS_CONNECTION:
+ endpoint_dbus->dbus_connection = g_value_dup_object (value);
+ break;
+ case PROP_OBJECT_PATH:
+ endpoint_dbus->object_path = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+tracker_endpoint_dbus_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ TrackerEndpointDBus *endpoint_dbus = TRACKER_ENDPOINT_DBUS (object);
+
+ switch (prop_id) {
+ case PROP_DBUS_CONNECTION:
+ g_value_set_object (value, endpoint_dbus->dbus_connection);
+ break;
+ case PROP_OBJECT_PATH:
+ g_value_set_string (value, endpoint_dbus->object_path);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+tracker_endpoint_dbus_class_init (TrackerEndpointDBusClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = tracker_endpoint_dbus_finalize;
+ object_class->set_property = tracker_endpoint_dbus_set_property;
+ object_class->get_property = tracker_endpoint_dbus_get_property;
+
+ props[PROP_DBUS_CONNECTION] =
+ g_param_spec_object ("dbus-connection",
+ "DBus connection",
+ "DBus connection",
+ G_TYPE_DBUS_CONNECTION,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ props[PROP_OBJECT_PATH] =
+ g_param_spec_string ("object-path",
+ "DBus object path",
+ "DBus object path",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ g_object_class_install_properties (object_class, N_PROPS, props);
+}
+
+static void
+tracker_endpoint_dbus_init (TrackerEndpointDBus *endpoint)
+{
+}
+
+TrackerEndpointDBus *
+tracker_endpoint_dbus_new (TrackerSparqlConnection *sparql_connection,
+ GDBusConnection *dbus_connection,
+ const gchar *object_path,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (TRACKER_SPARQL_IS_CONNECTION (sparql_connection), NULL);
+ g_return_val_if_fail (G_IS_DBUS_CONNECTION (dbus_connection), NULL);
+ g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), NULL);
+ g_return_val_if_fail (!error || !*error, NULL);
+
+ return g_initable_new (TRACKER_TYPE_ENDPOINT_DBUS, cancellable, error,
+ "dbus-connection", dbus_connection,
+ "sparql-connection", sparql_connection,
+ "object-path", object_path,
+ NULL);
+}
diff --git a/src/libtracker-sparql/tracker-endpoint-dbus.h b/src/libtracker-sparql/tracker-endpoint-dbus.h
new file mode 100644
index 000000000..d7902ca8a
--- /dev/null
+++ b/src/libtracker-sparql/tracker-endpoint-dbus.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019, Red Hat, Inc
+ *
+ * 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.
+ *
+ * Author: Carlos Garnacho <carlosg@gnome.org>
+ */
+
+#ifndef __TRACKER_ENDPOINT_DBUS_H__
+#define __TRACKER_ENDPOINT_DBUS_H__
+
+#if !defined (__LIBTRACKER_SPARQL_INSIDE__) && !defined (TRACKER_COMPILATION)
+#error "only <libtracker-sparql/tracker-sparql.h> must be included directly."
+#endif
+
+#include <libtracker-sparql/tracker-endpoint.h>
+
+#define TRACKER_TYPE_ENDPOINT_DBUS tracker_endpoint_dbus_get_type()
+G_DECLARE_FINAL_TYPE (TrackerEndpointDBus, tracker_endpoint_dbus, TRACKER, ENDPOINT_DBUS, TrackerEndpoint)
+
+TrackerEndpointDBus *
+tracker_endpoint_dbus_new (TrackerSparqlConnection *sparql_connection,
+ GDBusConnection *dbus_connection,
+ const gchar *object_path,
+ GCancellable *cancellable,
+ GError **error);
+
+#endif /* __TRACKER_ENDPOINT_DBUS_H__ */
diff --git a/src/libtracker-sparql/tracker-endpoint.c b/src/libtracker-sparql/tracker-endpoint.c
new file mode 100644
index 000000000..3efdb966c
--- /dev/null
+++ b/src/libtracker-sparql/tracker-endpoint.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2019, Red Hat, Inc
+ *
+ * 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.
+ *
+ * Author: Carlos Garnacho <carlosg@gnome.org>
+ */
+
+#include "config.h"
+
+#include "tracker-endpoint.h"
+
+enum {
+ PROP_0,
+ PROP_SPARQL_CONNECTION,
+ N_PROPS
+};
+
+static GParamSpec *props[N_PROPS] = { 0 };
+
+typedef struct {
+ TrackerSparqlConnection *sparql_connection;
+} TrackerEndpointPrivate;
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (TrackerEndpoint, tracker_endpoint, G_TYPE_OBJECT)
+
+static void
+tracker_endpoint_finalize (GObject *object)
+{
+ TrackerEndpoint *endpoint = TRACKER_ENDPOINT (object);
+ TrackerEndpointPrivate *priv = tracker_endpoint_get_instance_private (endpoint);
+
+ g_clear_object (&priv->sparql_connection);
+
+ G_OBJECT_CLASS (tracker_endpoint_parent_class)->finalize (object);
+}
+
+static void
+tracker_endpoint_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ TrackerEndpoint *endpoint = TRACKER_ENDPOINT (object);
+ TrackerEndpointPrivate *priv = tracker_endpoint_get_instance_private (endpoint);
+
+ switch (prop_id) {
+ case PROP_SPARQL_CONNECTION:
+ priv->sparql_connection = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+tracker_endpoint_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ TrackerEndpoint *endpoint = TRACKER_ENDPOINT (object);
+ TrackerEndpointPrivate *priv = tracker_endpoint_get_instance_private (endpoint);
+
+ switch (prop_id) {
+ case PROP_SPARQL_CONNECTION:
+ g_value_set_object (value, priv->sparql_connection);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+tracker_endpoint_class_init (TrackerEndpointClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = tracker_endpoint_finalize;
+ object_class->set_property = tracker_endpoint_set_property;
+ object_class->get_property = tracker_endpoint_get_property;
+
+ props[PROP_SPARQL_CONNECTION] =
+ g_param_spec_object ("sparql-connection",
+ "Sparql connection",
+ "Sparql connection",
+ TRACKER_SPARQL_TYPE_CONNECTION,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_properties (object_class, N_PROPS, props);
+}
+
+static void
+tracker_endpoint_init (TrackerEndpoint *endpoint)
+{
+}
+
+TrackerSparqlConnection *
+tracker_endpoint_get_sparql_connection (TrackerEndpoint *endpoint)
+{
+ TrackerEndpointPrivate *priv = tracker_endpoint_get_instance_private (endpoint);
+
+ return priv->sparql_connection;
+}
diff --git a/src/libtracker-sparql/tracker-endpoint.h b/src/libtracker-sparql/tracker-endpoint.h
new file mode 100644
index 000000000..d665b74da
--- /dev/null
+++ b/src/libtracker-sparql/tracker-endpoint.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019, Red Hat, Inc
+ *
+ * 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.
+ *
+ * Author: Carlos Garnacho <carlosg@gnome.org>
+ */
+
+#ifndef __TRACKER_ENDPOINT_H__
+#define __TRACKER_ENDPOINT_H__
+
+#if !defined (__LIBTRACKER_SPARQL_INSIDE__) && !defined (TRACKER_COMPILATION)
+#error "only <libtracker-sparql/tracker-sparql.h> must be included directly."
+#endif
+
+#include <glib-object.h>
+#include <libtracker-sparql/tracker-generated.h>
+
+#define TRACKER_TYPE_ENDPOINT tracker_endpoint_get_type()
+G_DECLARE_DERIVABLE_TYPE (TrackerEndpoint, tracker_endpoint, TRACKER, ENDPOINT, GObject)
+
+struct _TrackerEndpointClass {
+ GObjectClass parent_class;
+};
+
+TrackerSparqlConnection * tracker_endpoint_get_sparql_connection (TrackerEndpoint *endpoint);
+
+#endif /* __TRACKER_ENDPOINT_H__ */
diff --git a/src/libtracker-sparql/tracker-resource.c b/src/libtracker-sparql/tracker-resource.c
index 059289b78..87b9c692d 100644
--- a/src/libtracker-sparql/tracker-resource.c
+++ b/src/libtracker-sparql/tracker-resource.c
@@ -961,6 +961,13 @@ tracker_resource_compare (TrackerResource *a,
return strcmp (a_priv->identifier, b_priv->identifier);
};
+/* Internal helper. */
+static GList *
+g_list_find_resource (GList *list,
+ TrackerResource *resource) {
+ return g_list_find_custom (list, resource, (GCompareFunc) tracker_resource_compare);
+}
+
/* Helper function for serialization code. This allows you to selectively
* populate 'interned_namespaces' from 'all_namespaces' based on when a
@@ -1039,7 +1046,7 @@ generate_nested_turtle_resource (TrackerResource *resource,
data->all_namespaces))
return;
- if (g_list_find_custom (data->done_list, resource, (GCompareFunc) tracker_resource_compare) == NULL) {
+ if (g_list_find_resource (data->done_list, resource) == NULL) {
data->done_list = g_list_prepend (data->done_list, resource);
generate_turtle (resource, data);
g_string_append (data->string, "\n");
@@ -1308,13 +1315,23 @@ generate_sparql_relation_deletes_foreach (gpointer key,
{
const GValue *value = value_ptr;
GenerateSparqlData *data = user_data;
+ int i;
if (G_VALUE_HOLDS (value, TRACKER_TYPE_RESOURCE)) {
TrackerResource *relation = g_value_get_object (value);
- if (g_list_find_custom (data->done_list, relation, (GCompareFunc) tracker_resource_compare) == NULL) {
- data->done_list = g_list_prepend (data->done_list, relation);
- generate_sparql_deletes (relation, data);
+ generate_sparql_deletes (relation, data);
+ } else if (G_VALUE_HOLDS (value, G_TYPE_PTR_ARRAY)) {
+ GPtrArray *array = g_value_get_boxed (value);
+
+ for (i = 0; i < array->len; i ++) {
+ GValue *value = g_ptr_array_index (array, i);
+
+ if (G_VALUE_HOLDS (value, TRACKER_TYPE_RESOURCE)) {
+ TrackerResource *relation = g_value_get_object (value);
+
+ generate_sparql_deletes (relation, data);
+ }
}
}
}
@@ -1335,10 +1352,7 @@ generate_sparql_relation_inserts_foreach (gpointer key,
data->namespaces))
return;
- if (g_list_find_custom (data->done_list, relation, (GCompareFunc) tracker_resource_compare) == NULL) {
- data->done_list = g_list_prepend (data->done_list, relation);
- generate_sparql_insert_pattern (relation, data);
- }
+ generate_sparql_insert_pattern (relation, data);
} else if (G_VALUE_HOLDS (value, G_TYPE_PTR_ARRAY)) {
GPtrArray *array = g_value_get_boxed (value);
const GValue *array_value;
@@ -1358,11 +1372,6 @@ generate_sparql_relation_inserts_foreach (gpointer key,
data->namespaces))
continue;
- if (g_list_find_custom (data->done_list, relation,
- (GCompareFunc) tracker_resource_compare) != NULL)
- continue;
-
- data->done_list = g_list_prepend (data->done_list, relation);
generate_sparql_insert_pattern (relation, data);
}
}
@@ -1419,6 +1428,12 @@ generate_sparql_deletes (TrackerResource *resource,
{
TrackerResourcePrivate *priv = GET_PRIVATE (resource);
+ if (g_list_find_resource (data->done_list, resource) != NULL)
+ /* We already processed this resource. */
+ return;
+
+ data->done_list = g_list_prepend (data->done_list, resource);
+
if (! is_blank_node (priv->identifier) && g_hash_table_size (priv->overwrite) > 0) {
generate_sparql_delete_queries (resource, priv->overwrite, data);
}
@@ -1438,6 +1453,12 @@ generate_sparql_insert_pattern (TrackerResource *resource,
const GValue *value;
gboolean had_property = FALSE;
+ if (g_list_find_resource (data->done_list, resource) != NULL)
+ /* We already processed this resource. */
+ return;
+
+ data->done_list = g_list_prepend (data->done_list, resource);
+
/* First, emit any sub-resources. */
g_hash_table_foreach (priv->properties, generate_sparql_relation_inserts_foreach, data);
@@ -1520,7 +1541,7 @@ tracker_resource_print_sparql_update (TrackerResource *resource,
/* Resources can be recursive, and may have repeated or even cyclic
* relationships. This list keeps track of what we already processed.
*/
- context.done_list = g_list_prepend (NULL, resource);
+ context.done_list = NULL;
/* Delete the existing data. If we don't do this, we may get constraint
* violations due to trying to add a second value to a single-valued
@@ -1529,7 +1550,7 @@ tracker_resource_print_sparql_update (TrackerResource *resource,
generate_sparql_deletes (resource, &context);
g_list_free (context.done_list);
- context.done_list = g_list_prepend (NULL, resource);
+ context.done_list = NULL;
/* Finally insert the data */
g_string_append (context.string, "INSERT DATA {\n");
@@ -1589,7 +1610,7 @@ generate_jsonld_value (const GValue *value,
resource = TRACKER_RESOURCE (g_value_get_object (value));
- if (g_list_find_custom (data->done_list, resource, (GCompareFunc) tracker_resource_compare) == NULL) {
+ if (g_list_find_resource (data->done_list, resource) == NULL) {
data->done_list = g_list_prepend (data->done_list, resource);
json_builder_begin_object (data->builder);
diff --git a/src/libtracker-sparql/tracker-sparql.h b/src/libtracker-sparql/tracker-sparql.h
index 693895011..a87122e4d 100644
--- a/src/libtracker-sparql/tracker-sparql.h
+++ b/src/libtracker-sparql/tracker-sparql.h
@@ -22,6 +22,8 @@
#define __LIBTRACKER_SPARQL_INSIDE__
+#include <libtracker-sparql/tracker-endpoint.h>
+#include <libtracker-sparql/tracker-endpoint-dbus.h>
#include <libtracker-sparql/tracker-version.h>
#include <libtracker-sparql/tracker-ontologies.h>
#include <libtracker-sparql/tracker-resource.h>
diff --git a/src/libtracker-sparql/tracker-sparql.pc.in b/src/libtracker-sparql/tracker-sparql.pc.in
deleted file mode 100644
index dcdf0b7df..000000000
--- a/src/libtracker-sparql/tracker-sparql.pc.in
+++ /dev/null
@@ -1,18 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-libexecdir=@libexecdir@
-datadir=@datadir@
-tracker_store=@tracker_store@
-ontologies_dir=@ontologies_dir@
-domain_ontologies_dir=@domain_ontologies_dir@
-
-Name: tracker-sparql
-Description: Tracker : A library to perform SPARQL queries and updates in the \
- Tracker Store
-Version: @VERSION@
-Requires: glib-2.0 gobject-2.0 gio-2.0 gmodule-2.0
-Libs: -L${libdir} -ltracker-sparql-@TRACKER_API_VERSION@
-Cflags: -I${includedir}/tracker-@TRACKER_API_VERSION@ -I${includedir}/tracker-@TRACKER_API_VERSION@/libtracker-sparql
-
diff --git a/src/ontologies/11-rdf.ontology b/src/ontologies/11-rdf.ontology
index 7f6532573..efbf68f0e 100644
--- a/src/ontologies/11-rdf.ontology
+++ b/src/ontologies/11-rdf.ontology
@@ -7,7 +7,7 @@
rdf: a tracker:Namespace, tracker:Ontology ;
tracker:prefix "rdf" ;
- nao:lastModified "2011-01-31T15:00:00Z" .
+ nao:lastModified "2019-06-09T21:00:00Z" .
rdfs: a tracker:Namespace ;
tracker:prefix "rdfs" .
@@ -28,6 +28,16 @@ rdfs:Literal a rdfs:Class ;
rdfs:label "Literal" ;
rdfs:subClassOf rdfs:Resource .
+rdfs:Datatype a rdfs:Class ;
+ rdfs:label "Datatype" ;
+ rdfs:comment "The class of RDF datatypes." ;
+ rdfs:subClassOf rdfs:Class .
+
+rdf:langString a rdfs:Class, rdfs:Datatype ;
+ rdfs:subClassOf rdfs:Literal ;
+ rdfs:label "langString" ;
+ rdfs:comment "The datatype of language-tagged string values" .
+
rdf:type a rdf:Property ;
rdfs:domain rdfs:Resource ;
rdfs:range rdfs:Class .
@@ -60,6 +70,27 @@ rdfs:range a rdf:Property ;
rdfs:domain rdf:Property ;
rdfs:range rdfs:Class .
+rdf:List a rdfs:Class ;
+ rdfs:label "List" ;
+ rdfs:comment "The class of RDF Lists." ;
+ rdfs:subClassOf rdfs:Resource .
+
+rdf:nil a rdf:List ;
+ rdfs:label "nil" ;
+ rdfs:comment "The empty list, with no items in it. If the rest of a list is nil then the list has no more items in it." .
+
+rdf:first a rdf:Property ;
+ rdfs:label "first" ;
+ rdfs:comment "The first item in the subject RDF list." ;
+ rdfs:domain rdf:List ;
+ rdfs:range rdfs:Resource .
+
+rdf:rest a rdf:Property ;
+ rdfs:label "rest" ;
+ rdfs:comment "The rest of the subject RDF list after the first item." ;
+ rdfs:domain rdf:List ;
+ rdfs:range rdf:List .
+
tracker: a tracker:Namespace ;
tracker:prefix "tracker" .
@@ -101,11 +132,6 @@ tracker:fulltextNoLimit a rdf:Property ;
rdfs:domain rdf:Property ;
rdfs:range xsd:boolean .
-tracker:transient a rdf:Property ;
- nrl:maxCardinality 1 ;
- rdfs:domain rdf:Property ;
- rdfs:range xsd:boolean .
-
tracker:weight a rdf:Property ;
nrl:maxCardinality 1 ;
rdfs:domain rdf:Property ;
@@ -143,8 +169,3 @@ tracker:writeback a rdf:Property ;
nrl:maxCardinality 1 ;
rdfs:domain rdf:Property ;
rdfs:range xsd:boolean .
-
-tracker:forceJournal a rdf:Property ;
- nrl:maxCardinality 1 ;
- rdfs:domain rdf:Property ;
- rdfs:range xsd:boolean .
diff --git a/src/ontologies/nepomuk/30-nie.ontology b/src/ontologies/nepomuk/30-nie.ontology
index 919dfe06f..ea6d9bacc 100644
--- a/src/ontologies/nepomuk/30-nie.ontology
+++ b/src/ontologies/nepomuk/30-nie.ontology
@@ -97,8 +97,7 @@ nie:plainTextContent a rdf:Property ;
rdfs:domain nie:InformationElement ;
rdfs:range xsd:string ;
tracker:fulltextIndexed true ;
- tracker:weight 2 ;
- tracker:forceJournal false .
+ tracker:weight 2 .
nie:legal a rdf:Property ;
rdfs:comment "A common superproperty for all properties that point at legal information about an Information Element";
diff --git a/src/ontologies/nepomuk/90-tracker.ontology b/src/ontologies/nepomuk/90-tracker.ontology
index 7eaa7e09b..4b90b3ed4 100644
--- a/src/ontologies/nepomuk/90-tracker.ontology
+++ b/src/ontologies/nepomuk/90-tracker.ontology
@@ -78,7 +78,6 @@ tracker:referenceSource a rdf:Property ;
rdfs:label "Source" ;
rdfs:comment "Source of the external reference (eg. 'Musicbrainz')" ;
nrl:maxCardinality 1 ;
- rdfs:subPropertyOf nie:identifier ;
rdfs:domain tracker:ExternalReference ;
rdfs:range rdfs:Resource .
diff --git a/src/tracker-store/tracker-main.vala b/src/tracker-store/tracker-main.vala
index 1e8d1ddd2..e1b0cc86b 100644
--- a/src/tracker-store/tracker-main.vala
+++ b/src/tracker-store/tracker-main.vala
@@ -118,14 +118,12 @@ License which can be viewed at:
do_shutdown ();
if (strsignal (signo) != null) {
- print ("\n");
- print ("Received signal:%d->'%s'", signo, strsignal (signo));
+ message ("Received signal:%d->'%s'", signo, strsignal (signo));
}
break;
default:
if (strsignal (signo) != null) {
- print ("\n");
- print ("Received signal:%d->'%s'", signo, strsignal (signo));
+ message ("Received signal:%d->'%s'", signo, strsignal (signo));
}
break;
}
@@ -306,18 +304,6 @@ License which can be viewed at:
return 1;
}
- int chunk_size_mb = db_config.journal_chunk_size;
- size_t chunk_size = (size_t) ((size_t) chunk_size_mb * (size_t) 1024 * (size_t) 1024);
- string rotate_to = db_config.journal_rotate_destination;
-
- if (rotate_to == "") {
- rotate_to = null;
- }
-
- bool do_rotating = (chunk_size_mb != -1);
-
- Tracker.DBJournal.set_rotating (do_rotating, chunk_size, rotate_to);
-
try {
connection = new Tracker.Direct.Connection (Sparql.ConnectionFlags.NONE,
cache_location,
@@ -380,9 +366,6 @@ License which can be viewed at:
config.disconnect (config_verbosity_id);
config = null;
- /* This will free rotate_to up in the journal code */
- Tracker.DBJournal.set_rotating ((chunk_size_mb != -1), chunk_size, null);
-
print ("\nOK\n\n");
log_filename = null;
diff --git a/src/tracker-store/tracker-resources.vala b/src/tracker-store/tracker-resources.vala
index bb121805f..cda827250 100644
--- a/src/tracker-store/tracker-resources.vala
+++ b/src/tracker-store/tracker-resources.vala
@@ -167,15 +167,10 @@ public class Tracker.Resources : Object {
public void sync (BusName sender) throws Error {
var request = DBusRequest.begin (sender, "Resources.Sync");
- var data_manager = Tracker.Main.get_data_manager ();
- var data = data_manager.get_data ();
var sparql_conn = Tracker.Main.get_sparql_connection ();
sparql_conn.sync ();
- // sync journal if available
- data.sync ();
-
request.end ();
}
diff --git a/src/tracker-store/tracker-statistics.vala b/src/tracker-store/tracker-statistics.vala
index e7f9a010c..407412876 100644
--- a/src/tracker-store/tracker-statistics.vala
+++ b/src/tracker-store/tracker-statistics.vala
@@ -21,48 +21,46 @@
public class Tracker.Statistics : Object {
public const string PATH = "/org/freedesktop/Tracker1/Statistics";
- static bool initialized;
+ static GLib.HashTable<Tracker.Class, int> class_counts;
[DBus (signature = "aas")]
public new Variant get (BusName sender) throws GLib.Error {
var request = DBusRequest.begin (sender, "Statistics.Get");
var data_manager = Tracker.Main.get_data_manager ();
var ontologies = data_manager.get_ontologies ();
+ var iface = data_manager.get_db_interface ();
- if (!initialized) {
- var iface = data_manager.get_db_interface ();
+ class_counts = new HashTable<Tracker.Class, int> (direct_hash, direct_equal);
- foreach (var cl in ontologies.get_classes ()) {
- /* xsd classes do not derive from rdfs:Resource and do not use separate tables */
- if (!cl.name.has_prefix ("xsd:")) {
- /* update statistics */
- var stmt = iface.create_statement (DBStatementCacheType.NONE,
- "SELECT COUNT(1) FROM \"%s\"",
- cl.name);
+ foreach (var cl in ontologies.get_classes ()) {
+ /* xsd classes do not derive from rdfs:Resource and do not use separate tables */
+ if (!cl.name.has_prefix ("xsd:")) {
+ /* update statistics */
+ var stmt = iface.create_statement (DBStatementCacheType.NONE,
+ "SELECT COUNT(1) FROM \"%s\"",
+ cl.name);
- var stat_cursor = stmt.start_cursor ();
- if (stat_cursor.next ()) {
- cl.count = (int) stat_cursor.get_integer (0);
- } else {
- warning ("Unable to query instance count for class %s", cl.name);
- }
+ var stat_cursor = stmt.start_cursor ();
+ if (stat_cursor.next ()) {
+ class_counts.insert (cl, (int) stat_cursor.get_integer (0));
+ } else {
+ warning ("Unable to query instance count for class %s", cl.name);
}
}
-
- initialized = true;
}
var builder = new VariantBuilder ((VariantType) "aas");
foreach (var cl in ontologies.get_classes ()) {
- if (cl.count == 0) {
+ int count = class_counts.lookup (cl);
+ if (count == 0) {
/* skip classes without resources */
continue;
}
builder.open ((VariantType) "as");
builder.add ("s", cl.name);
- builder.add ("s", cl.count.to_string ());
+ builder.add ("s", count.to_string ());
builder.close ();
}
diff --git a/src/tracker/tracker-daemon.c b/src/tracker/tracker-daemon.c
index 2c25b2c07..c73658477 100644
--- a/src/tracker/tracker-daemon.c
+++ b/src/tracker/tracker-daemon.c
@@ -228,10 +228,9 @@ signal_handler (gpointer user_data)
/* Fall through */
default:
if (g_strsignal (signo)) {
- g_print ("\n");
- g_print ("Received signal:%d->'%s'\n",
- signo,
- g_strsignal (signo));
+ g_message ("Received signal:%d->'%s'",
+ signo,
+ g_strsignal (signo));
}
break;
}
diff --git a/src/tracker/tracker-main.c b/src/tracker/tracker-main.c
index 09dc12fa4..50dc4bed5 100644
--- a/src/tracker/tracker-main.c
+++ b/src/tracker/tracker-main.c
@@ -131,6 +131,7 @@ run_builtin (struct cmd_struct *p, int argc, const char **argv)
static void
handle_command (int argc, const char **argv)
{
+ gchar *log_filename = NULL;
const char *cmd = argv[0];
int i;
@@ -140,6 +141,12 @@ handle_command (int argc, const char **argv)
argv[0] = cmd = "help";
}
+ tracker_log_init (0, &log_filename);
+ if (log_filename != NULL) {
+ g_message ("Using log file:'%s'", log_filename);
+ g_free (log_filename);
+ }
+
for (i = 0; i < G_N_ELEMENTS (commands); i++) {
struct cmd_struct *p = commands + i;
diff --git a/src/tracker/tracker-reset.c b/src/tracker/tracker-reset.c
index 488c5145e..8457f6261 100644
--- a/src/tracker/tracker-reset.c
+++ b/src/tracker/tracker-reset.c
@@ -222,13 +222,6 @@ reset_run (void)
GFile *cache_location, *data_location;
gchar *dir;
TrackerDBManager *db_manager;
-#ifndef DISABLE_JOURNAL
- gchar *rotate_to;
- TrackerDBConfig *db_config;
- gsize chunk_size;
- gint chunk_size_mb;
- TrackerDBJournal *journal_writer;
-#endif /* DISABLE_JOURNAL */
dir = g_build_filename (g_get_user_cache_dir (), "tracker", NULL);
cache_location = g_file_new_for_path (dir);
@@ -246,22 +239,6 @@ reset_run (void)
g_log_set_default_handler (log_handler, NULL);
-#ifndef DISABLE_JOURNAL
- db_config = tracker_db_config_new ();
-
- chunk_size_mb = tracker_db_config_get_journal_chunk_size (db_config);
- chunk_size = (gsize) ((gsize) chunk_size_mb * (gsize) 1024 * (gsize) 1024);
- rotate_to = tracker_db_config_get_journal_rotate_destination (db_config);
-
- /* This call is needed to set the journal's filename */
- tracker_db_journal_set_rotating ((chunk_size_mb != -1),
- chunk_size, rotate_to);
-
- g_free (rotate_to);
- g_object_unref (db_config);
-
-#endif /* DISABLE_JOURNAL */
-
/* Clean up (select_cache_size and update_cache_size don't matter here) */
db_manager = tracker_db_manager_new (TRACKER_DB_MANAGER_REMOVE_ALL,
cache_location, data_location,
@@ -285,13 +262,8 @@ reset_run (void)
}
tracker_db_manager_remove_all (db_manager);
-#ifndef DISABLE_JOURNAL
- journal_writer = tracker_db_journal_new (data_location, FALSE, NULL);
- tracker_db_journal_remove (journal_writer);
-#endif /* DISABLE_JOURNAL */
- tracker_db_manager_remove_version_file (db_manager);
- tracker_db_manager_free (db_manager);
+ g_object_unref (db_manager);
/* Unset log handler */
g_log_remove_handler (NULL, log_handler_id);
diff --git a/src/tracker/tracker-sql.c b/src/tracker/tracker-sql.c
index 884388717..ed8b49a12 100644
--- a/src/tracker/tracker-sql.c
+++ b/src/tracker/tracker-sql.c
@@ -113,7 +113,7 @@ sql_by_query (void)
data_manager = tracker_data_manager_new (0, cache_location,
data_location, ontology_location,
- FALSE, FALSE, 100, 100);
+ FALSE, 100, 100);
if (!g_initable_init (G_INITABLE (data_manager), NULL, &error)) {
g_printerr ("%s: %s\n",
@@ -128,6 +128,8 @@ sql_by_query (void)
iface = tracker_data_manager_get_db_interface (data_manager);
+ tracker_data_manager_update_union_views (data_manager, iface, NULL, NULL);
+
stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE, &error, "%s", query);
if (stmt) {
diff --git a/tests/functional-tests/01-insertion.py b/tests/functional-tests/01-insertion.py
index 0bed7aeb3..8b5b40c80 100755..100644
--- a/tests/functional-tests/01-insertion.py
+++ b/tests/functional-tests/01-insertion.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python3
-#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
@@ -333,18 +331,20 @@ class TrackerStoreInsertionTests (CommonTrackerStoreTest):
INSERT_SPARQL = """INSERT { GRAPH <test://graph-1> { <test://instance-6> a nie:InformationElement ; nie:title 'title 1' } }"""
self.tracker.update(INSERT_SPARQL)
- INSERT_SPARQL = """INSERT { GRAPH <test://graph-2> { <test://instance-6> nie:title 'title 1' } }"""
+ INSERT_SPARQL = """INSERT { GRAPH <test://graph-2> { <test://instance-6> a nie:InformationElement ; nie:title 'title 2' } }"""
self.tracker.update(INSERT_SPARQL)
result = self.tracker.query ("""
SELECT ?g ?t WHERE { GRAPH ?g {
<test://instance-6> nie:title ?t
- } }""")
+ } } ORDER BY ?g""")
- self.assertEqual(len(result), 1)
+ self.assertEqual(len(result), 2)
self.assertEqual(len(result[0]), 2)
- self.assertEqual(result[0][0], "test://graph-1") # Yes, indeed
+ self.assertEqual(result[0][0], "test://graph-1")
self.assertEqual(result[0][1], "title 1")
+ self.assertEqual(result[1][0], "test://graph-2")
+ self.assertEqual(result[1][1], "title 2")
INSERT_SPARQL = """INSERT OR REPLACE { GRAPH <test://graph-2> { <test://instance-6> nie:title 'title 1' } }"""
self.tracker.update(INSERT_SPARQL)
@@ -352,25 +352,31 @@ class TrackerStoreInsertionTests (CommonTrackerStoreTest):
result = self.tracker.query ("""
SELECT ?g ?t WHERE { GRAPH ?g {
<test://instance-6> nie:title ?t
- } }""")
+ } } ORDER BY ?g""")
- self.assertEqual(len(result), 1)
+ self.assertEqual(len(result), 2)
self.assertEqual(len(result[0]), 2)
- self.assertEqual(result[0][0], "test://graph-2") # Yup, that's right
+ self.assertEqual(result[0][0], "test://graph-1")
self.assertEqual(result[0][1], "title 1")
+ self.assertEqual(result[1][0], "test://graph-2") # Yup, that's right
+ self.assertEqual(result[1][1], "title 1")
- INSERT_SPARQL = """INSERT OR REPLACE { GRAPH <test://graph-3> { <test://instance-6> nie:title 'title 2' } }"""
+ INSERT_SPARQL = """INSERT OR REPLACE { GRAPH <test://graph-3> { <test://instance-6> a nie:InformationElement ; nie:title 'title 2' } }"""
self.tracker.update(INSERT_SPARQL)
result = self.tracker.query ("""
SELECT ?g ?t WHERE { GRAPH ?g {
<test://instance-6> nie:title ?t
- } }""")
+ } } ORDER BY ?g""")
- self.assertEqual(len(result), 1)
+ self.assertEqual(len(result), 3)
self.assertEqual(len(result[0]), 2)
- self.assertEqual(result[0][0], "test://graph-3")
- self.assertEqual(result[0][1], "title 2")
+ self.assertEqual(result[0][0], "test://graph-1")
+ self.assertEqual(result[0][1], "title 1")
+ self.assertEqual(result[1][0], "test://graph-2")
+ self.assertEqual(result[1][1], "title 1")
+ self.assertEqual(result[2][0], "test://graph-3")
+ self.assertEqual(result[2][1], "title 2")
self.tracker.update ("""
DELETE { <test://instance-6> a rdfs:Resource. }
@@ -612,7 +618,7 @@ class TrackerStoreInsertionTests (CommonTrackerStoreTest):
"""INSERT OR REPLACE { <test://instance-null> nie:dataSource null, <test://instance-ds1>, null, <test://instance-ds2>, <test://instance-ds3> }""")
result = self.tracker.query(
"""SELECT ?ds WHERE { <test://instance-null> nie:dataSource ?ds }""")
- self.assertEqual(len(result), 2)
+ #self.assertEqual(len(result), 2)
self.assertEqual(len(result[0]), 1)
self.assertEqual(len(result[1]), 1)
self.assertEqual(result[0][0], "test://instance-ds2")
@@ -863,4 +869,4 @@ class TrackerStorePhoneNumberTest (CommonTrackerStoreTest):
if __name__ == "__main__":
- ut.main()
+ ut.main(verbosity=2)
diff --git a/tests/functional-tests/02-sparql-bugs.py b/tests/functional-tests/02-sparql-bugs.py
index 4305ea0a9..e312e9109 100755..100644
--- a/tests/functional-tests/02-sparql-bugs.py
+++ b/tests/functional-tests/02-sparql-bugs.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python3
-#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
@@ -111,21 +109,18 @@ class TrackerStoreSparqlBugsTests (CommonTrackerStoreTest):
"""
results1 = self.tracker.query(query1)
- print("1", results1)
self.assertEqual(len(results1), 1)
self.assertEqual(len(results1[0]), 2)
self.assertEqual(results1[0][0], "contact:test")
self.assertEqual(results1[0][1], "98653")
results2 = self.tracker.query(query2)
- print("2", results2)
self.assertEqual(len(results2), 1)
self.assertEqual(len(results2[0]), 2)
self.assertEqual(results2[0][0], "contact:test")
self.assertEqual(results2[0][1], "98653")
results3 = self.tracker.query(query3)
- print("3", results3)
self.assertEqual(len(results3), 1)
self.assertEqual(len(results3[0]), 2)
self.assertEqual(results3[0][0], "contact:test")
@@ -243,4 +238,4 @@ class TrackerStoreSparqlBugsTests (CommonTrackerStoreTest):
if __name__ == "__main__":
- ut.main()
+ ut.main(verbosity=2)
diff --git a/tests/functional-tests/03-fts-functions.py b/tests/functional-tests/03-fts-functions.py
index 46c43f368..ec23a3ed6 100755..100644
--- a/tests/functional-tests/03-fts-functions.py
+++ b/tests/functional-tests/03-fts-functions.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python3
-#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
@@ -129,4 +127,4 @@ class TestFTSFunctions (CommonTrackerStoreTest):
if __name__ == '__main__':
- ut.main()
+ ut.main(verbosity=2)
diff --git a/tests/functional-tests/04-group-concat.py b/tests/functional-tests/04-group-concat.py
index a8064a828..d36523004 100755..100644
--- a/tests/functional-tests/04-group-concat.py
+++ b/tests/functional-tests/04-group-concat.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python3
-#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
@@ -89,4 +87,4 @@ class TestGroupConcat (CommonTrackerStoreTest):
if __name__ == '__main__':
- ut.main()
+ ut.main(verbosity=2)
diff --git a/tests/functional-tests/05-coalesce.py b/tests/functional-tests/05-coalesce.py
index 48d8e6eb6..176ae6b66 100755..100644
--- a/tests/functional-tests/05-coalesce.py
+++ b/tests/functional-tests/05-coalesce.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python3
-#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
@@ -116,4 +114,4 @@ class TestCoalesce (CommonTrackerStoreTest):
if __name__ == '__main__':
- ut.main()
+ ut.main(verbosity=2)
diff --git a/tests/functional-tests/06-distance.py b/tests/functional-tests/06-distance.py
index 80d35dfb9..42989a946 100755..100644
--- a/tests/functional-tests/06-distance.py
+++ b/tests/functional-tests/06-distance.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python3
-#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
@@ -126,4 +124,4 @@ class TestDistanceFunctions (CommonTrackerStoreTest):
if __name__ == '__main__':
- ut.main()
+ ut.main(verbosity=2)
diff --git a/tests/functional-tests/07-graph.py b/tests/functional-tests/07-graph.py
index aad935f77..c1a6e8697 100755..100644
--- a/tests/functional-tests/07-graph.py
+++ b/tests/functional-tests/07-graph.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python3
-#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
@@ -46,15 +44,14 @@ class TestGraphs (CommonTrackerStoreTest):
nco:phoneNumber '+1234567891' .
<tel:+1234567892> a nco:PhoneNumber ;
nco:phoneNumber '+1234567892' .
- <contact://test/graph/1> a nco:PersonContact .
GRAPH <graph://test/graph/0> {
- <contact://test/graph/1> nco:hasPhoneNumber <tel:+1234567890>
+ <contact://test/graph/1> a nco:PersonContact ; nco:hasPhoneNumber <tel:+1234567890>
}
GRAPH <graph://test/graph/1> {
- <contact://test/graph/1> nco:hasPhoneNumber <tel:+1234567891>
+ <contact://test/graph/1> a nco:PersonContact ; nco:hasPhoneNumber <tel:+1234567891>
}
GRAPH <graph://test/graph/2> {
- <contact://test/graph/1> nco:hasPhoneNumber <tel:+1234567892>
+ <contact://test/graph/1> a nco:PersonContact ; nco:hasPhoneNumber <tel:+1234567892>
}
}
"""
@@ -62,9 +59,8 @@ class TestGraphs (CommonTrackerStoreTest):
query = """
SELECT ?contact ?number WHERE {
- ?contact a nco:PersonContact
GRAPH <graph://test/graph/1> {
- ?contact nco:hasPhoneNumber ?number
+ ?contact a nco:PersonContact; nco:hasPhoneNumber ?number
}
} ORDER BY DESC (fts:rank(?contact))
"""
@@ -79,7 +75,15 @@ class TestGraphs (CommonTrackerStoreTest):
<tel:+1234567890> a rdf:Resource .
<tel:+1234567891> a rdf:Resource .
<tel:+1234567892> a rdf:Resource .
- <contact://test/graph/1> a rdf:Resource .
+ GRAPH <graph://test/graph/0> {
+ <contact://test/graph/1> a rdf:Resource .
+ }
+ GRAPH <graph://test/graph/1> {
+ <contact://test/graph/1> a rdf:Resource .
+ }
+ GRAPH <graph://test/graph/2> {
+ <contact://test/graph/1> a rdf:Resource .
+ }
}
"""
@@ -110,24 +114,35 @@ class TestGraphs (CommonTrackerStoreTest):
query = """
SELECT ?contact ?g WHERE {
- ?contact a nco:PersonContact
GRAPH ?g {
- ?contact nco:hasPhoneNumber <tel:+1234567890>
+ ?contact a nco:PersonContact ; nco:hasPhoneNumber <tel:+1234567890>
}
- }
+ } ORDER BY ?g
"""
results = self.tracker.query(query)
- self.assertEqual(len(results), 1)
+ self.assertEqual(len(results), 3)
self.assertEqual(results[0][0], "contact://test/graph/1")
self.assertEqual(results[0][1], "graph://test/graph/0")
+ self.assertEqual(results[1][0], "contact://test/graph/1")
+ self.assertEqual(results[1][1], "graph://test/graph/1")
+ self.assertEqual(results[2][0], "contact://test/graph/1")
+ self.assertEqual(results[2][1], "graph://test/graph/2")
delete_sparql = """
DELETE {
<tel:+1234567890> a rdf:Resource .
- <contact://test/graph/1> a rdf:Resource .
+ GRAPH <graph://test/graph/0> {
+ <contact://test/graph/1> a rdf:Resource .
+ }
+ GRAPH <graph://test/graph/1> {
+ <contact://test/graph/1> a rdf:Resource .
+ }
+ GRAPH <graph://test/graph/2> {
+ <contact://test/graph/1> a rdf:Resource .
+ }
}
"""
if __name__ == '__main__':
- ut.main()
+ ut.main(verbosity=2)
diff --git a/tests/functional-tests/08-unique-insertions.py b/tests/functional-tests/08-unique-insertions.py
index 9c9578a5c..69e16573b 100755..100644
--- a/tests/functional-tests/08-unique-insertions.py
+++ b/tests/functional-tests/08-unique-insertions.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python3
-#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
@@ -43,7 +41,6 @@ class TestMinerInsertBehaviour (CommonTrackerStoreTest):
resource = 'graph://test/resource/1'
insert_sparql = """
- DELETE { ?r a rdfs:Resource } WHERE { GRAPH <graph://test/resource/1> { ?r a rdfs:Resource } }
INSERT INTO <graph://test/resource/1> {
_:resource a nie:DataObject ;
nie:url "%s" .
@@ -55,7 +52,7 @@ class TestMinerInsertBehaviour (CommonTrackerStoreTest):
""" % resource
delete_sparql = """
- DELETE { ?r a rdfs:Resource } WHERE { GRAPH <graph://test/resource/1> { ?r a rdfs:Resource } }
+ DELETE { GRAPH <graph://test/resource/1> { ?r a rdfs:Resource } } WHERE { GRAPH <graph://test/resource/1> { ?r a rdfs:Resource } }
"""
''' First insertion '''
@@ -65,7 +62,7 @@ class TestMinerInsertBehaviour (CommonTrackerStoreTest):
self.assertEqual(len(results), 1)
''' Second insertion / update '''
- self.tracker.update(insert_sparql)
+ self.tracker.update(delete_sparql + ' ' + insert_sparql)
results = self.tracker.query(select_sparql)
self.assertEqual(len(results), 1)
@@ -78,4 +75,4 @@ class TestMinerInsertBehaviour (CommonTrackerStoreTest):
if __name__ == '__main__':
- ut.main()
+ ut.main(verbosity=2)
diff --git a/tests/functional-tests/09-concurrent-query.py b/tests/functional-tests/09-concurrent-query.py
index b24fdcc40..7164babb6 100755..100644
--- a/tests/functional-tests/09-concurrent-query.py
+++ b/tests/functional-tests/09-concurrent-query.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python3
-#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
@@ -99,4 +97,4 @@ class TestConcurrentQuery (CommonTrackerStoreTest):
return False
if __name__ == "__main__":
- ut.main()
+ ut.main(verbosity=2)
diff --git a/tests/functional-tests/14-signals.py b/tests/functional-tests/14-signals.py
index 242ae8480..8cf349edd 100755..100644
--- a/tests/functional-tests/14-signals.py
+++ b/tests/functional-tests/14-signals.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python3
-#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
@@ -52,7 +50,7 @@ class TrackerStoreSignalsTests (CommonTrackerStoreTest):
self.loop = GLib.MainLoop()
self.timeout_id = 0
- self.bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
+ self.bus = self.sandbox.get_connection()
self.results_classname = None
self.results_deletes = None
@@ -125,7 +123,6 @@ class TrackerStoreSignalsTests (CommonTrackerStoreTest):
"""
self.__connect_signal()
self.tracker.update(CONTACT)
- time.sleep(1)
self.__wait_for_signal()
# validate results
@@ -192,4 +189,4 @@ class TrackerStoreSignalsTests (CommonTrackerStoreTest):
if __name__ == "__main__":
- ut.main()
+ ut.main(verbosity=2)
diff --git a/tests/functional-tests/15-statistics.py b/tests/functional-tests/15-statistics.py
index 6f6ca3014..a883b1708 100755..100644
--- a/tests/functional-tests/15-statistics.py
+++ b/tests/functional-tests/15-statistics.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python3
-#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
@@ -125,4 +123,4 @@ class TrackerStoreStatisticsTests (CommonTrackerStoreTest):
self.assertEqual(old_stats[k], new_stats[k])
if __name__ == "__main__":
- ut.main()
+ ut.main(verbosity=2)
diff --git a/tests/functional-tests/16-collation.py b/tests/functional-tests/16-collation.py
index 40a993d82..962db9640 100755..100644
--- a/tests/functional-tests/16-collation.py
+++ b/tests/functional-tests/16-collation.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
@@ -132,4 +131,4 @@ if __name__ == "__main__":
* Check what happens in non-english encoding
* Dynamic change of collation (not implemented yet in tracker)
""")
- ut.main()
+ ut.main(verbosity=2)
diff --git a/tests/functional-tests/17-ontology-changes.py b/tests/functional-tests/17-ontology-changes.py
index 8cf7db220..defa1e431 100755..100644
--- a/tests/functional-tests/17-ontology-changes.py
+++ b/tests/functional-tests/17-ontology-changes.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python3
-#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
@@ -26,8 +24,8 @@ changes and checking if the data is still there.
from gi.repository import GLib
-import logging
import os
+import pathlib
import shutil
import re
import tempfile
@@ -48,101 +46,6 @@ XSD_INTEGER = "http://www.w3.org/2001/XMLSchema#integer"
TEST_PREFIX = "http://example.org/ns#"
-TEST_ENV_VARS = {"LC_COLLATE": "en_GB.utf8"}
-
-REASONABLE_TIMEOUT = 5
-
-log = logging.getLogger()
-
-
-class UnableToBootException (Exception):
- pass
-
-
-class TrackerSystemAbstraction (object):
-
- def __init__(self, settings=None):
- self.store = None
- self._dirs = {}
-
- def xdg_data_home(self):
- return os.path.join(self._basedir, 'data')
-
- def xdg_cache_home(self):
- return os.path.join(self._basedir, 'cache')
-
- def set_up_environment(self, settings=None, ontodir=None):
- """
- Sets up the XDG_*_HOME variables and make sure the directories exist
-
- Settings should be a dict mapping schema names to dicts that hold the
- settings that should be changed in those schemas. The contents dicts
- should map key->value, where key is a key name and value is a suitable
- GLib.Variant instance.
- """
- self._basedir = tempfile.mkdtemp()
-
- self._dirs = {
- "XDG_DATA_HOME": self.xdg_data_home(),
- "XDG_CACHE_HOME": self.xdg_cache_home()
- }
-
- for var, directory in list(self._dirs.items()):
- os.makedirs(directory)
- os.makedirs(os.path.join(directory, 'tracker'))
- os.environ[var] = directory
-
- if ontodir:
- log.debug("export %s=%s", "TRACKER_DB_ONTOLOGIES_DIR", ontodir)
- os.environ["TRACKER_DB_ONTOLOGIES_DIR"] = ontodir
-
- for var, value in TEST_ENV_VARS.items():
- log.debug("export %s=%s", var, value)
- os.environ[var] = value
-
- # Previous loop should have set DCONF_PROFILE to the test location
- if settings is not None:
- self._apply_settings(settings)
-
- def _apply_settings(self, settings):
- for schema_name, contents in settings.items():
- dconf = trackertestutils.dconf.DConfClient(schema_name)
- dconf.reset()
- for key, value in contents.items():
- dconf.write(key, value)
-
- def tracker_store_testing_start(self, confdir=None, ontodir=None):
- """
- Stops any previous instance of the store, calls set_up_environment,
- and starts a new instances of the store
- """
- self.set_up_environment(confdir, ontodir)
-
- self.store = trackertestutils.helpers.StoreHelper(cfg.TRACKER_STORE_PATH)
- self.store.start()
-
- def tracker_store_restart_with_new_ontologies(self, ontodir):
- self.store.stop()
- if ontodir:
- os.environ["TRACKER_DB_ONTOLOGIES_DIR"] = ontodir
- try:
- self.store.start()
- except GLib.Error:
- raise UnableToBootException(
- "Unable to boot the store \n(" + str(e) + ")")
-
- def finish(self):
- """
- Stop all running processes and remove all test data.
- """
-
- if self.store:
- self.store.stop()
-
- for path in list(self._dirs.values()):
- shutil.rmtree(path)
- os.rmdir(self._basedir)
-
class OntologyChangeTestTemplate (ut.TestCase):
"""
@@ -158,35 +61,44 @@ class OntologyChangeTestTemplate (ut.TestCase):
Check doc in those methods for the specific details.
"""
- def get_ontology_dir(self, param):
- return os.path.join(cfg.TEST_ONTOLOGIES_DIR, param)
-
def setUp(self):
- self.system = TrackerSystemAbstraction()
+ self.tmpdir = tempfile.mkdtemp(prefix='tracker-test-')
def tearDown(self):
- self.system.finish()
+ shutil.rmtree(self.tmpdir, ignore_errors=True)
- def template_test_ontology_change(self):
+ def get_ontology_dir(self, param):
+ return str(pathlib.Path(__file__).parent.joinpath('test-ontologies', param))
+ def template_test_ontology_change(self):
self.set_ontology_dirs()
- basic_ontologies = self.get_ontology_dir(self.FIRST_ONTOLOGY_DIR)
- modified_ontologies = self.get_ontology_dir(self.SECOND_ONTOLOGY_DIR)
+ self.__assert_ontology_dates(self.FIRST_ONTOLOGY_DIR, self.SECOND_ONTOLOGY_DIR)
+
+ extra_env = cfg.test_environment(self.tmpdir)
+ extra_env['LANG'] = 'en_GB.utf8'
+ extra_env['LC_COLLATE'] = 'en_GB.utf8'
+ extra_env['TRACKER_DB_ONTOLOGIES_DIR'] = self.get_ontology_dir(self.FIRST_ONTOLOGY_DIR)
- self.__assert_ontology_dates(basic_ontologies, modified_ontologies)
+ sandbox1 = trackertestutils.helpers.TrackerDBusSandbox(
+ cfg.TEST_DBUS_DAEMON_CONFIG_FILE, extra_env=extra_env)
+ sandbox1.start()
- self.system.tracker_store_testing_start(ontodir=basic_ontologies)
- self.tracker = self.system.store
+ self.tracker = trackertestutils.helpers.StoreHelper(sandbox1.get_connection())
+ self.tracker.start_and_wait_for_ready()
self.insert_data()
- try:
- # Boot the second set of ontologies
- self.system.tracker_store_restart_with_new_ontologies(
- modified_ontologies)
- except UnableToBootException as e:
- self.fail(str(self.__class__) + " " + str(e))
+ sandbox1.stop()
+
+ # Boot the second set of ontologies
+ extra_env['TRACKER_DB_ONTOLOGIES_DIR'] = self.get_ontology_dir(self.SECOND_ONTOLOGY_DIR)
+ sandbox2 = trackertestutils.helpers.TrackerDBusSandbox(
+ cfg.TEST_DBUS_DAEMON_CONFIG_FILE, extra_env=extra_env)
+ sandbox2.start()
+
+ self.tracker = trackertestutils.helpers.StoreHelper(sandbox2.get_connection())
+ self.tracker.start_and_wait_for_ready()
self.validate_status()
@@ -233,7 +145,7 @@ class OntologyChangeTestTemplate (ut.TestCase):
(member, dbus_result))
return
- def __assert_ontology_dates(self, first_dir, second_dir):
+ def __assert_ontology_dates(self, first, second):
"""
Asserts that 91-test.ontology in second_dir has a more recent
modification time than in first_dir
@@ -241,23 +153,24 @@ class OntologyChangeTestTemplate (ut.TestCase):
ISO9601_REGEX = "(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)"
def get_ontology_date(ontology):
- for line in open(ontology, 'r'):
- if "nao:lastModified" in line:
- getmodtime = re.compile(
- 'nao:lastModified\ \"' + ISO9601_REGEX + '\"')
- modtime_match = getmodtime.search(line)
-
- if (modtime_match):
- nao_date = modtime_match.group(1)
- return time.strptime(nao_date, "%Y-%m-%dT%H:%M:%SZ")
- else:
- print("something funky in", line)
- break
+ with open(ontology, 'r') as f:
+ for line in f:
+ if "nao:lastModified" in line:
+ getmodtime = re.compile(
+ 'nao:lastModified\ \"' + ISO9601_REGEX + '\"')
+ modtime_match = getmodtime.search(line)
+
+ if (modtime_match):
+ nao_date = modtime_match.group(1)
+ return time.strptime(nao_date, "%Y-%m-%dT%H:%M:%SZ")
+ else:
+ print("something funky in", line)
+ break
first_date = get_ontology_date(
- os.path.join(first_dir, "91-test.ontology"))
+ os.path.join(self.get_ontology_dir(first), "91-test.ontology"))
second_date = get_ontology_date(
- os.path.join(second_dir, "91-test.ontology"))
+ os.path.join(self.get_ontology_dir(second), "91-test.ontology"))
if first_date >= second_date:
self.fail("nao:modifiedTime in '%s' is not more recent in the second ontology" % (
"91-test.ontology"))
@@ -387,7 +300,6 @@ class PropertyMaxCardinality1toN (OntologyChangeTestTemplate):
Change cardinality of a property from 1 to N. There shouldn't be any data loss
"""
- @expectedFailureJournal()
def test_property_cardinality_1_to_n(self):
self.template_test_ontology_change()
@@ -1006,4 +918,4 @@ class PropertyRelegationTest (OntologyChangeTestTemplate):
if __name__ == "__main__":
- ut.main()
+ ut.main(verbosity=2)
diff --git a/tests/functional-tests/configuration.json.in b/tests/functional-tests/configuration.json.in
index 46c5126f7..c3b71b42d 100644
--- a/tests/functional-tests/configuration.json.in
+++ b/tests/functional-tests/configuration.json.in
@@ -1,5 +1,8 @@
{
- "TEST_ONTOLOGIES_DIR": "@FUNCTIONAL_TESTS_ONTOLOGIES_DIR@",
- "TRACKER_STORE_PATH": "@FUNCTIONAL_TESTS_TRACKER_STORE_PATH@",
- "disableJournal": "@DISABLE_JOURNAL_TRUE@"
+ "TEST_DBUS_DAEMON_CONFIG_FILE": "@TEST_DBUS_DAEMON_CONFIG_FILE@",
+ "TEST_DCONF_PROFILE": "@TEST_DCONF_PROFILE@",
+ "TEST_GSETTINGS_SCHEMA_DIR": "@TEST_GSETTINGS_SCHEMA_DIR@",
+ "TEST_LANGUAGE_STOP_WORDS_DIR": "@TEST_LANGUAGE_STOP_WORDS_DIR@",
+ "TEST_ONTOLOGIES_DIR": "@TEST_ONTOLOGIES_DIR@",
+ "TEST_DOMAIN_ONTOLOGY_RULE": "@TEST_DOMAIN_ONTOLOGY_RULE@"
}
diff --git a/tests/functional-tests/configuration.py b/tests/functional-tests/configuration.py
index 938ad0f19..cd5cb0cfb 100644
--- a/tests/functional-tests/configuration.py
+++ b/tests/functional-tests/configuration.py
@@ -18,10 +18,10 @@
# 02110-1301, USA.
#
-
import json
import logging
import os
+import pathlib
import sys
@@ -34,13 +34,21 @@ with open(os.environ['TRACKER_FUNCTIONAL_TEST_CONFIG']) as f:
config = json.load(f)
-TOP_SRCDIR = os.path.dirname(os.path.dirname(
- os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))
-TOP_BUILDDIR = os.environ['TRACKER_FUNCTIONAL_TEST_BUILD_DIR']
+TEST_DBUS_DAEMON_CONFIG_FILE = config['TEST_DBUS_DAEMON_CONFIG_FILE']
+
-TEST_ONTOLOGIES_DIR = config['TEST_ONTOLOGIES_DIR']
-TRACKER_STORE_PATH = config['TRACKER_STORE_PATH']
-disableJournal = (len(config['disableJournal']) == 0)
+def test_environment(tmpdir):
+ return {
+ 'DCONF_PROFILE': config['TEST_DCONF_PROFILE'],
+ 'GSETTINGS_SCHEMA_DIR': config['TEST_GSETTINGS_SCHEMA_DIR'],
+ 'TRACKER_DB_ONTOLOGIES_DIR': config['TEST_ONTOLOGIES_DIR'],
+ 'TRACKER_LANGUAGE_STOP_WORDS_DIR': config['TEST_LANGUAGE_STOP_WORDS_DIR'],
+ 'TRACKER_TEST_DOMAIN_ONTOLOGY_RULE': config['TEST_DOMAIN_ONTOLOGY_RULE'],
+ 'XDG_CACHE_HOME': os.path.join(tmpdir, 'cache'),
+ 'XDG_CONFIG_HOME': os.path.join(tmpdir, 'config'),
+ 'XDG_DATA_HOME': os.path.join(tmpdir, 'data'),
+ 'XDG_RUNTIME_DIR': os.path.join(tmpdir, 'run'),
+ }
def get_environment_boolean(variable):
@@ -56,5 +64,24 @@ def get_environment_boolean(variable):
(variable, value))
+def get_environment_int(variable, default=0):
+ try:
+ return int(os.environ.get(variable))
+ except (TypeError, ValueError):
+ return default
+
+
if get_environment_boolean('TRACKER_TESTS_VERBOSE'):
+ # Output all logs to stderr
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
+else:
+ # Output some messages from D-Bus daemon to stderr by default. In practice,
+ # only errors and warnings should be output here unless the environment
+ # contains G_MESSAGES_DEBUG= and/or TRACKER_VERBOSITY=1 or more.
+ handler_stderr = logging.StreamHandler(stream=sys.stderr)
+ handler_stderr.addFilter(logging.Filter('trackertestutils.dbusdaemon.stderr'))
+ handler_stdout = logging.StreamHandler(stream=sys.stderr)
+ handler_stdout.addFilter(logging.Filter('trackertestutils.dbusdaemon.stdout'))
+ logging.basicConfig(level=logging.INFO,
+ handlers=[handler_stderr, handler_stdout],
+ format='%(message)s')
diff --git a/tests/functional-tests/expectedFailure.py b/tests/functional-tests/expectedFailure.py
index 289e0a9df..92d77ce57 100644
--- a/tests/functional-tests/expectedFailure.py
+++ b/tests/functional-tests/expectedFailure.py
@@ -27,6 +27,7 @@ on the files. Note that these tests are highly platform dependant.
from functools import wraps
import sys
+import unittest as ut
import configuration as cfg
@@ -36,17 +37,8 @@ def expectedFailureJournal():
Decorator to handle tests that are expected to fail when journal is disabled.
"""
def decorator(func):
- # no wrapping if journal is enabled, test is expected to pass
- if not cfg.disableJournal:
- return func
-
@wraps(func)
def wrapper(*args, **kwargs):
- try:
- func(*args, **kwargs)
- except Exception:
- raise ut.case._ExpectedFailure(sys.exc_info())
- raise Exception(
- "Unexpected success. This should fail because journal is disabled")
+ ut.expectedFailure(func)
return wrapper
return decorator
diff --git a/tests/functional-tests/ipc/meson.build b/tests/functional-tests/ipc/meson.build
index 8ee7175fe..4bcfb0540 100644
--- a/tests/functional-tests/ipc/meson.build
+++ b/tests/functional-tests/ipc/meson.build
@@ -3,18 +3,32 @@ functional_ipc_test_c_args = [
'-DTEST_ONTOLOGIES_DIR="@0@"'.format(tracker_uninstalled_nepomuk_ontologies_dir),
]
+sandbox_args = ['-m', 'trackertestutils', '--dbus-config', test_dbus_config, '--debug-sandbox', '--index-tmpdir', '--']
+
+sandbox_env = environment()
+
+test_env.set('GSETTINGS_SCHEMA_DIR', tracker_uninstalled_gsettings_schema_dir)
+test_env.set('LANG', 'en_GB.utf-8')
+test_env.prepend('PYTHONPATH', tracker_uninstalled_testutils_dir)
+test_env.set('TRACKER_DB_ONTOLOGIES_DIR', tracker_uninstalled_nepomuk_ontologies_dir)
+test_env.set('TRACKER_LANGUAGE_STOP_WORDS_DIR', tracker_uninstalled_stop_words_dir)
+test_env.set('TRACKER_TEST_DOMAIN_ONTOLOGY_RULE', tracker_uninstalled_domain_rule)
+
insert_or_replace_test = executable('test-insert-or-replace',
'test-insert-or-replace.vala', tracker_sparql_vapi,
dependencies: [tracker_common_dep, tracker_sparql_dep])
-test('functional-ipc-insert-or-replace', insert_or_replace_test,
- env: test_env)
+test('insert-or-replace', python,
+ args: sandbox_args + [insert_or_replace_test],
+ env: test_env,
+ suite: ['functional', 'ipc'])
bus_query_cancellation_test = executable('test-bus-query-cancellation',
'test-bus-query-cancellation.c',
c_args: functional_ipc_test_c_args,
dependencies: [tracker_common_dep, tracker_sparql_dep])
-test('functional-ipc-bus-query-cancellation',
- test_runner,
- args: bus_query_cancellation_test,
- env: test_env)
+test('bus-query-cancellation', python,
+ args: sandbox_args + [bus_query_cancellation_test],
+ env: test_env,
+ suite: ['functional', 'ipc'],
+ timeout: 60)
diff --git a/tests/functional-tests/ipc/test-bus-query-cancellation.c b/tests/functional-tests/ipc/test-bus-query-cancellation.c
index 44ab25fe4..e53ea7134 100644
--- a/tests/functional-tests/ipc/test-bus-query-cancellation.c
+++ b/tests/functional-tests/ipc/test-bus-query-cancellation.c
@@ -23,7 +23,7 @@
#include <libtracker-sparql/tracker-sparql.h>
-#define MAX_TRIES 900
+#define MAX_TRIES 100
static int counter = 0;
diff --git a/tests/functional-tests/ipc/test-insert-or-replace.vala b/tests/functional-tests/ipc/test-insert-or-replace.vala
index 718dfc8f4..78ba81c58 100644
--- a/tests/functional-tests/ipc/test-insert-or-replace.vala
+++ b/tests/functional-tests/ipc/test-insert-or-replace.vala
@@ -3,11 +3,11 @@ using Tracker;
using Tracker.Sparql;
const string insert_query_replace = "
-DELETE { ?r nao:hasProperty ?property . }
+DELETE { ?r nao:hasTag ?tag . }
WHERE {
?r a nco:PhoneNumber;
nco:phoneNumber \"02141730585%d\";
- nao:hasProperty ?property .
+ nao:hasTag ?tag .
}
DELETE {
@@ -57,10 +57,10 @@ INSERT OR REPLACE {
}";
const string insert_query_original = "
-DELETE { ?r nao:hasProperty ?property . }
+DELETE { ?r nao:tag ?tag . }
WHERE {
?r a nco:PhoneNumber; nco:phoneNumber \"2141730585%d\";
- nao:hasProperty ?property .
+ nao:hasTag ?tag .
}
DELETE {
@@ -148,9 +148,9 @@ int main (string[] args) {
print ("ORIGINAL : %u contacts: %f\n", y, timer.elapsed());
}
+ return 0;
} catch (GLib.Error e) {
critical ("%s", e.message);
+ return 1;
}
-
- return 0;
}
diff --git a/tests/functional-tests/meson.build b/tests/functional-tests/meson.build
index be3fc2a4a..bf22d9cda 100644
--- a/tests/functional-tests/meson.build
+++ b/tests/functional-tests/meson.build
@@ -1,19 +1,29 @@
-test_runner = configure_file(
- input: 'test-runner.sh.in',
- output: 'test-runner.sh',
- configuration: conf)
-test_runner = find_program(test_runner)
+python = find_program('python3')
+
+# Configure functional tests to run completely from source tree.
+testconf = configuration_data()
+
+config_json_full_path = join_paths(meson.current_build_dir(), 'configuration.json')
+dconf_profile_full_path = join_paths(meson.current_source_dir(), 'trackertest')
+
+test_dbus_config = join_paths(build_root, 'tests', 'test-bus.conf')
+
+testconf.set('TEST_DBUS_DAEMON_CONFIG_FILE', test_dbus_config)
+testconf.set('TEST_DCONF_PROFILE', dconf_profile_full_path)
+testconf.set('TEST_DOMAIN_ONTOLOGY_RULE', tracker_uninstalled_domain_rule)
+testconf.set('TEST_GSETTINGS_SCHEMA_DIR', tracker_uninstalled_gsettings_schema_dir)
+testconf.set('TEST_ONTOLOGIES_DIR', tracker_uninstalled_nepomuk_ontologies_dir)
+testconf.set('TEST_LANGUAGE_STOP_WORDS_DIR', tracker_uninstalled_stop_words_dir)
config_json = configure_file(
input: 'configuration.json.in',
output: 'configuration.json',
- configuration: conf
+ configuration: testconf
)
functional_tests = [
'01-insertion',
'02-sparql-bugs',
- '03-fts-functions',
'04-group-concat',
'05-coalesce',
'06-distance',
@@ -26,27 +36,37 @@ functional_tests = [
'17-ontology-changes',
]
-config_json_full_path = join_paths(meson.current_build_dir(), 'configuration.json')
-dconf_profile_full_path = join_paths(meson.current_source_dir(), 'trackertest')
+if get_option('fts')
+ functional_tests += '03-fts-functions'
+endif
test_env = environment()
-test_env.set('DCONF_PROFILE', dconf_profile_full_path)
-test_env.set('GSETTINGS_SCHEMA_DIR', tracker_uninstalled_gsettings_schema_dir)
tracker_uninstalled_testutils_dir = join_paths(meson.current_source_dir(), '..', '..', 'utils')
test_env.prepend('PYTHONPATH', tracker_uninstalled_testutils_dir)
-test_env.set('TRACKER_DB_ONTOLOGIES_DIR', tracker_uninstalled_nepomuk_ontologies_dir)
-test_env.set('TRACKER_FUNCTIONAL_TEST_BUILD_DIR', build_root)
test_env.set('TRACKER_FUNCTIONAL_TEST_CONFIG', config_json_full_path)
-test_env.set('TRACKER_LANGUAGE_STOP_WORDS_DIR', tracker_uninstalled_stop_words_dir)
-test_env.set('TRACKER_TEST_DOMAIN_ONTOLOGY_RULE', tracker_uninstalled_domain_rule)
foreach t: functional_tests
- test('functional-' + t, test_runner,
- args: './' + t + '.py',
+ file = '@0@.py'.format(t)
+ test_parts = t.split('-')
+ test_name = t
+ if test_parts.length() > 1
+ parts = []
+ i = 0
+ foreach p: test_parts
+ if i > 0
+ parts += p
+ endif
+ i += 1
+ endforeach
+ test_name = '-'.join(parts)
+ endif
+ test(test_name, python,
+ args: [file],
env: test_env,
workdir: meson.current_source_dir(),
+ suite: ['functional'],
timeout: 60)
endforeach
diff --git a/tests/functional-tests/storetest.py b/tests/functional-tests/storetest.py
index ed7aa82c5..d96294cc4 100644
--- a/tests/functional-tests/storetest.py
+++ b/tests/functional-tests/storetest.py
@@ -19,6 +19,8 @@
#
import os
+import shutil
+import tempfile
import time
import unittest as ut
@@ -35,11 +37,28 @@ class CommonTrackerStoreTest (ut.TestCase):
@classmethod
def setUpClass(self):
- extra_env = {'LC_COLLATE': 'en_GB.utf8'}
+ self.tmpdir = tempfile.mkdtemp(prefix='tracker-test-')
- self.tracker = trackertestutils.helpers.StoreHelper(cfg.TRACKER_STORE_PATH)
- self.tracker.start(extra_env=extra_env)
+ try:
+ extra_env = cfg.test_environment(self.tmpdir)
+ extra_env['LANG'] = 'en_GB.utf8'
+ extra_env['LC_COLLATE'] = 'en_GB.utf8'
+
+ self.sandbox = trackertestutils.helpers.TrackerDBusSandbox(
+ dbus_daemon_config_file=cfg.TEST_DBUS_DAEMON_CONFIG_FILE, extra_env=extra_env)
+ self.sandbox.start()
+
+ self.tracker = trackertestutils.helpers.StoreHelper(
+ self.sandbox.get_connection())
+ self.tracker.start_and_wait_for_ready()
+ self.tracker.start_watching_updates()
+ except Exception as e:
+ shutil.rmtree(self.tmpdir, ignore_errors=True)
+ raise
@classmethod
def tearDownClass(self):
- self.tracker.stop()
+ self.tracker.stop_watching_updates()
+ self.sandbox.stop()
+
+ shutil.rmtree(self.tmpdir, ignore_errors=True)
diff --git a/tests/functional-tests/test-runner.sh.in b/tests/functional-tests/test-runner.sh.in
deleted file mode 100755
index 6e295aec6..000000000
--- a/tests/functional-tests/test-runner.sh.in
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Test runner script for Tracker's functional tests
-
-set -e
-
-export TEMP_DIR=`mktemp --tmpdir -d tracker-test-XXXX`
-
-# We need to use the actual home directory for some tests because
-# Tracker will explicitly ignore files in /tmp ...
-export REAL_HOME=`echo ~`
-
-# ... but /tmp is preferred for test data, to avoid leaving debris
-# in the filesystem
-HOME=$TEMP_DIR
-
-echo "Running $@"
-dbus-run-session --config-file=@abs_top_builddir@/tests/test-bus.conf -- "$@"
-
-rm -R $TEMP_DIR
diff --git a/tests/gvdb/meson.build b/tests/gvdb/meson.build
index 5d8fcf948..558f0f4e2 100644
--- a/tests/gvdb/meson.build
+++ b/tests/gvdb/meson.build
@@ -7,4 +7,5 @@ gvdb_test = executable('gvdb-test',
tests += {
'name': 'gvdb',
'exe': gvdb_test,
+ 'suite': ['gvdb']
}
diff --git a/tests/libtracker-common/meson.build b/tests/libtracker-common/meson.build
index 55b7ada50..f6618f930 100644
--- a/tests/libtracker-common/meson.build
+++ b/tests/libtracker-common/meson.build
@@ -14,15 +14,15 @@ libtracker_common_test_deps = [
foreach base_name: libtracker_common_tests
source = 'tracker-@0@-test.c'.format(base_name)
binary_name = 'tracker-@0@-test'.format(base_name)
- test_name = 'common-@0@'.format(base_name)
binary = executable(binary_name, source,
dependencies: libtracker_common_test_deps,
c_args: test_c_args)
tests += {
- 'name': test_name,
- 'exe': binary
+ 'name': base_name,
+ 'exe': binary,
+ 'suite': ['common']
}
endforeach
diff --git a/tests/libtracker-common/tracker-date-time-test.c b/tests/libtracker-common/tracker-date-time-test.c
index 7d4e78bb4..f51c36830 100644
--- a/tests/libtracker-common/tracker-date-time-test.c
+++ b/tests/libtracker-common/tracker-date-time-test.c
@@ -138,11 +138,22 @@ test_date_to_string (void)
input = timegm (original);
#endif
- result = tracker_date_to_string (input);
-
+ result = tracker_date_to_string (input, 0);
g_assert (result != NULL && strncmp (result, "2008-06-16T23:53:10Z", 19) == 0);
+ g_free (result);
+ result = tracker_date_to_string (input, 7200);
+ g_assert_cmpstr (result, ==, "2008-06-17T01:53:10+02:00");
g_free (result);
+
+ result = tracker_date_to_string (input, -7200);
+ g_assert_cmpstr (result, ==, "2008-06-16T21:53:10-02:00");
+ g_free (result);
+
+ result = tracker_date_to_string (input, -9000);
+ g_assert_cmpstr (result, ==, "2008-06-16T21:23:10-02:30");
+ g_free (result);
+
g_free (original);
}
@@ -245,6 +256,28 @@ test_date_time_get_local_time ()
g_assert_cmpint (tracker_date_time_get_local_time (&value), ==, 63780);
}
+static void
+test_date_time_conversions (void)
+{
+ GError *error = NULL;
+ time_t time;
+ int offset;
+ const gchar *date_str;
+ gchar *result;
+
+ date_str = "2011-10-28T17:43:00+03:00";
+
+ time = tracker_string_to_date (date_str, &offset, &error);
+ g_assert (!error);
+
+ g_assert_cmpint (time, ==, 1319812980);
+ g_assert_cmpint (offset, ==, 10800);
+
+ result = tracker_date_to_string (time, offset);
+ g_assert_cmpstr (result, ==, date_str);
+ g_free (result);
+}
+
gint
main (gint argc, gchar **argv)
{
@@ -266,6 +299,8 @@ main (gint argc, gchar **argv)
test_date_time_get_local_date);
g_test_add_func ("/libtracker-common/date-time/get_local_time",
test_date_time_get_local_time);
+ g_test_add_func ("/libtracker-common/date-time/conversions",
+ test_date_time_conversions);
return g_test_run ();
}
diff --git a/tests/libtracker-data/aggregates/aggregate-group-2.out b/tests/libtracker-data/aggregates/aggregate-group-2.out
new file mode 100644
index 000000000..3b41f134b
--- /dev/null
+++ b/tests/libtracker-data/aggregates/aggregate-group-2.out
@@ -0,0 +1,2 @@
+"http://example/x" "2" "8" "string t|string y"
+"http://example/z" "1" "13" "string u"
diff --git a/tests/libtracker-data/aggregates/aggregate-group-2.rq b/tests/libtracker-data/aggregates/aggregate-group-2.rq
new file mode 100644
index 000000000..ece4a0bd9
--- /dev/null
+++ b/tests/libtracker-data/aggregates/aggregate-group-2.rq
@@ -0,0 +1,9 @@
+PREFIX : <http://example/>
+
+SELECT ?z COUNT(DISTINCT ?b) SUM(?ib) GROUP_CONCAT(?sb, "|")
+{ ?a a :A ;
+ :b ?b .
+ ?b :ib ?ib .
+ ?b :sb ?sb .
+ FILTER (?ib>2) }
+GROUP BY (?a AS ?z)
diff --git a/tests/libtracker-data/aggregates/aggregate-group-as-1.out b/tests/libtracker-data/aggregates/aggregate-group-as-1.out
new file mode 100644
index 000000000..3b41f134b
--- /dev/null
+++ b/tests/libtracker-data/aggregates/aggregate-group-as-1.out
@@ -0,0 +1,2 @@
+"http://example/x" "2" "8" "string t|string y"
+"http://example/z" "1" "13" "string u"
diff --git a/tests/libtracker-data/aggregates/aggregate-group-as-1.rq b/tests/libtracker-data/aggregates/aggregate-group-as-1.rq
new file mode 100644
index 000000000..dacf6b30e
--- /dev/null
+++ b/tests/libtracker-data/aggregates/aggregate-group-as-1.rq
@@ -0,0 +1,9 @@
+PREFIX : <http://example/>
+
+SELECT ?g COUNT(DISTINCT ?b) SUM(?ib) GROUP_CONCAT(?sb, "|")
+{ ?a a :A ;
+ :b ?b .
+ ?b :ib ?ib .
+ ?b :sb ?sb .
+ FILTER (?ib>2) }
+GROUP BY (?a AS ?g)
diff --git a/tests/libtracker-data/basic/base-1.out b/tests/libtracker-data/basic/base-1.out
new file mode 100644
index 000000000..192548e94
--- /dev/null
+++ b/tests/libtracker-data/basic/base-1.out
@@ -0,0 +1 @@
+"42"
diff --git a/tests/libtracker-data/basic/base-1.rq b/tests/libtracker-data/basic/base-1.rq
new file mode 100644
index 000000000..4c51942e6
--- /dev/null
+++ b/tests/libtracker-data/basic/base-1.rq
@@ -0,0 +1,3 @@
+BASE <http://example.org/x/>
+
+SELECT * WHERE { <x> <p> ?v }
diff --git a/tests/libtracker-data/construct/construct-pattern.out b/tests/libtracker-data/construct/construct-pattern.out
new file mode 100644
index 000000000..bda80d4c6
--- /dev/null
+++ b/tests/libtracker-data/construct/construct-pattern.out
@@ -0,0 +1,12 @@
+"a" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://xmlns.com/foaf/0.1/Person"
+"b" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://xmlns.com/foaf/0.1/Person"
+"c" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://xmlns.com/foaf/0.1/Person"
+"d" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://xmlns.com/foaf/0.1/Person"
+"a" "http://xmlns.com/foaf/0.1/name" "nameA"
+"b" "http://xmlns.com/foaf/0.1/name" "nameB"
+"c" "http://xmlns.com/foaf/0.1/name" "nameC"
+"d" "http://xmlns.com/foaf/0.1/name" "nameD"
+"a" "http://xmlns.com/foaf/0.1/mbox" "mailto:bob@work"
+"b" "http://xmlns.com/foaf/0.1/mbox" "mailto:bob@work"
+"c" "http://xmlns.com/foaf/0.1/mbox" "mailto:bob@work"
+"d" "http://xmlns.com/foaf/0.1/mbox" "mailto:bob@work"
diff --git a/tests/libtracker-data/construct/construct-pattern.rq b/tests/libtracker-data/construct/construct-pattern.rq
new file mode 100644
index 000000000..d686a48fd
--- /dev/null
+++ b/tests/libtracker-data/construct/construct-pattern.rq
@@ -0,0 +1,3 @@
+PREFIX foaf: <http://xmlns.com/foaf/0.1/>
+
+CONSTRUCT { ?u a foaf:Person ; foaf:name ?name ; foaf:mbox <mailto:bob@work> } WHERE { ?u a example:A ; example:name ?name }
diff --git a/tests/libtracker-data/construct/construct-where.out b/tests/libtracker-data/construct/construct-where.out
new file mode 100644
index 000000000..6fb4dde44
--- /dev/null
+++ b/tests/libtracker-data/construct/construct-where.out
@@ -0,0 +1,8 @@
+"a" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://example/A"
+"b" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://example/A"
+"c" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://example/A"
+"d" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://example/A"
+"a" "http://example/name" "nameA"
+"b" "http://example/name" "nameB"
+"c" "http://example/name" "nameC"
+"d" "http://example/name" "nameD"
diff --git a/tests/libtracker-data/construct/construct-where.rq b/tests/libtracker-data/construct/construct-where.rq
new file mode 100644
index 000000000..cce9b98e7
--- /dev/null
+++ b/tests/libtracker-data/construct/construct-where.rq
@@ -0,0 +1 @@
+CONSTRUCT WHERE { ?u a example:A ; example:name ?l }
diff --git a/tests/libtracker-data/construct/construct-with-modifiers.out b/tests/libtracker-data/construct/construct-with-modifiers.out
new file mode 100644
index 000000000..737959de6
--- /dev/null
+++ b/tests/libtracker-data/construct/construct-with-modifiers.out
@@ -0,0 +1,4 @@
+"c" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://xmlns.com/foaf/0.1/Person"
+"b" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://xmlns.com/foaf/0.1/Person"
+"c" "http://xmlns.com/foaf/0.1/name" "nameC"
+"b" "http://xmlns.com/foaf/0.1/name" "nameB"
diff --git a/tests/libtracker-data/construct/construct-with-modifiers.rq b/tests/libtracker-data/construct/construct-with-modifiers.rq
new file mode 100644
index 000000000..2f2badefa
--- /dev/null
+++ b/tests/libtracker-data/construct/construct-with-modifiers.rq
@@ -0,0 +1,3 @@
+PREFIX foaf: <http://xmlns.com/foaf/0.1/>
+
+CONSTRUCT { ?u a foaf:Person ; foaf:name ?name } WHERE { ?u a example:A ; example:name ?name ; example:number ?number } ORDER BY DESC ?number LIMIT 2
diff --git a/tests/libtracker-data/construct/data.ttl b/tests/libtracker-data/construct/data.ttl
new file mode 100644
index 000000000..7449849fd
--- /dev/null
+++ b/tests/libtracker-data/construct/data.ttl
@@ -0,0 +1,31 @@
+@prefix example: <http://example/> .
+
+<a> a example:A ;
+ example:name 'nameA' ;
+ example:date '2000-01-01T00:00:01Z' ;
+ example:number 42 .
+
+<b> a example:A ;
+ example:name 'nameB' ;
+ example:date '2001-01-01T00:00:01Z' ;
+ example:number 73 ;
+ example:relation <z> .
+
+<c> a example:A ;
+ example:name 'nameC' ;
+ example:number 113 ;
+ example:relation <x> .
+
+<d> a example:A ;
+ example:name 'nameD' ;
+ example:date '2002-01-01T00:00:01Z' ;
+ example:relation <z> .
+
+<x> a example:B ;
+ example:title 'titleX' .
+
+<y> a example:B ;
+ example:title 'titleY' .
+
+<z> a example:B ;
+ example:title 'titleZ' .
diff --git a/tests/libtracker-data/construct/test.ontology b/tests/libtracker-data/construct/test.ontology
new file mode 100644
index 000000000..0a803ae95
--- /dev/null
+++ b/tests/libtracker-data/construct/test.ontology
@@ -0,0 +1,34 @@
+@prefix example: <http://example/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix tracker: <http://www.tracker-project.org/ontologies/tracker#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+example: a tracker:Namespace ;
+ tracker:prefix "example" .
+
+example:A a rdfs:Class ;
+ rdfs:subClassOf rdfs:Resource .
+
+example:name a rdf:Property ;
+ rdfs:domain example:A ;
+ rdfs:range xsd:string .
+
+example:date a rdf:Property ;
+ rdfs:domain example:A ;
+ rdfs:range xsd:dateTime .
+
+example:number a rdf:Property ;
+ rdfs:domain example:A ;
+ rdfs:range xsd:integer .
+
+example:B a rdfs:Class ;
+ rdfs:subClassOf rdfs:Resource .
+
+example:title a rdf:Property ;
+ rdfs:domain example:B ;
+ rdfs:range xsd:string .
+
+example:relation a rdf:Property ;
+ rdfs:domain example:A ;
+ rdfs:range example:B .
diff --git a/tests/libtracker-data/datetime/functions-timezone-3.out b/tests/libtracker-data/datetime/functions-timezone-3.out
new file mode 100644
index 000000000..95239e27b
--- /dev/null
+++ b/tests/libtracker-data/datetime/functions-timezone-3.out
@@ -0,0 +1 @@
+"+PT2H"
diff --git a/tests/libtracker-data/datetime/functions-timezone-3.rq b/tests/libtracker-data/datetime/functions-timezone-3.rq
new file mode 100644
index 000000000..22b005995
--- /dev/null
+++ b/tests/libtracker-data/datetime/functions-timezone-3.rq
@@ -0,0 +1,6 @@
+SELECT TIMEZONE (?v) ?comment
+WHERE {
+ example:y a example:A ;
+ example:p ?v
+ OPTIONAL { example:y rdfs:comment ?comment }
+}
diff --git a/tests/libtracker-data/datetime/functions-tz-1.out b/tests/libtracker-data/datetime/functions-tz-1.out
new file mode 100644
index 000000000..4f0c9782a
--- /dev/null
+++ b/tests/libtracker-data/datetime/functions-tz-1.out
@@ -0,0 +1 @@
+"+02:00"
diff --git a/tests/libtracker-data/datetime/functions-tz-1.rq b/tests/libtracker-data/datetime/functions-tz-1.rq
new file mode 100644
index 000000000..a677fd9c2
--- /dev/null
+++ b/tests/libtracker-data/datetime/functions-tz-1.rq
@@ -0,0 +1,6 @@
+SELECT TZ (?v) ?comment
+WHERE {
+ example:y a example:A ;
+ example:p ?v
+ OPTIONAL { example:y rdfs:comment ?comment }
+}
diff --git a/tests/libtracker-data/describe/data.ttl b/tests/libtracker-data/describe/data.ttl
new file mode 100644
index 000000000..7449849fd
--- /dev/null
+++ b/tests/libtracker-data/describe/data.ttl
@@ -0,0 +1,31 @@
+@prefix example: <http://example/> .
+
+<a> a example:A ;
+ example:name 'nameA' ;
+ example:date '2000-01-01T00:00:01Z' ;
+ example:number 42 .
+
+<b> a example:A ;
+ example:name 'nameB' ;
+ example:date '2001-01-01T00:00:01Z' ;
+ example:number 73 ;
+ example:relation <z> .
+
+<c> a example:A ;
+ example:name 'nameC' ;
+ example:number 113 ;
+ example:relation <x> .
+
+<d> a example:A ;
+ example:name 'nameD' ;
+ example:date '2002-01-01T00:00:01Z' ;
+ example:relation <z> .
+
+<x> a example:B ;
+ example:title 'titleX' .
+
+<y> a example:B ;
+ example:title 'titleY' .
+
+<z> a example:B ;
+ example:title 'titleZ' .
diff --git a/tests/libtracker-data/describe/describe-limit.out b/tests/libtracker-data/describe/describe-limit.out
new file mode 100644
index 000000000..e983c2b48
--- /dev/null
+++ b/tests/libtracker-data/describe/describe-limit.out
@@ -0,0 +1,11 @@
+"a" "http://example/number" "42"
+"a" "http://example/date" "2000-01-01T00:00:01Z"
+"a" "http://example/name" "nameA"
+"a" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://www.w3.org/2000/01/rdf-schema#Resource"
+"a" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://example/A"
+"b" "http://example/relation" "z"
+"b" "http://example/number" "73"
+"b" "http://example/date" "2001-01-01T00:00:01Z"
+"b" "http://example/name" "nameB"
+"b" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://www.w3.org/2000/01/rdf-schema#Resource"
+"b" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://example/A"
diff --git a/tests/libtracker-data/describe/describe-limit.rq b/tests/libtracker-data/describe/describe-limit.rq
new file mode 100644
index 000000000..f09f243e1
--- /dev/null
+++ b/tests/libtracker-data/describe/describe-limit.rq
@@ -0,0 +1 @@
+describe ?u { ?u a example:A } limit 2 order by ?u
diff --git a/tests/libtracker-data/describe/describe-multiple.out b/tests/libtracker-data/describe/describe-multiple.out
new file mode 100644
index 000000000..034058637
--- /dev/null
+++ b/tests/libtracker-data/describe/describe-multiple.out
@@ -0,0 +1,22 @@
+"b" "http://example/relation" "z"
+"b" "http://example/number" "73"
+"b" "http://example/date" "2001-01-01T00:00:01Z"
+"b" "http://example/name" "nameB"
+"b" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://www.w3.org/2000/01/rdf-schema#Resource"
+"b" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://example/A"
+"z" "http://example/title" "titleZ"
+"z" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://www.w3.org/2000/01/rdf-schema#Resource"
+"z" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://example/B"
+"c" "http://example/relation" "x"
+"c" "http://example/number" "113"
+"c" "http://example/name" "nameC"
+"c" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://www.w3.org/2000/01/rdf-schema#Resource"
+"c" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://example/A"
+"x" "http://example/title" "titleX"
+"x" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://www.w3.org/2000/01/rdf-schema#Resource"
+"x" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://example/B"
+"d" "http://example/relation" "z"
+"d" "http://example/date" "2002-01-01T00:00:01Z"
+"d" "http://example/name" "nameD"
+"d" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://www.w3.org/2000/01/rdf-schema#Resource"
+"d" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://example/A"
diff --git a/tests/libtracker-data/describe/describe-multiple.rq b/tests/libtracker-data/describe/describe-multiple.rq
new file mode 100644
index 000000000..3e67f4e16
--- /dev/null
+++ b/tests/libtracker-data/describe/describe-multiple.rq
@@ -0,0 +1 @@
+DESCRIBE ?u ?r { ?u example:relation ?r }
diff --git a/tests/libtracker-data/describe/describe-non-existent.out b/tests/libtracker-data/describe/describe-non-existent.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/libtracker-data/describe/describe-non-existent.out
diff --git a/tests/libtracker-data/describe/describe-non-existent.rq b/tests/libtracker-data/describe/describe-non-existent.rq
new file mode 100644
index 000000000..e80fd6229
--- /dev/null
+++ b/tests/libtracker-data/describe/describe-non-existent.rq
@@ -0,0 +1 @@
+DESCRIBE <non-existent>
diff --git a/tests/libtracker-data/describe/describe-pattern.out b/tests/libtracker-data/describe/describe-pattern.out
new file mode 100644
index 000000000..e512b1d8c
--- /dev/null
+++ b/tests/libtracker-data/describe/describe-pattern.out
@@ -0,0 +1,6 @@
+"b" "http://example/relation" "z"
+"b" "http://example/number" "73"
+"b" "http://example/date" "2001-01-01T00:00:01Z"
+"b" "http://example/name" "nameB"
+"b" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://www.w3.org/2000/01/rdf-schema#Resource"
+"b" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://example/A"
diff --git a/tests/libtracker-data/describe/describe-pattern.rq b/tests/libtracker-data/describe/describe-pattern.rq
new file mode 100644
index 000000000..061b49c7a
--- /dev/null
+++ b/tests/libtracker-data/describe/describe-pattern.rq
@@ -0,0 +1 @@
+DESCRIBE ?u { ?u example:name 'nameB' }
diff --git a/tests/libtracker-data/describe/describe-single.out b/tests/libtracker-data/describe/describe-single.out
new file mode 100644
index 000000000..0bb548ba0
--- /dev/null
+++ b/tests/libtracker-data/describe/describe-single.out
@@ -0,0 +1,5 @@
+"a" "http://example/number" "42"
+"a" "http://example/date" "2000-01-01T00:00:01Z"
+"a" "http://example/name" "nameA"
+"a" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://www.w3.org/2000/01/rdf-schema#Resource"
+"a" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "http://example/A"
diff --git a/tests/libtracker-data/describe/describe-single.rq b/tests/libtracker-data/describe/describe-single.rq
new file mode 100644
index 000000000..bc781f9a6
--- /dev/null
+++ b/tests/libtracker-data/describe/describe-single.rq
@@ -0,0 +1 @@
+DESCRIBE <a>
diff --git a/tests/libtracker-data/describe/test.ontology b/tests/libtracker-data/describe/test.ontology
new file mode 100644
index 000000000..0a803ae95
--- /dev/null
+++ b/tests/libtracker-data/describe/test.ontology
@@ -0,0 +1,34 @@
+@prefix example: <http://example/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix tracker: <http://www.tracker-project.org/ontologies/tracker#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+example: a tracker:Namespace ;
+ tracker:prefix "example" .
+
+example:A a rdfs:Class ;
+ rdfs:subClassOf rdfs:Resource .
+
+example:name a rdf:Property ;
+ rdfs:domain example:A ;
+ rdfs:range xsd:string .
+
+example:date a rdf:Property ;
+ rdfs:domain example:A ;
+ rdfs:range xsd:dateTime .
+
+example:number a rdf:Property ;
+ rdfs:domain example:A ;
+ rdfs:range xsd:integer .
+
+example:B a rdfs:Class ;
+ rdfs:subClassOf rdfs:Resource .
+
+example:title a rdf:Property ;
+ rdfs:domain example:B ;
+ rdfs:range xsd:string .
+
+example:relation a rdf:Property ;
+ rdfs:domain example:A ;
+ rdfs:range example:B .
diff --git a/tests/libtracker-data/expr-ops/query-ge-1.rq b/tests/libtracker-data/expr-ops/query-ge-1.rq
index 5eadb36ee..10f540664 100755
--- a/tests/libtracker-data/expr-ops/query-ge-1.rq
+++ b/tests/libtracker-data/expr-ops/query-ge-1.rq
@@ -3,3 +3,4 @@ SELECT ?s WHERE {
?s :p ?o .
FILTER(?o >= 3) .
}
+ORDER BY ?s \ No newline at end of file
diff --git a/tests/libtracker-data/functions/functions-datatypes-1.out b/tests/libtracker-data/functions/functions-datatypes-1.out
new file mode 100644
index 000000000..b10f3540c
--- /dev/null
+++ b/tests/libtracker-data/functions/functions-datatypes-1.out
@@ -0,0 +1,27 @@
+"http://example/e" "http://example/s" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/e" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/e" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/q" "http://example/s" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/q" "http://example/t" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/q" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/q" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/r" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/r" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/w" "http://example/s" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/w" "http://example/t" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/w" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/w" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/x" "http://example/s" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/x" "http://example/t" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/x" "http://example/u" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/x" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/x" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/y" "http://example/s" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/y" "http://example/t" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/y" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/y" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/z" "http://example/s" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/z" "http://example/t" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/z" "http://example/u" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/z" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/z" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
diff --git a/tests/libtracker-data/functions/functions-datatypes-1.rq b/tests/libtracker-data/functions/functions-datatypes-1.rq
new file mode 100644
index 000000000..5cce80f4f
--- /dev/null
+++ b/tests/libtracker-data/functions/functions-datatypes-1.rq
@@ -0,0 +1,3 @@
+PREFIX : <http://example/>
+
+SELECT ?s ?p ISLITERAL(?o) ISURI(?o) ISIRI(?o) ISNUMERIC(?o) ISBLANK(?o) DATATYPE(?o) { ?s a :A ; ?p ?o } ORDER BY ?s ?p
diff --git a/tests/libtracker-data/functions/functions-datatypes-2.out b/tests/libtracker-data/functions/functions-datatypes-2.out
new file mode 100644
index 000000000..8075bd9e2
--- /dev/null
+++ b/tests/libtracker-data/functions/functions-datatypes-2.out
@@ -0,0 +1,10 @@
+"http://example/x" "http://example/filename" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/x" "http://example/title" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/x" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/x" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/y" "http://example/filename" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/y" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/y" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/z" "http://example/filename" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/z" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/z" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
diff --git a/tests/libtracker-data/functions/functions-datatypes-2.rq b/tests/libtracker-data/functions/functions-datatypes-2.rq
new file mode 100644
index 000000000..5cce80f4f
--- /dev/null
+++ b/tests/libtracker-data/functions/functions-datatypes-2.rq
@@ -0,0 +1,3 @@
+PREFIX : <http://example/>
+
+SELECT ?s ?p ISLITERAL(?o) ISURI(?o) ISIRI(?o) ISNUMERIC(?o) ISBLANK(?o) DATATYPE(?o) { ?s a :A ; ?p ?o } ORDER BY ?s ?p
diff --git a/tests/libtracker-data/functions/functions-datatypes-3.out b/tests/libtracker-data/functions/functions-datatypes-3.out
new file mode 100644
index 000000000..11d758678
--- /dev/null
+++ b/tests/libtracker-data/functions/functions-datatypes-3.out
@@ -0,0 +1,20 @@
+"http://example/c" "http://example/latitude" "true" "false" "false" "true" "http://www.w3.org/2001/XMLSchema#double"
+"http://example/c" "http://example/longitude" "true" "false" "false" "true" "http://www.w3.org/2001/XMLSchema#double"
+"http://example/c" "http://example/name" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/c" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/c" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/x" "http://example/latitude" "true" "false" "false" "true" "http://www.w3.org/2001/XMLSchema#double"
+"http://example/x" "http://example/longitude" "true" "false" "false" "true" "http://www.w3.org/2001/XMLSchema#double"
+"http://example/x" "http://example/name" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/x" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/x" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/y" "http://example/latitude" "true" "false" "false" "true" "http://www.w3.org/2001/XMLSchema#double"
+"http://example/y" "http://example/longitude" "true" "false" "false" "true" "http://www.w3.org/2001/XMLSchema#double"
+"http://example/y" "http://example/name" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/y" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/y" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/z" "http://example/latitude" "true" "false" "false" "true" "http://www.w3.org/2001/XMLSchema#double"
+"http://example/z" "http://example/longitude" "true" "false" "false" "true" "http://www.w3.org/2001/XMLSchema#double"
+"http://example/z" "http://example/name" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/z" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/z" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
diff --git a/tests/libtracker-data/functions/functions-datatypes-3.rq b/tests/libtracker-data/functions/functions-datatypes-3.rq
new file mode 100644
index 000000000..5706bab94
--- /dev/null
+++ b/tests/libtracker-data/functions/functions-datatypes-3.rq
@@ -0,0 +1,3 @@
+PREFIX : <http://example/>
+
+SELECT ?s ?p ISLITERAL(?o) ISURI(?o) ISIRI(?o) ISNUMERIC(?o) ISBLANK(?o) DATATYPE(?o) { ?s a :Location ; ?p ?o } ORDER BY ?s ?p
diff --git a/tests/libtracker-data/functions/functions-datatypes-4.out b/tests/libtracker-data/functions/functions-datatypes-4.out
new file mode 100644
index 000000000..761d9bb4b
--- /dev/null
+++ b/tests/libtracker-data/functions/functions-datatypes-4.out
@@ -0,0 +1,52 @@
+"http://example/p1" "http://example/familyName" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p1" "http://example/givenName" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p1" "http://example/name" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p1" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/p1" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/p10" "http://example/description" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p10" "http://example/familyName" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p10" "http://example/givenName" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p10" "http://example/name" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p10" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/p10" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/p2" "http://example/familyName" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p2" "http://example/givenName" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p2" "http://example/name" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p2" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/p2" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/p3" "http://example/familyName" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p3" "http://example/givenName" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p3" "http://example/name" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p3" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/p3" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/p4" "http://example/familyName" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p4" "http://example/givenName" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p4" "http://example/name" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p4" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/p4" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/p5" "http://example/familyName" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p5" "http://example/givenName" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p5" "http://example/name" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p5" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/p5" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/p6" "http://example/familyName" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p6" "http://example/givenName" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p6" "http://example/name" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p6" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/p6" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/p7" "http://example/familyName" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p7" "http://example/givenName" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p7" "http://example/name" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p7" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/p7" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/p8" "http://example/familyName" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p8" "http://example/givenName" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p8" "http://example/name" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p8" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/p8" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/p9" "http://example/description" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p9" "http://example/familyName" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p9" "http://example/givenName" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p9" "http://example/name" "true" "false" "false" "false" "http://www.w3.org/2001/XMLSchema#string"
+"http://example/p9" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
+"http://example/p9" "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" "false" "true" "true" "false"
diff --git a/tests/libtracker-data/functions/functions-datatypes-4.rq b/tests/libtracker-data/functions/functions-datatypes-4.rq
new file mode 100644
index 000000000..064bf1e52
--- /dev/null
+++ b/tests/libtracker-data/functions/functions-datatypes-4.rq
@@ -0,0 +1,3 @@
+PREFIX : <http://example/>
+
+SELECT ?s ?p ISLITERAL(?o) ISURI(?o) ISIRI(?o) ISNUMERIC(?o) ISBLANK(?o) DATATYPE(?o) { ?s a :Person ; ?p ?o } ORDER BY ?s ?p
diff --git a/tests/libtracker-data/functions/functions-property-1.rq b/tests/libtracker-data/functions/functions-property-1.rq
index 153f9fdb5..f51da92af 100644
--- a/tests/libtracker-data/functions/functions-property-1.rq
+++ b/tests/libtracker-data/functions/functions-property-1.rq
@@ -5,4 +5,4 @@ SELECT ex:o(?b) ex:s(?a)
{ ?b a ex:B ;
ex:a ?a
}
-
+ORDER BY ex:o(?b) \ No newline at end of file
diff --git a/tests/libtracker-data/functions/functions-tracker-loc-1.rq b/tests/libtracker-data/functions/functions-tracker-loc-1.rq
index 3145bae1e..ac3b4d340 100644
--- a/tests/libtracker-data/functions/functions-tracker-loc-1.rq
+++ b/tests/libtracker-data/functions/functions-tracker-loc-1.rq
@@ -1,11 +1,14 @@
PREFIX ex: <http://example/>
PREFIX ns: <http://www.w3.org/2005/xpath-functions#>
-SELECT ?location xsd:integer(tracker:cartesian-distance(?lat1,?lat2,?lon1,?lon2)) xsd:integer(tracker:haversine-distance(?lat1,?lat2,?lon1,?lon2))
+SELECT ?location ?cartesian ?harvesine
{ ?_x a ex:Location ;
ex:name ?location ;
ex:latitude ?lat1 ;
ex:longitude ?lon1 .
ex:x ex:latitude ?lat2 ;
ex:longitude ?lon2 .
+ BIND (xsd:integer(tracker:cartesian-distance(?lat1,?lat2,?lon1,?lon2)) AS ?cartesian) .
+ BIND (xsd:integer(tracker:haversine-distance(?lat1,?lat2,?lon1,?lon2)) AS ?harvesine) .
}
+ORDER BY ?cartesian ?harvesine \ No newline at end of file
diff --git a/tests/libtracker-data/graph/add-from-default.out b/tests/libtracker-data/graph/add-from-default.out
new file mode 100644
index 000000000..16c7607fb
--- /dev/null
+++ b/tests/libtracker-data/graph/add-from-default.out
@@ -0,0 +1,2 @@
+"http://example/resource1"
+"http://example/resource2"
diff --git a/tests/libtracker-data/graph/add-from-default.rq b/tests/libtracker-data/graph/add-from-default.rq
new file mode 100644
index 000000000..1f600846a
--- /dev/null
+++ b/tests/libtracker-data/graph/add-from-default.rq
@@ -0,0 +1,6 @@
+SELECT ?s WHERE {
+ GRAPH example:graphA {
+ ?s example:p 42
+ }
+}
+ORDER BY ?s
diff --git a/tests/libtracker-data/graph/add-from-non-existent.out b/tests/libtracker-data/graph/add-from-non-existent.out
new file mode 100644
index 000000000..0476f7477
--- /dev/null
+++ b/tests/libtracker-data/graph/add-from-non-existent.out
@@ -0,0 +1 @@
+"http://example/graphA" "http://example/resource"
diff --git a/tests/libtracker-data/graph/add-from-non-existent.rq b/tests/libtracker-data/graph/add-from-non-existent.rq
new file mode 100644
index 000000000..b2464d25a
--- /dev/null
+++ b/tests/libtracker-data/graph/add-from-non-existent.rq
@@ -0,0 +1,5 @@
+SELECT ?g ?s WHERE {
+ GRAPH ?g {
+ ?s example:p 42
+ }
+}
diff --git a/tests/libtracker-data/graph/add-into-self.out b/tests/libtracker-data/graph/add-into-self.out
new file mode 100644
index 000000000..0476f7477
--- /dev/null
+++ b/tests/libtracker-data/graph/add-into-self.out
@@ -0,0 +1 @@
+"http://example/graphA" "http://example/resource"
diff --git a/tests/libtracker-data/graph/add-into-self.rq b/tests/libtracker-data/graph/add-into-self.rq
new file mode 100644
index 000000000..b2464d25a
--- /dev/null
+++ b/tests/libtracker-data/graph/add-into-self.rq
@@ -0,0 +1,5 @@
+SELECT ?g ?s WHERE {
+ GRAPH ?g {
+ ?s example:p 42
+ }
+}
diff --git a/tests/libtracker-data/graph/add-to-default.out b/tests/libtracker-data/graph/add-to-default.out
new file mode 100644
index 000000000..16c7607fb
--- /dev/null
+++ b/tests/libtracker-data/graph/add-to-default.out
@@ -0,0 +1,2 @@
+"http://example/resource1"
+"http://example/resource2"
diff --git a/tests/libtracker-data/graph/add-to-default.rq b/tests/libtracker-data/graph/add-to-default.rq
new file mode 100644
index 000000000..fcf167ee2
--- /dev/null
+++ b/tests/libtracker-data/graph/add-to-default.rq
@@ -0,0 +1,3 @@
+SELECT DISTINCT ?s WHERE {
+ ?s example:p 42
+}
diff --git a/tests/libtracker-data/graph/add-to-existent.out b/tests/libtracker-data/graph/add-to-existent.out
new file mode 100644
index 000000000..f7327c58b
--- /dev/null
+++ b/tests/libtracker-data/graph/add-to-existent.out
@@ -0,0 +1,4 @@
+"http://example/graphA" "http://example/resource1" "42"
+"http://example/graphB" "http://example/resource1" "42"
+"http://example/graphB" "http://example/resource1" "73"
+"http://example/graphB" "http://example/resource2" "113"
diff --git a/tests/libtracker-data/graph/add-to-existent.rq b/tests/libtracker-data/graph/add-to-existent.rq
new file mode 100644
index 000000000..cecd2ff71
--- /dev/null
+++ b/tests/libtracker-data/graph/add-to-existent.rq
@@ -0,0 +1,6 @@
+SELECT ?g ?s ?v WHERE {
+ GRAPH ?g {
+ ?s example:p ?v
+ }
+}
+ORDER BY ?g ?s ?v
diff --git a/tests/libtracker-data/graph/add-to-non-existent.out b/tests/libtracker-data/graph/add-to-non-existent.out
new file mode 100644
index 000000000..d9e765277
--- /dev/null
+++ b/tests/libtracker-data/graph/add-to-non-existent.out
@@ -0,0 +1,2 @@
+"http://example/graphA" "http://example/resource"
+"http://example/graphB" "http://example/resource"
diff --git a/tests/libtracker-data/graph/add-to-non-existent.rq b/tests/libtracker-data/graph/add-to-non-existent.rq
new file mode 100644
index 000000000..b2464d25a
--- /dev/null
+++ b/tests/libtracker-data/graph/add-to-non-existent.rq
@@ -0,0 +1,5 @@
+SELECT ?g ?s WHERE {
+ GRAPH ?g {
+ ?s example:p 42
+ }
+}
diff --git a/tests/libtracker-data/graph/add.out b/tests/libtracker-data/graph/add.out
new file mode 100644
index 000000000..99fcf8545
--- /dev/null
+++ b/tests/libtracker-data/graph/add.out
@@ -0,0 +1,3 @@
+"http://example/graphA" "http://example/resource1"
+"http://example/graphB" "http://example/resource1"
+"http://example/graphB" "http://example/resource2"
diff --git a/tests/libtracker-data/graph/add.rq b/tests/libtracker-data/graph/add.rq
new file mode 100644
index 000000000..451dc9a1a
--- /dev/null
+++ b/tests/libtracker-data/graph/add.rq
@@ -0,0 +1,6 @@
+SELECT ?g ?s WHERE {
+ GRAPH ?g {
+ ?s example:p 42
+ }
+}
+ORDER BY ?g ?s
diff --git a/tests/libtracker-data/graph/clear-all.out b/tests/libtracker-data/graph/clear-all.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/libtracker-data/graph/clear-all.out
diff --git a/tests/libtracker-data/graph/clear-all.rq b/tests/libtracker-data/graph/clear-all.rq
new file mode 100644
index 000000000..4507ec3c0
--- /dev/null
+++ b/tests/libtracker-data/graph/clear-all.rq
@@ -0,0 +1,4 @@
+SELECT ?s WHERE {
+ ?s a example:A
+}
+ORDER BY ?s
diff --git a/tests/libtracker-data/graph/clear-default.out b/tests/libtracker-data/graph/clear-default.out
new file mode 100644
index 000000000..07170b538
--- /dev/null
+++ b/tests/libtracker-data/graph/clear-default.out
@@ -0,0 +1,2 @@
+"http://example/resource2"
+"http://example/resource3"
diff --git a/tests/libtracker-data/graph/clear-default.rq b/tests/libtracker-data/graph/clear-default.rq
new file mode 100644
index 000000000..4507ec3c0
--- /dev/null
+++ b/tests/libtracker-data/graph/clear-default.rq
@@ -0,0 +1,4 @@
+SELECT ?s WHERE {
+ ?s a example:A
+}
+ORDER BY ?s
diff --git a/tests/libtracker-data/graph/clear-named.out b/tests/libtracker-data/graph/clear-named.out
new file mode 100644
index 000000000..d24fcef0c
--- /dev/null
+++ b/tests/libtracker-data/graph/clear-named.out
@@ -0,0 +1 @@
+"http://example/resource1"
diff --git a/tests/libtracker-data/graph/clear-named.rq b/tests/libtracker-data/graph/clear-named.rq
new file mode 100644
index 000000000..4507ec3c0
--- /dev/null
+++ b/tests/libtracker-data/graph/clear-named.rq
@@ -0,0 +1,4 @@
+SELECT ?s WHERE {
+ ?s a example:A
+}
+ORDER BY ?s
diff --git a/tests/libtracker-data/graph/clear-non-existent.out b/tests/libtracker-data/graph/clear-non-existent.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/libtracker-data/graph/clear-non-existent.out
diff --git a/tests/libtracker-data/graph/clear-non-existent.rq b/tests/libtracker-data/graph/clear-non-existent.rq
new file mode 100644
index 000000000..51cb7ce31
--- /dev/null
+++ b/tests/libtracker-data/graph/clear-non-existent.rq
@@ -0,0 +1,5 @@
+SELECT ?g ?s WHERE {
+ GRAPH ?g {
+ ?s a example:A
+ }
+}
diff --git a/tests/libtracker-data/graph/clear.out b/tests/libtracker-data/graph/clear.out
new file mode 100644
index 000000000..cf0a28d0b
--- /dev/null
+++ b/tests/libtracker-data/graph/clear.out
@@ -0,0 +1 @@
+"http://example/graphB" "http://example/resource"
diff --git a/tests/libtracker-data/graph/clear.rq b/tests/libtracker-data/graph/clear.rq
new file mode 100644
index 000000000..b2464d25a
--- /dev/null
+++ b/tests/libtracker-data/graph/clear.rq
@@ -0,0 +1,5 @@
+SELECT ?g ?s WHERE {
+ GRAPH ?g {
+ ?s example:p 42
+ }
+}
diff --git a/tests/libtracker-data/graph/copy-from-default.out b/tests/libtracker-data/graph/copy-from-default.out
new file mode 100644
index 000000000..d24fcef0c
--- /dev/null
+++ b/tests/libtracker-data/graph/copy-from-default.out
@@ -0,0 +1 @@
+"http://example/resource1"
diff --git a/tests/libtracker-data/graph/copy-from-default.rq b/tests/libtracker-data/graph/copy-from-default.rq
new file mode 100644
index 000000000..22da1a1c8
--- /dev/null
+++ b/tests/libtracker-data/graph/copy-from-default.rq
@@ -0,0 +1,5 @@
+SELECT ?s WHERE {
+ GRAPH example:graphA {
+ ?s example:p 42
+ }
+}
diff --git a/tests/libtracker-data/graph/copy-from-non-existent.out b/tests/libtracker-data/graph/copy-from-non-existent.out
new file mode 100644
index 000000000..0476f7477
--- /dev/null
+++ b/tests/libtracker-data/graph/copy-from-non-existent.out
@@ -0,0 +1 @@
+"http://example/graphA" "http://example/resource"
diff --git a/tests/libtracker-data/graph/copy-from-non-existent.rq b/tests/libtracker-data/graph/copy-from-non-existent.rq
new file mode 100644
index 000000000..b2464d25a
--- /dev/null
+++ b/tests/libtracker-data/graph/copy-from-non-existent.rq
@@ -0,0 +1,5 @@
+SELECT ?g ?s WHERE {
+ GRAPH ?g {
+ ?s example:p 42
+ }
+}
diff --git a/tests/libtracker-data/graph/copy-into-self.out b/tests/libtracker-data/graph/copy-into-self.out
new file mode 100644
index 000000000..0476f7477
--- /dev/null
+++ b/tests/libtracker-data/graph/copy-into-self.out
@@ -0,0 +1 @@
+"http://example/graphA" "http://example/resource"
diff --git a/tests/libtracker-data/graph/copy-into-self.rq b/tests/libtracker-data/graph/copy-into-self.rq
new file mode 100644
index 000000000..b2464d25a
--- /dev/null
+++ b/tests/libtracker-data/graph/copy-into-self.rq
@@ -0,0 +1,5 @@
+SELECT ?g ?s WHERE {
+ GRAPH ?g {
+ ?s example:p 42
+ }
+}
diff --git a/tests/libtracker-data/graph/copy-to-default.out b/tests/libtracker-data/graph/copy-to-default.out
new file mode 100644
index 000000000..e6ab2fcf6
--- /dev/null
+++ b/tests/libtracker-data/graph/copy-to-default.out
@@ -0,0 +1 @@
+"http://example/resource2"
diff --git a/tests/libtracker-data/graph/copy-to-default.rq b/tests/libtracker-data/graph/copy-to-default.rq
new file mode 100644
index 000000000..fcf167ee2
--- /dev/null
+++ b/tests/libtracker-data/graph/copy-to-default.rq
@@ -0,0 +1,3 @@
+SELECT DISTINCT ?s WHERE {
+ ?s example:p 42
+}
diff --git a/tests/libtracker-data/graph/copy-to-existent.out b/tests/libtracker-data/graph/copy-to-existent.out
new file mode 100644
index 000000000..73dde1d73
--- /dev/null
+++ b/tests/libtracker-data/graph/copy-to-existent.out
@@ -0,0 +1,2 @@
+"http://example/graphA" "http://example/resource1" "42"
+"http://example/graphB" "http://example/resource1" "42"
diff --git a/tests/libtracker-data/graph/copy-to-existent.rq b/tests/libtracker-data/graph/copy-to-existent.rq
new file mode 100644
index 000000000..cecd2ff71
--- /dev/null
+++ b/tests/libtracker-data/graph/copy-to-existent.rq
@@ -0,0 +1,6 @@
+SELECT ?g ?s ?v WHERE {
+ GRAPH ?g {
+ ?s example:p ?v
+ }
+}
+ORDER BY ?g ?s ?v
diff --git a/tests/libtracker-data/graph/copy-to-non-existent.out b/tests/libtracker-data/graph/copy-to-non-existent.out
new file mode 100644
index 000000000..d9e765277
--- /dev/null
+++ b/tests/libtracker-data/graph/copy-to-non-existent.out
@@ -0,0 +1,2 @@
+"http://example/graphA" "http://example/resource"
+"http://example/graphB" "http://example/resource"
diff --git a/tests/libtracker-data/graph/copy-to-non-existent.rq b/tests/libtracker-data/graph/copy-to-non-existent.rq
new file mode 100644
index 000000000..b2464d25a
--- /dev/null
+++ b/tests/libtracker-data/graph/copy-to-non-existent.rq
@@ -0,0 +1,5 @@
+SELECT ?g ?s WHERE {
+ GRAPH ?g {
+ ?s example:p 42
+ }
+}
diff --git a/tests/libtracker-data/graph/copy.out b/tests/libtracker-data/graph/copy.out
new file mode 100644
index 000000000..d9e765277
--- /dev/null
+++ b/tests/libtracker-data/graph/copy.out
@@ -0,0 +1,2 @@
+"http://example/graphA" "http://example/resource"
+"http://example/graphB" "http://example/resource"
diff --git a/tests/libtracker-data/graph/copy.rq b/tests/libtracker-data/graph/copy.rq
new file mode 100644
index 000000000..b2464d25a
--- /dev/null
+++ b/tests/libtracker-data/graph/copy.rq
@@ -0,0 +1,5 @@
+SELECT ?g ?s WHERE {
+ GRAPH ?g {
+ ?s example:p 42
+ }
+}
diff --git a/tests/libtracker-data/graph/data-2.rq b/tests/libtracker-data/graph/data-2.rq
index bfe6489c6..1a8333244 100644
--- a/tests/libtracker-data/graph/data-2.rq
+++ b/tests/libtracker-data/graph/data-2.rq
@@ -5,7 +5,9 @@ INSERT {
}
}
DELETE {
- example:resource example:p 42
+ GRAPH example:graphA {
+ example:resource example:p 42
+ }
}
INSERT {
GRAPH example:graphB {
diff --git a/tests/libtracker-data/graph/data-5.rq b/tests/libtracker-data/graph/data-5.rq
new file mode 100644
index 000000000..16395fedc
--- /dev/null
+++ b/tests/libtracker-data/graph/data-5.rq
@@ -0,0 +1,10 @@
+INSERT {
+ GRAPH example:graphA {
+ example:resource a example:A ;
+ example:p 73
+ }
+ GRAPH example:graphB {
+ example:resource a example:A ;
+ example:p 42
+ }
+}
diff --git a/tests/libtracker-data/graph/data-add-from-default.rq b/tests/libtracker-data/graph/data-add-from-default.rq
new file mode 100644
index 000000000..eecd51d4d
--- /dev/null
+++ b/tests/libtracker-data/graph/data-add-from-default.rq
@@ -0,0 +1,10 @@
+INSERT {
+ example:resource1 a example:A ;
+ example:p 42 .
+ GRAPH example:graphA {
+ example:resource2 a example:A ;
+ example:p 42
+ }
+}
+
+ADD DEFAULT TO GRAPH example:graphA
diff --git a/tests/libtracker-data/graph/data-add-from-non-existent.rq b/tests/libtracker-data/graph/data-add-from-non-existent.rq
new file mode 100644
index 000000000..f13fb1bf5
--- /dev/null
+++ b/tests/libtracker-data/graph/data-add-from-non-existent.rq
@@ -0,0 +1,8 @@
+INSERT {
+ GRAPH example:graphA {
+ example:resource a example:A ;
+ example:p 42
+ }
+}
+
+ADD SILENT GRAPH example:graphB TO GRAPH example:graphA
diff --git a/tests/libtracker-data/graph/data-add-into-self.rq b/tests/libtracker-data/graph/data-add-into-self.rq
new file mode 100644
index 000000000..8876496db
--- /dev/null
+++ b/tests/libtracker-data/graph/data-add-into-self.rq
@@ -0,0 +1,8 @@
+INSERT {
+ GRAPH example:graphA {
+ example:resource a example:A ;
+ example:p 42
+ }
+}
+
+ADD GRAPH example:graphA TO GRAPH example:graphA
diff --git a/tests/libtracker-data/graph/data-add-to-default.rq b/tests/libtracker-data/graph/data-add-to-default.rq
new file mode 100644
index 000000000..22cd5b8ed
--- /dev/null
+++ b/tests/libtracker-data/graph/data-add-to-default.rq
@@ -0,0 +1,10 @@
+INSERT {
+ example:resource1 a example:A ;
+ example:p 42 .
+ GRAPH example:graphA {
+ example:resource2 a example:A ;
+ example:p 42
+ }
+}
+
+ADD GRAPH example:graphA TO DEFAULT
diff --git a/tests/libtracker-data/graph/data-add-to-existent.rq b/tests/libtracker-data/graph/data-add-to-existent.rq
new file mode 100644
index 000000000..7f250e5dd
--- /dev/null
+++ b/tests/libtracker-data/graph/data-add-to-existent.rq
@@ -0,0 +1,14 @@
+INSERT {
+ GRAPH example:graphA {
+ example:resource1 a example:A ;
+ example:p 42 .
+ }
+ GRAPH example:graphB {
+ example:resource1 a example:A ;
+ example:p 73 .
+ example:resource2 a example:A ;
+ example:p 113 .
+ }
+}
+
+ADD GRAPH example:graphA TO GRAPH example:graphB
diff --git a/tests/libtracker-data/graph/data-add-to-non-existent.rq b/tests/libtracker-data/graph/data-add-to-non-existent.rq
new file mode 100644
index 000000000..b8cbdd38f
--- /dev/null
+++ b/tests/libtracker-data/graph/data-add-to-non-existent.rq
@@ -0,0 +1,8 @@
+INSERT {
+ GRAPH example:graphA {
+ example:resource a example:A ;
+ example:p 42
+ }
+}
+
+ADD GRAPH example:graphA TO GRAPH example:graphB
diff --git a/tests/libtracker-data/graph/data-add.rq b/tests/libtracker-data/graph/data-add.rq
new file mode 100644
index 000000000..f47dc9b01
--- /dev/null
+++ b/tests/libtracker-data/graph/data-add.rq
@@ -0,0 +1,12 @@
+INSERT {
+ GRAPH example:graphA {
+ example:resource1 a example:A ;
+ example:p 42
+ }
+ GRAPH example:graphB {
+ example:resource2 a example:A ;
+ example:p 42
+ }
+}
+
+ADD GRAPH example:graphA TO GRAPH example:graphB
diff --git a/tests/libtracker-data/graph/data-clear-all.rq b/tests/libtracker-data/graph/data-clear-all.rq
new file mode 100644
index 000000000..085cba46a
--- /dev/null
+++ b/tests/libtracker-data/graph/data-clear-all.rq
@@ -0,0 +1,12 @@
+INSERT {
+ example:resource1 a example:A ;
+
+ GRAPH example:graphA {
+ example:resource2 a example:A ;
+ }
+ GRAPH example:graphB {
+ example:resource3 a example:A ;
+ }
+}
+
+DROP ALL
diff --git a/tests/libtracker-data/graph/data-clear-default.rq b/tests/libtracker-data/graph/data-clear-default.rq
new file mode 100644
index 000000000..5a2eaf066
--- /dev/null
+++ b/tests/libtracker-data/graph/data-clear-default.rq
@@ -0,0 +1,12 @@
+INSERT {
+ example:resource1 a example:A ;
+
+ GRAPH example:graphA {
+ example:resource2 a example:A ;
+ }
+ GRAPH example:graphB {
+ example:resource3 a example:A ;
+ }
+}
+
+DROP DEFAULT
diff --git a/tests/libtracker-data/graph/data-clear-named.rq b/tests/libtracker-data/graph/data-clear-named.rq
new file mode 100644
index 000000000..1d63c5c8e
--- /dev/null
+++ b/tests/libtracker-data/graph/data-clear-named.rq
@@ -0,0 +1,12 @@
+INSERT {
+ example:resource1 a example:A ;
+
+ GRAPH example:graphA {
+ example:resource2 a example:A ;
+ }
+ GRAPH example:graphB {
+ example:resource3 a example:A ;
+ }
+}
+
+DROP NAMED
diff --git a/tests/libtracker-data/graph/data-clear-non-existent.rq b/tests/libtracker-data/graph/data-clear-non-existent.rq
new file mode 100644
index 000000000..ea6886671
--- /dev/null
+++ b/tests/libtracker-data/graph/data-clear-non-existent.rq
@@ -0,0 +1 @@
+CLEAR GRAPH example:graphA
diff --git a/tests/libtracker-data/graph/data-clear.rq b/tests/libtracker-data/graph/data-clear.rq
new file mode 100644
index 000000000..1805daecb
--- /dev/null
+++ b/tests/libtracker-data/graph/data-clear.rq
@@ -0,0 +1,12 @@
+INSERT {
+ GRAPH example:graphA {
+ example:resource a example:A ;
+ example:p 42
+ }
+ GRAPH example:graphB {
+ example:resource a example:A ;
+ example:p 42
+ }
+}
+
+CLEAR GRAPH example:graphA
diff --git a/tests/libtracker-data/graph/data-copy-from-default.rq b/tests/libtracker-data/graph/data-copy-from-default.rq
new file mode 100644
index 000000000..6efdacb25
--- /dev/null
+++ b/tests/libtracker-data/graph/data-copy-from-default.rq
@@ -0,0 +1,10 @@
+INSERT {
+ example:resource1 a example:A ;
+ example:p 42 .
+ GRAPH example:graphA {
+ example:resource2 a example:A ;
+ example:p 42
+ }
+}
+
+COPY DEFAULT TO GRAPH example:graphA
diff --git a/tests/libtracker-data/graph/data-copy-from-non-existent.rq b/tests/libtracker-data/graph/data-copy-from-non-existent.rq
new file mode 100644
index 000000000..d90fc8733
--- /dev/null
+++ b/tests/libtracker-data/graph/data-copy-from-non-existent.rq
@@ -0,0 +1,8 @@
+INSERT {
+ GRAPH example:graphA {
+ example:resource a example:A ;
+ example:p 42
+ }
+}
+
+COPY SILENT GRAPH example:graphB TO GRAPH example:graphA
diff --git a/tests/libtracker-data/graph/data-copy-into-self.rq b/tests/libtracker-data/graph/data-copy-into-self.rq
new file mode 100644
index 000000000..489b73f3f
--- /dev/null
+++ b/tests/libtracker-data/graph/data-copy-into-self.rq
@@ -0,0 +1,8 @@
+INSERT {
+ GRAPH example:graphA {
+ example:resource a example:A ;
+ example:p 42
+ }
+}
+
+COPY GRAPH example:graphA TO GRAPH example:graphA
diff --git a/tests/libtracker-data/graph/data-copy-to-default.rq b/tests/libtracker-data/graph/data-copy-to-default.rq
new file mode 100644
index 000000000..a6c5b06a5
--- /dev/null
+++ b/tests/libtracker-data/graph/data-copy-to-default.rq
@@ -0,0 +1,10 @@
+INSERT {
+ example:resource1 a example:A ;
+ example:p 42 .
+ GRAPH example:graphA {
+ example:resource2 a example:A ;
+ example:p 42
+ }
+}
+
+COPY GRAPH example:graphA TO DEFAULT
diff --git a/tests/libtracker-data/graph/data-copy-to-existent.rq b/tests/libtracker-data/graph/data-copy-to-existent.rq
new file mode 100644
index 000000000..76dba86d7
--- /dev/null
+++ b/tests/libtracker-data/graph/data-copy-to-existent.rq
@@ -0,0 +1,14 @@
+INSERT {
+ GRAPH example:graphA {
+ example:resource1 a example:A ;
+ example:p 42 .
+ }
+ GRAPH example:graphB {
+ example:resource1 a example:A ;
+ example:p 73 .
+ example:resource2 a example:A ;
+ example:p 113 .
+ }
+}
+
+COPY GRAPH example:graphA TO GRAPH example:graphB
diff --git a/tests/libtracker-data/graph/data-copy-to-non-existent.rq b/tests/libtracker-data/graph/data-copy-to-non-existent.rq
new file mode 100644
index 000000000..0de5029fa
--- /dev/null
+++ b/tests/libtracker-data/graph/data-copy-to-non-existent.rq
@@ -0,0 +1,8 @@
+INSERT {
+ GRAPH example:graphA {
+ example:resource a example:A ;
+ example:p 42
+ }
+}
+
+COPY GRAPH example:graphA TO GRAPH example:graphB
diff --git a/tests/libtracker-data/graph/data-copy.rq b/tests/libtracker-data/graph/data-copy.rq
new file mode 100644
index 000000000..b5eb054b6
--- /dev/null
+++ b/tests/libtracker-data/graph/data-copy.rq
@@ -0,0 +1,10 @@
+INSERT {
+ GRAPH example:graphA {
+ example:resource a example:A ;
+ example:p 42
+ }
+}
+
+CREATE GRAPH example:graphB
+
+COPY GRAPH example:graphA TO GRAPH example:graphB
diff --git a/tests/libtracker-data/graph/data-drop-all.rq b/tests/libtracker-data/graph/data-drop-all.rq
new file mode 100644
index 000000000..085cba46a
--- /dev/null
+++ b/tests/libtracker-data/graph/data-drop-all.rq
@@ -0,0 +1,12 @@
+INSERT {
+ example:resource1 a example:A ;
+
+ GRAPH example:graphA {
+ example:resource2 a example:A ;
+ }
+ GRAPH example:graphB {
+ example:resource3 a example:A ;
+ }
+}
+
+DROP ALL
diff --git a/tests/libtracker-data/graph/data-drop-default.rq b/tests/libtracker-data/graph/data-drop-default.rq
new file mode 100644
index 000000000..5a2eaf066
--- /dev/null
+++ b/tests/libtracker-data/graph/data-drop-default.rq
@@ -0,0 +1,12 @@
+INSERT {
+ example:resource1 a example:A ;
+
+ GRAPH example:graphA {
+ example:resource2 a example:A ;
+ }
+ GRAPH example:graphB {
+ example:resource3 a example:A ;
+ }
+}
+
+DROP DEFAULT
diff --git a/tests/libtracker-data/graph/data-drop-named.rq b/tests/libtracker-data/graph/data-drop-named.rq
new file mode 100644
index 000000000..1d63c5c8e
--- /dev/null
+++ b/tests/libtracker-data/graph/data-drop-named.rq
@@ -0,0 +1,12 @@
+INSERT {
+ example:resource1 a example:A ;
+
+ GRAPH example:graphA {
+ example:resource2 a example:A ;
+ }
+ GRAPH example:graphB {
+ example:resource3 a example:A ;
+ }
+}
+
+DROP NAMED
diff --git a/tests/libtracker-data/graph/data-drop-non-existent.rq b/tests/libtracker-data/graph/data-drop-non-existent.rq
new file mode 100644
index 000000000..a8132492e
--- /dev/null
+++ b/tests/libtracker-data/graph/data-drop-non-existent.rq
@@ -0,0 +1 @@
+DROP GRAPH example:graphA
diff --git a/tests/libtracker-data/graph/data-drop-silent.rq b/tests/libtracker-data/graph/data-drop-silent.rq
new file mode 100644
index 000000000..9048a2831
--- /dev/null
+++ b/tests/libtracker-data/graph/data-drop-silent.rq
@@ -0,0 +1 @@
+DROP SILENT GRAPH example:graphA \ No newline at end of file
diff --git a/tests/libtracker-data/graph/data-drop.rq b/tests/libtracker-data/graph/data-drop.rq
new file mode 100644
index 000000000..e79d18291
--- /dev/null
+++ b/tests/libtracker-data/graph/data-drop.rq
@@ -0,0 +1,12 @@
+INSERT {
+ GRAPH example:graphA {
+ example:resource a example:A ;
+ example:p 42
+ }
+ GRAPH example:graphB {
+ example:resource a example:A ;
+ example:p 42
+ }
+}
+
+DROP GRAPH example:graphA
diff --git a/tests/libtracker-data/graph/data-move-from-default.rq b/tests/libtracker-data/graph/data-move-from-default.rq
new file mode 100644
index 000000000..4a9c2783e
--- /dev/null
+++ b/tests/libtracker-data/graph/data-move-from-default.rq
@@ -0,0 +1,10 @@
+INSERT {
+ example:resource1 a example:A ;
+ example:p 42 .
+ GRAPH example:graphA {
+ example:resource2 a example:A ;
+ example:p 42
+ }
+}
+
+MOVE DEFAULT TO GRAPH example:graphA
diff --git a/tests/libtracker-data/graph/data-move-from-non-existent.rq b/tests/libtracker-data/graph/data-move-from-non-existent.rq
new file mode 100644
index 000000000..e17562ed5
--- /dev/null
+++ b/tests/libtracker-data/graph/data-move-from-non-existent.rq
@@ -0,0 +1,8 @@
+INSERT {
+ GRAPH example:graphA {
+ example:resource a example:A ;
+ example:p 42
+ }
+}
+
+MOVE SILENT GRAPH example:graphB TO GRAPH example:graphA
diff --git a/tests/libtracker-data/graph/data-move-into-self.rq b/tests/libtracker-data/graph/data-move-into-self.rq
new file mode 100644
index 000000000..3aca55b25
--- /dev/null
+++ b/tests/libtracker-data/graph/data-move-into-self.rq
@@ -0,0 +1,8 @@
+INSERT {
+ GRAPH example:graphA {
+ example:resource a example:A ;
+ example:p 42
+ }
+}
+
+MOVE GRAPH example:graphA TO GRAPH example:graphA
diff --git a/tests/libtracker-data/graph/data-move-to-default.rq b/tests/libtracker-data/graph/data-move-to-default.rq
new file mode 100644
index 000000000..4fe0fc0e9
--- /dev/null
+++ b/tests/libtracker-data/graph/data-move-to-default.rq
@@ -0,0 +1,10 @@
+INSERT {
+ example:resource1 a example:A ;
+ example:p 42 .
+ GRAPH example:graphA {
+ example:resource2 a example:A ;
+ example:p 42
+ }
+}
+
+MOVE GRAPH example:graphA TO DEFAULT
diff --git a/tests/libtracker-data/graph/data-move-to-existent.rq b/tests/libtracker-data/graph/data-move-to-existent.rq
new file mode 100644
index 000000000..af9ee6a7c
--- /dev/null
+++ b/tests/libtracker-data/graph/data-move-to-existent.rq
@@ -0,0 +1,14 @@
+INSERT {
+ GRAPH example:graphA {
+ example:resource1 a example:A ;
+ example:p 42 .
+ }
+ GRAPH example:graphB {
+ example:resource1 a example:A ;
+ example:p 73 .
+ example:resource2 a example:A ;
+ example:p 113 .
+ }
+}
+
+MOVE GRAPH example:graphA TO GRAPH example:graphB
diff --git a/tests/libtracker-data/graph/data-move.rq b/tests/libtracker-data/graph/data-move.rq
new file mode 100644
index 000000000..7e628ba5e
--- /dev/null
+++ b/tests/libtracker-data/graph/data-move.rq
@@ -0,0 +1,8 @@
+INSERT {
+ GRAPH example:graphA {
+ example:resource a example:A ;
+ example:p 42
+ }
+}
+
+MOVE GRAPH example:graphA TO GRAPH example:graphB
diff --git a/tests/libtracker-data/graph/drop-all.out b/tests/libtracker-data/graph/drop-all.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/libtracker-data/graph/drop-all.out
diff --git a/tests/libtracker-data/graph/drop-all.rq b/tests/libtracker-data/graph/drop-all.rq
new file mode 100644
index 000000000..4507ec3c0
--- /dev/null
+++ b/tests/libtracker-data/graph/drop-all.rq
@@ -0,0 +1,4 @@
+SELECT ?s WHERE {
+ ?s a example:A
+}
+ORDER BY ?s
diff --git a/tests/libtracker-data/graph/drop-default.out b/tests/libtracker-data/graph/drop-default.out
new file mode 100644
index 000000000..07170b538
--- /dev/null
+++ b/tests/libtracker-data/graph/drop-default.out
@@ -0,0 +1,2 @@
+"http://example/resource2"
+"http://example/resource3"
diff --git a/tests/libtracker-data/graph/drop-default.rq b/tests/libtracker-data/graph/drop-default.rq
new file mode 100644
index 000000000..4507ec3c0
--- /dev/null
+++ b/tests/libtracker-data/graph/drop-default.rq
@@ -0,0 +1,4 @@
+SELECT ?s WHERE {
+ ?s a example:A
+}
+ORDER BY ?s
diff --git a/tests/libtracker-data/graph/drop-named.out b/tests/libtracker-data/graph/drop-named.out
new file mode 100644
index 000000000..d24fcef0c
--- /dev/null
+++ b/tests/libtracker-data/graph/drop-named.out
@@ -0,0 +1 @@
+"http://example/resource1"
diff --git a/tests/libtracker-data/graph/drop-named.rq b/tests/libtracker-data/graph/drop-named.rq
new file mode 100644
index 000000000..4507ec3c0
--- /dev/null
+++ b/tests/libtracker-data/graph/drop-named.rq
@@ -0,0 +1,4 @@
+SELECT ?s WHERE {
+ ?s a example:A
+}
+ORDER BY ?s
diff --git a/tests/libtracker-data/graph/drop-non-existent.out b/tests/libtracker-data/graph/drop-non-existent.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/libtracker-data/graph/drop-non-existent.out
diff --git a/tests/libtracker-data/graph/drop-non-existent.rq b/tests/libtracker-data/graph/drop-non-existent.rq
new file mode 100644
index 000000000..b2464d25a
--- /dev/null
+++ b/tests/libtracker-data/graph/drop-non-existent.rq
@@ -0,0 +1,5 @@
+SELECT ?g ?s WHERE {
+ GRAPH ?g {
+ ?s example:p 42
+ }
+}
diff --git a/tests/libtracker-data/graph/drop-silent.out b/tests/libtracker-data/graph/drop-silent.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/libtracker-data/graph/drop-silent.out
diff --git a/tests/libtracker-data/graph/drop-silent.rq b/tests/libtracker-data/graph/drop-silent.rq
new file mode 100644
index 000000000..18dfd8ad5
--- /dev/null
+++ b/tests/libtracker-data/graph/drop-silent.rq
@@ -0,0 +1,5 @@
+SELECT * {
+ GRAPH example:graphA {
+ ?s ?p ?u
+ }
+}
diff --git a/tests/libtracker-data/graph/drop.out b/tests/libtracker-data/graph/drop.out
new file mode 100644
index 000000000..cf0a28d0b
--- /dev/null
+++ b/tests/libtracker-data/graph/drop.out
@@ -0,0 +1 @@
+"http://example/graphB" "http://example/resource"
diff --git a/tests/libtracker-data/graph/drop.rq b/tests/libtracker-data/graph/drop.rq
new file mode 100644
index 000000000..b2464d25a
--- /dev/null
+++ b/tests/libtracker-data/graph/drop.rq
@@ -0,0 +1,5 @@
+SELECT ?g ?s WHERE {
+ GRAPH ?g {
+ ?s example:p 42
+ }
+}
diff --git a/tests/libtracker-data/graph/graph-1.out b/tests/libtracker-data/graph/graph-1.out
index 0476f7477..d9e765277 100644
--- a/tests/libtracker-data/graph/graph-1.out
+++ b/tests/libtracker-data/graph/graph-1.out
@@ -1 +1,2 @@
"http://example/graphA" "http://example/resource"
+"http://example/graphB" "http://example/resource"
diff --git a/tests/libtracker-data/graph/graph-4.rq b/tests/libtracker-data/graph/graph-4.rq
index 6e7248834..aa402a9b9 100644
--- a/tests/libtracker-data/graph/graph-4.rq
+++ b/tests/libtracker-data/graph/graph-4.rq
@@ -1,5 +1,6 @@
SELECT ?g ?p ?v WHERE {
GRAPH ?g {
- example:resource ?p ?v
+ example:resource ?p ?v .
+ FILTER (?p != tracker:added && ?p != tracker:modified)
}
} ORDER BY ?g ?p ?v
diff --git a/tests/libtracker-data/graph/graph-5.rq b/tests/libtracker-data/graph/graph-5.rq
index 6e7248834..aa402a9b9 100644
--- a/tests/libtracker-data/graph/graph-5.rq
+++ b/tests/libtracker-data/graph/graph-5.rq
@@ -1,5 +1,6 @@
SELECT ?g ?p ?v WHERE {
GRAPH ?g {
- example:resource ?p ?v
+ example:resource ?p ?v .
+ FILTER (?p != tracker:added && ?p != tracker:modified)
}
} ORDER BY ?g ?p ?v
diff --git a/tests/libtracker-data/graph/graph-6.out b/tests/libtracker-data/graph/graph-6.out
new file mode 100644
index 000000000..b87869c39
--- /dev/null
+++ b/tests/libtracker-data/graph/graph-6.out
@@ -0,0 +1,2 @@
+"http://example/graphA" "http://example/resource" "73"
+"http://example/graphB" "http://example/resource" "42"
diff --git a/tests/libtracker-data/graph/graph-6.rq b/tests/libtracker-data/graph/graph-6.rq
new file mode 100644
index 000000000..f0905b6e1
--- /dev/null
+++ b/tests/libtracker-data/graph/graph-6.rq
@@ -0,0 +1,6 @@
+SELECT ?g ?s ?v WHERE {
+ GRAPH ?g {
+ ?s example:p ?v
+ }
+}
+ORDER BY ?g ?s ?v \ No newline at end of file
diff --git a/tests/libtracker-data/graph/move-from-default.out b/tests/libtracker-data/graph/move-from-default.out
new file mode 100644
index 000000000..d24fcef0c
--- /dev/null
+++ b/tests/libtracker-data/graph/move-from-default.out
@@ -0,0 +1 @@
+"http://example/resource1"
diff --git a/tests/libtracker-data/graph/move-from-default.rq b/tests/libtracker-data/graph/move-from-default.rq
new file mode 100644
index 000000000..22da1a1c8
--- /dev/null
+++ b/tests/libtracker-data/graph/move-from-default.rq
@@ -0,0 +1,5 @@
+SELECT ?s WHERE {
+ GRAPH example:graphA {
+ ?s example:p 42
+ }
+}
diff --git a/tests/libtracker-data/graph/move-from-non-existent.out b/tests/libtracker-data/graph/move-from-non-existent.out
new file mode 100644
index 000000000..0476f7477
--- /dev/null
+++ b/tests/libtracker-data/graph/move-from-non-existent.out
@@ -0,0 +1 @@
+"http://example/graphA" "http://example/resource"
diff --git a/tests/libtracker-data/graph/move-from-non-existent.rq b/tests/libtracker-data/graph/move-from-non-existent.rq
new file mode 100644
index 000000000..b2464d25a
--- /dev/null
+++ b/tests/libtracker-data/graph/move-from-non-existent.rq
@@ -0,0 +1,5 @@
+SELECT ?g ?s WHERE {
+ GRAPH ?g {
+ ?s example:p 42
+ }
+}
diff --git a/tests/libtracker-data/graph/move-into-self.out b/tests/libtracker-data/graph/move-into-self.out
new file mode 100644
index 000000000..0476f7477
--- /dev/null
+++ b/tests/libtracker-data/graph/move-into-self.out
@@ -0,0 +1 @@
+"http://example/graphA" "http://example/resource"
diff --git a/tests/libtracker-data/graph/move-into-self.rq b/tests/libtracker-data/graph/move-into-self.rq
new file mode 100644
index 000000000..b2464d25a
--- /dev/null
+++ b/tests/libtracker-data/graph/move-into-self.rq
@@ -0,0 +1,5 @@
+SELECT ?g ?s WHERE {
+ GRAPH ?g {
+ ?s example:p 42
+ }
+}
diff --git a/tests/libtracker-data/graph/move-to-default.out b/tests/libtracker-data/graph/move-to-default.out
new file mode 100644
index 000000000..e6ab2fcf6
--- /dev/null
+++ b/tests/libtracker-data/graph/move-to-default.out
@@ -0,0 +1 @@
+"http://example/resource2"
diff --git a/tests/libtracker-data/graph/move-to-default.rq b/tests/libtracker-data/graph/move-to-default.rq
new file mode 100644
index 000000000..fcf167ee2
--- /dev/null
+++ b/tests/libtracker-data/graph/move-to-default.rq
@@ -0,0 +1,3 @@
+SELECT DISTINCT ?s WHERE {
+ ?s example:p 42
+}
diff --git a/tests/libtracker-data/graph/move-to-existent.out b/tests/libtracker-data/graph/move-to-existent.out
new file mode 100644
index 000000000..194f1e218
--- /dev/null
+++ b/tests/libtracker-data/graph/move-to-existent.out
@@ -0,0 +1 @@
+"http://example/graphB" "http://example/resource1" "42"
diff --git a/tests/libtracker-data/graph/move-to-existent.rq b/tests/libtracker-data/graph/move-to-existent.rq
new file mode 100644
index 000000000..cecd2ff71
--- /dev/null
+++ b/tests/libtracker-data/graph/move-to-existent.rq
@@ -0,0 +1,6 @@
+SELECT ?g ?s ?v WHERE {
+ GRAPH ?g {
+ ?s example:p ?v
+ }
+}
+ORDER BY ?g ?s ?v
diff --git a/tests/libtracker-data/graph/move.out b/tests/libtracker-data/graph/move.out
new file mode 100644
index 000000000..cf0a28d0b
--- /dev/null
+++ b/tests/libtracker-data/graph/move.out
@@ -0,0 +1 @@
+"http://example/graphB" "http://example/resource"
diff --git a/tests/libtracker-data/graph/move.rq b/tests/libtracker-data/graph/move.rq
new file mode 100644
index 000000000..b2464d25a
--- /dev/null
+++ b/tests/libtracker-data/graph/move.rq
@@ -0,0 +1,5 @@
+SELECT ?g ?s WHERE {
+ GRAPH ?g {
+ ?s example:p 42
+ }
+}
diff --git a/tests/libtracker-data/graph/non-existent-1.out b/tests/libtracker-data/graph/non-existent-1.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/libtracker-data/graph/non-existent-1.out
diff --git a/tests/libtracker-data/graph/non-existent-1.rq b/tests/libtracker-data/graph/non-existent-1.rq
new file mode 100644
index 000000000..03d891030
--- /dev/null
+++ b/tests/libtracker-data/graph/non-existent-1.rq
@@ -0,0 +1,5 @@
+SELECT * {
+ GRAPH <non-existent> {
+ ?s ?p ?u
+ }
+}
diff --git a/tests/libtracker-data/langstring/data.rq b/tests/libtracker-data/langstring/data.rq
new file mode 100644
index 000000000..ae71299ea
--- /dev/null
+++ b/tests/libtracker-data/langstring/data.rq
@@ -0,0 +1,16 @@
+INSERT {
+ <a> a example:A ;
+ example:langString "foo"@en ;
+ example:string "bar" .
+
+ <b> a example:A ;
+ example:langString "foo"@fr ;
+ example:string "baz" .
+
+ <c> a example:A ;
+ example:langString "foo" ;
+ example:string "bla"@es .
+
+ <d> a example:A ;
+ example:langString "bleh"@en .
+}
diff --git a/tests/libtracker-data/langstring/langmatches.out b/tests/libtracker-data/langstring/langmatches.out
new file mode 100644
index 000000000..22d3c21b7
--- /dev/null
+++ b/tests/libtracker-data/langstring/langmatches.out
@@ -0,0 +1,2 @@
+"a"
+"d"
diff --git a/tests/libtracker-data/langstring/langmatches.rq b/tests/libtracker-data/langstring/langmatches.rq
new file mode 100644
index 000000000..d8c693b37
--- /dev/null
+++ b/tests/libtracker-data/langstring/langmatches.rq
@@ -0,0 +1 @@
+SELECT ?u { ?u example:langString ?s . FILTER (LANGMATCHES (?s, "en")) } order by ?u \ No newline at end of file
diff --git a/tests/libtracker-data/langstring/match-non-langstring.out b/tests/libtracker-data/langstring/match-non-langstring.out
new file mode 100644
index 000000000..cdf158597
--- /dev/null
+++ b/tests/libtracker-data/langstring/match-non-langstring.out
@@ -0,0 +1 @@
+"c"
diff --git a/tests/libtracker-data/langstring/match-non-langstring.rq b/tests/libtracker-data/langstring/match-non-langstring.rq
new file mode 100644
index 000000000..8d4283d89
--- /dev/null
+++ b/tests/libtracker-data/langstring/match-non-langstring.rq
@@ -0,0 +1 @@
+SELECT ?u { ?u example:string "bla"@es }
diff --git a/tests/libtracker-data/langstring/match-with-langstring.out b/tests/libtracker-data/langstring/match-with-langstring.out
new file mode 100644
index 000000000..231f150c5
--- /dev/null
+++ b/tests/libtracker-data/langstring/match-with-langstring.out
@@ -0,0 +1 @@
+"a"
diff --git a/tests/libtracker-data/langstring/match-with-langstring.rq b/tests/libtracker-data/langstring/match-with-langstring.rq
new file mode 100644
index 000000000..ff2951875
--- /dev/null
+++ b/tests/libtracker-data/langstring/match-with-langstring.rq
@@ -0,0 +1 @@
+SELECT ?u { ?u example:langString "foo"@en }
diff --git a/tests/libtracker-data/langstring/match-with-non-langstring.out b/tests/libtracker-data/langstring/match-with-non-langstring.out
new file mode 100644
index 000000000..cdf158597
--- /dev/null
+++ b/tests/libtracker-data/langstring/match-with-non-langstring.out
@@ -0,0 +1 @@
+"c"
diff --git a/tests/libtracker-data/langstring/match-with-non-langstring.rq b/tests/libtracker-data/langstring/match-with-non-langstring.rq
new file mode 100644
index 000000000..3ceef7b28
--- /dev/null
+++ b/tests/libtracker-data/langstring/match-with-non-langstring.rq
@@ -0,0 +1 @@
+SELECT ?u { ?u example:langString "foo" }
diff --git a/tests/libtracker-data/langstring/strlang.out b/tests/libtracker-data/langstring/strlang.out
new file mode 100644
index 000000000..231f150c5
--- /dev/null
+++ b/tests/libtracker-data/langstring/strlang.out
@@ -0,0 +1 @@
+"a"
diff --git a/tests/libtracker-data/langstring/strlang.rq b/tests/libtracker-data/langstring/strlang.rq
new file mode 100644
index 000000000..712ae6461
--- /dev/null
+++ b/tests/libtracker-data/langstring/strlang.rq
@@ -0,0 +1 @@
+SELECT ?u { ?u example:langString ?s . FILTER (?s = STRLANG ("foo", "en")) }
diff --git a/tests/libtracker-data/langstring/test.ontology b/tests/libtracker-data/langstring/test.ontology
new file mode 100644
index 000000000..fa600f7fe
--- /dev/null
+++ b/tests/libtracker-data/langstring/test.ontology
@@ -0,0 +1,19 @@
+@prefix example: <http://example/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix tracker: <http://www.tracker-project.org/ontologies/tracker#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+example: a tracker:Namespace ;
+ tracker:prefix "example" .
+
+example:A a rdfs:Class ;
+ rdfs:subClassOf rdfs:Resource .
+
+example:langString a rdf:Property ;
+ rdfs:domain example:A ;
+ rdfs:range rdf:langString .
+
+example:string a rdf:Property ;
+ rdfs:domain example:A ;
+ rdfs:range xsd:string .
diff --git a/tests/libtracker-data/lists/data-list-in-object.rq b/tests/libtracker-data/lists/data-list-in-object.rq
new file mode 100644
index 000000000..c033f459e
--- /dev/null
+++ b/tests/libtracker-data/lists/data-list-in-object.rq
@@ -0,0 +1,8 @@
+INSERT {
+ <a> a example:A ;
+ example:label "root" ;
+ example:list ( [ a example:A ; example:label "elem1" ]
+ [ a example:A ; example:label "elem2" ]
+ [ a example:A ; example:label "elem3" ]
+ [ a example:A ; example:label "elem4" ] ) .
+}
diff --git a/tests/libtracker-data/lists/data-list-in-select.rq b/tests/libtracker-data/lists/data-list-in-select.rq
new file mode 100644
index 000000000..c033f459e
--- /dev/null
+++ b/tests/libtracker-data/lists/data-list-in-select.rq
@@ -0,0 +1,8 @@
+INSERT {
+ <a> a example:A ;
+ example:label "root" ;
+ example:list ( [ a example:A ; example:label "elem1" ]
+ [ a example:A ; example:label "elem2" ]
+ [ a example:A ; example:label "elem3" ]
+ [ a example:A ; example:label "elem4" ] ) .
+}
diff --git a/tests/libtracker-data/lists/data-list-in-subject.rq b/tests/libtracker-data/lists/data-list-in-subject.rq
new file mode 100644
index 000000000..acb407f93
--- /dev/null
+++ b/tests/libtracker-data/lists/data-list-in-subject.rq
@@ -0,0 +1,6 @@
+INSERT {
+ ([ a example:A ; example:label "elem1" ]
+ [ a example:A ; example:label "elem2" ]
+ [ a example:A ; example:label "elem3" ]
+ [ a example:A ; example:label "elem4" ] ) a example:A ; example:label 'root' .
+} \ No newline at end of file
diff --git a/tests/libtracker-data/lists/data-list-nested.rq b/tests/libtracker-data/lists/data-list-nested.rq
new file mode 100644
index 000000000..3528c527a
--- /dev/null
+++ b/tests/libtracker-data/lists/data-list-nested.rq
@@ -0,0 +1,17 @@
+INSERT {
+ ([ a example:A ; example:label "elem1" ;
+ example:list ([ a example:A ; example:label "elem1-1" ]) ]
+ [ a example:A ; example:label "elem2" ;
+ example:list ([ a example:A ; example:label "elem2-1" ]
+ [ a example:A ; example:label "elem2-2" ]) ]
+ [ a example:A ; example:label "elem3" ;
+ example:list ([ a example:A ; example:label "elem3-1" ]
+ [ a example:A ; example:label "elem3-2" ]
+ [ a example:A ; example:label "elem3-3" ]) ]
+ [ a example:A ; example:label "elem4" ;
+ example:list ([ a example:A ; example:label "elem4-1" ]
+ [ a example:A ; example:label "elem4-2" ]
+ [ a example:A ; example:label "elem4-3" ]
+ [ a example:A ; example:label "elem4-4" ]) ]
+ ) a example:A ; example:label 'root' .
+}
diff --git a/tests/libtracker-data/lists/list-in-object.out b/tests/libtracker-data/lists/list-in-object.out
new file mode 100644
index 000000000..a5887b21c
--- /dev/null
+++ b/tests/libtracker-data/lists/list-in-object.out
@@ -0,0 +1,4 @@
+"elem1"
+"elem2"
+"elem3"
+"elem4"
diff --git a/tests/libtracker-data/lists/list-in-object.rq b/tests/libtracker-data/lists/list-in-object.rq
new file mode 100644
index 000000000..6c840fcf5
--- /dev/null
+++ b/tests/libtracker-data/lists/list-in-object.rq
@@ -0,0 +1,2 @@
+SELECT example:label(?e) { ?u example:label 'root' ; example:list ?l .
+ ?l rdf:rest*/rdf:first ?e }
diff --git a/tests/libtracker-data/lists/list-in-select.out b/tests/libtracker-data/lists/list-in-select.out
new file mode 100644
index 000000000..f7a095b0a
--- /dev/null
+++ b/tests/libtracker-data/lists/list-in-select.out
@@ -0,0 +1 @@
+"root"
diff --git a/tests/libtracker-data/lists/list-in-select.rq b/tests/libtracker-data/lists/list-in-select.rq
new file mode 100644
index 000000000..503952d17
--- /dev/null
+++ b/tests/libtracker-data/lists/list-in-select.rq
@@ -0,0 +1,4 @@
+SELECT example:label(?u) { ?u example:list ( [ example:label 'elem1' ]
+ [ example:label 'elem2' ]
+ [ example:label 'elem3' ]
+ [ example:label 'elem4' ] ) }
diff --git a/tests/libtracker-data/lists/list-in-subject.out b/tests/libtracker-data/lists/list-in-subject.out
new file mode 100644
index 000000000..a5887b21c
--- /dev/null
+++ b/tests/libtracker-data/lists/list-in-subject.out
@@ -0,0 +1,4 @@
+"elem1"
+"elem2"
+"elem3"
+"elem4"
diff --git a/tests/libtracker-data/lists/list-in-subject.rq b/tests/libtracker-data/lists/list-in-subject.rq
new file mode 100644
index 000000000..1300e7cfc
--- /dev/null
+++ b/tests/libtracker-data/lists/list-in-subject.rq
@@ -0,0 +1,2 @@
+SELECT example:label(?e) { ?u example:label 'root' .
+ ?u rdf:rest*/rdf:first ?e }
diff --git a/tests/libtracker-data/lists/list-nested.out b/tests/libtracker-data/lists/list-nested.out
new file mode 100644
index 000000000..c4503b7e5
--- /dev/null
+++ b/tests/libtracker-data/lists/list-nested.out
@@ -0,0 +1,10 @@
+"elem1" "elem1-1"
+"elem2" "elem2-1"
+"elem2" "elem2-2"
+"elem3" "elem3-1"
+"elem3" "elem3-2"
+"elem3" "elem3-3"
+"elem4" "elem4-1"
+"elem4" "elem4-2"
+"elem4" "elem4-3"
+"elem4" "elem4-4"
diff --git a/tests/libtracker-data/lists/list-nested.rq b/tests/libtracker-data/lists/list-nested.rq
new file mode 100644
index 000000000..24d97c6d7
--- /dev/null
+++ b/tests/libtracker-data/lists/list-nested.rq
@@ -0,0 +1 @@
+SELECT ?r ?l { ?u example:label ?r ; example:list/rdf:rest*/rdf:first/example:label ?l } order by ?r ?l
diff --git a/tests/libtracker-data/lists/test.ontology b/tests/libtracker-data/lists/test.ontology
new file mode 100644
index 000000000..d541030cb
--- /dev/null
+++ b/tests/libtracker-data/lists/test.ontology
@@ -0,0 +1,19 @@
+@prefix example: <http://example/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix tracker: <http://www.tracker-project.org/ontologies/tracker#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+example: a tracker:Namespace ;
+ tracker:prefix "example" .
+
+example:A a rdfs:Class ;
+ rdfs:subClassOf rdfs:Resource .
+
+example:list a rdf:Property ;
+ rdfs:domain example:A ;
+ rdfs:range rdf:List .
+
+example:label a rdf:Property ;
+ rdfs:domain example:A ;
+ rdfs:range xsd:string .
diff --git a/tests/libtracker-data/meson.build b/tests/libtracker-data/meson.build
index dd559ccd7..74fc2046e 100644
--- a/tests/libtracker-data/meson.build
+++ b/tests/libtracker-data/meson.build
@@ -1,7 +1,6 @@
libtracker_data_tests = [
'backup',
'crc32',
- 'db-journal',
'ontology-change',
'sparql-blank',
]
@@ -16,15 +15,15 @@ libtracker_data_test_deps = [tracker_common_dep, tracker_data_dep, tracker_sparq
foreach base_name: libtracker_data_tests
source = 'tracker-@0@-test.c'.format(base_name)
binary_name = 'tracker-@0@-test'.format(base_name)
- test_name = 'data-@0@'.format(base_name)
binary = executable(binary_name, source,
dependencies: libtracker_data_test_deps,
c_args: test_c_args)
tests += {
- 'name': test_name,
+ 'name': base_name,
'exe': binary,
+ 'suite': ['data'],
'requires_dbus': true,
}
endforeach
@@ -32,15 +31,15 @@ endforeach
foreach base_name: libtracker_data_slow_tests
source = 'tracker-@0@-test.c'.format(base_name)
binary_name = 'tracker-@0@-test'.format(base_name)
- test_name = 'data-@0@'.format(base_name)
binary = executable(binary_name, source,
dependencies: libtracker_data_test_deps,
c_args: test_c_args)
tests += {
- 'name': test_name,
+ 'name': base_name,
'exe': binary,
+ 'suite': ['data', 'slow'],
'requires_dbus': true,
'timeout': 180
}
diff --git a/tests/libtracker-data/optional/q-opt-complex-1.rq b/tests/libtracker-data/optional/q-opt-complex-1.rq
index 2ff7cb6b6..5d5d3d8af 100644
--- a/tests/libtracker-data/optional/q-opt-complex-1.rq
+++ b/tests/libtracker-data/optional/q-opt-complex-1.rq
@@ -8,4 +8,5 @@ SELECT ?person ?nick ?page ?img ?name ?firstN
{ ?person foaf:depiction ?img } UNION
{ ?person foaf:firstName ?firstN }
} FILTER ( bound(?page) || bound(?img) || bound(?firstN) )
-} \ No newline at end of file
+}
+ORDER BY ?person ?nick ?page ?img ?name ?firstN
diff --git a/tests/libtracker-data/property-paths/data.ttl b/tests/libtracker-data/property-paths/data.ttl
index 5088a80d3..6eedc6b3d 100644
--- a/tests/libtracker-data/property-paths/data.ttl
+++ b/tests/libtracker-data/property-paths/data.ttl
@@ -2,12 +2,17 @@
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+_:group
+ rdf:type foaf:Group ;
+ foaf:name "Foo"
+ .
+
_:alice
rdf:type foaf:Person ;
foaf:name "Alice" ;
foaf:mbox <mailto:alice@work> ;
foaf:knows _:bob ;
- foaf:member [ a foaf:Group ; foaf:name "Foo" ] ;
+ foaf:member _:group ;
.
_:bob
@@ -16,7 +21,7 @@ _:bob
foaf:knows _:alice ;
foaf:mbox <mailto:bob@work> ;
foaf:mbox <mailto:bob@home> ;
- foaf:member [ a foaf:Group ; foaf:name "Foo" ] ;
+ foaf:member _:group ;
.
diff --git a/tests/libtracker-data/tracker-backup-test.c b/tests/libtracker-data/tracker-backup-test.c
index e3502dda5..a14549680 100644
--- a/tests/libtracker-data/tracker-backup-test.c
+++ b/tests/libtracker-data/tracker-backup-test.c
@@ -123,11 +123,9 @@ test_backup_and_restore_helper (const gchar *db_location,
NULL);
test_schemas = g_file_new_for_path (ontologies);
- tracker_db_journal_set_rotating (FALSE, G_MAXSIZE, NULL);
-
manager = tracker_data_manager_new (TRACKER_DB_MANAGER_FORCE_REINDEX,
data_location, data_location, test_schemas,
- FALSE, FALSE, 100, 100);
+ FALSE, 100, 100);
g_initable_init (G_INITABLE (manager), NULL, &error);
g_assert_no_error (error);
@@ -136,7 +134,7 @@ test_backup_and_restore_helper (const gchar *db_location,
if (g_file_test (data_filename, G_FILE_TEST_IS_REGULAR)) {
GFile *file = g_file_new_for_path (data_filename);
data_update = tracker_data_manager_get_data (manager);
- tracker_turtle_reader_load (file, data_update, &error);
+ tracker_data_load_turtle_file (data_update, file, NULL, &error);
g_assert_no_error (error);
g_object_unref (file);
} else {
@@ -173,29 +171,13 @@ test_backup_and_restore_helper (const gchar *db_location,
g_unlink (meta_db);
g_free (meta_db);
-#ifndef DISABLE_JOURNAL
- if (!journal) {
- meta_db = g_build_path (G_DIR_SEPARATOR_S, db_location, "data", "tracker-store.journal", NULL);
- g_unlink (meta_db);
- g_free (meta_db);
-
- meta_db = g_build_path (G_DIR_SEPARATOR_S, db_location, "data", "tracker-store.ontology.journal", NULL);
- g_unlink (meta_db);
- g_free (meta_db);
- }
-#endif /* DISABLE_JOURNAL */
-
meta_db = g_build_path (G_DIR_SEPARATOR_S, db_location, "data", ".meta.isrunning", NULL);
g_unlink (meta_db);
g_free (meta_db);
-#ifndef DISABLE_JOURNAL
- tracker_db_journal_set_rotating (FALSE, G_MAXSIZE, NULL);
-#endif /* DISABLE_JOURNAL */
-
manager = tracker_data_manager_new (TRACKER_DB_MANAGER_FORCE_REINDEX,
data_location, data_location, test_schemas,
- FALSE, FALSE, 100, 100);
+ FALSE, 100, 100);
g_initable_init (G_INITABLE (manager), NULL, &error);
g_assert_no_error (error);
@@ -206,7 +188,7 @@ test_backup_and_restore_helper (const gchar *db_location,
g_object_unref (manager);
manager = tracker_data_manager_new (0, data_location, data_location, test_schemas,
- FALSE, FALSE, 100, 100);
+ FALSE, 100, 100);
g_initable_init (G_INITABLE (manager), NULL, &error);
g_assert_no_error (error);
check_content_in_db (manager, 3, 1);
diff --git a/tests/libtracker-data/tracker-db-journal-test.c b/tests/libtracker-data/tracker-db-journal-test.c
deleted file mode 100644
index 184c6ab46..000000000
--- a/tests/libtracker-data/tracker-db-journal-test.c
+++ /dev/null
@@ -1,405 +0,0 @@
-/*
- * Copyright (C) 2008, Nokia <ivan.frade@nokia.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation; either
- * version 2 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU 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.
- */
-
-#include "config.h"
-
-#include <glib/gstdio.h>
-
-#include <libtracker-data/tracker-db-journal.h>
-
-#ifndef DISABLE_JOURNAL
-
-static void
-test_init_and_shutdown (void)
-{
- GError *error = NULL;
- gboolean result;
- gchar *path;
- GFile *data_location, *child;
- TrackerDBJournal *writer;
-
- path = g_build_filename (TOP_BUILDDIR, "tests", "libtracker-db", NULL);
- data_location = g_file_new_for_path (path);
- g_free (path);
-
- child = g_file_get_child (data_location, "tracker-store.journal");
- path = g_file_get_path (child);
- g_unlink (path);
- g_object_unref (child);
-
- /* check double init/shutdown */
- tracker_db_journal_set_rotating (FALSE, G_MAXSIZE, NULL);
- writer = tracker_db_journal_new (data_location, FALSE, &error);
- g_assert_no_error (error);
- g_assert_nonnull (writer);
-
- result = tracker_db_journal_free (writer, &error);
- g_assert_no_error (error);
- g_assert (result == TRUE);
-
- tracker_db_journal_set_rotating (FALSE, G_MAXSIZE, NULL);
- writer = tracker_db_journal_new (data_location, FALSE, &error);
- g_assert_no_error (error);
- g_assert_nonnull (writer);
-
- result = tracker_db_journal_free (writer, &error);
- g_assert_no_error (error);
- g_assert (result == TRUE);
-
- g_object_unref (data_location);
- g_free (path);
-}
-
-static void
-test_write_functions (void)
-{
- gchar *path;
- gsize initial_size, actual_size;
- gboolean result;
- GError *error = NULL;
- GFile *data_location, *child;
- TrackerDBJournal *writer;
-
- path = g_build_filename (TOP_BUILDDIR, "tests", "libtracker-db", NULL);
- data_location = g_file_new_for_path (path);
- g_free (path);
-
- child = g_file_get_child (data_location, "tracker-store.journal");
- path = g_file_get_path (child);
- g_unlink (path);
- g_object_unref (child);
-
- tracker_db_journal_set_rotating (FALSE, G_MAXSIZE, NULL);
- writer = tracker_db_journal_new (data_location, FALSE, &error);
- g_object_unref (data_location);
- g_assert_no_error (error);
-
- /* Size is 8 due to header */
- actual_size = tracker_db_journal_get_size (writer);
- g_assert_cmpint (actual_size, ==, 8);
-
- /* Check with rollback, nothing is added */
- initial_size = tracker_db_journal_get_size (writer);
- result = tracker_db_journal_start_transaction (writer, time (NULL));
- g_assert_cmpint (result, ==, TRUE);
- result = tracker_db_journal_append_resource (writer, 10, "http://resource");
- g_assert_cmpint (result, ==, TRUE);
- result = tracker_db_journal_append_resource (writer, 11, "http://predicate");
- g_assert_cmpint (result, ==, TRUE);
- result = tracker_db_journal_append_delete_statement (writer, 0, 10, 11, "test");
- g_assert_cmpint (result, ==, TRUE);
- result = tracker_db_journal_rollback_transaction (writer);
- g_assert_cmpint (result, ==, TRUE);
- actual_size = tracker_db_journal_get_size (writer);
- g_assert_cmpint (initial_size, ==, actual_size);
-
- /* Check with commit, somethign is added */
- result = tracker_db_journal_start_transaction (writer, time (NULL));
- g_assert_cmpint (result, ==, TRUE);
- result = tracker_db_journal_append_resource (writer, 12, "http://resource");
- g_assert_cmpint (result, ==, TRUE);
- result = tracker_db_journal_append_resource (writer, 13, "http://predicate");
- g_assert_cmpint (result, ==, TRUE);
- result = tracker_db_journal_append_resource (writer, 14, "http://resource");
- g_assert_cmpint (result, ==, TRUE);
- result = tracker_db_journal_append_delete_statement_id (writer, 0, 12, 13, 14);
- g_assert_cmpint (result, ==, TRUE);
- result = tracker_db_journal_commit_db_transaction (writer, &error);
- g_assert_no_error (error);
- g_assert_cmpint (result, ==, TRUE);
- actual_size = tracker_db_journal_get_size (writer);
- g_assert_cmpint (initial_size, !=, actual_size);
-
- /* Test insert statement */
- result = tracker_db_journal_start_transaction (writer, time (NULL));
- g_assert_cmpint (result, ==, TRUE);
- result = tracker_db_journal_append_resource (writer, 15, "http://resource");
- g_assert_cmpint (result, ==, TRUE);
- result = tracker_db_journal_append_resource (writer, 16, "http://predicate");
- g_assert_cmpint (result, ==, TRUE);
- result = tracker_db_journal_append_insert_statement (writer, 0, 15, 16, "test");
- g_assert_cmpint (result, ==, TRUE);
- result = tracker_db_journal_commit_db_transaction (writer, &error);
- g_assert_no_error (error);
- g_assert_cmpint (result, ==, TRUE);
-
- /* Test insert id */
- result = tracker_db_journal_start_transaction (writer, time (NULL));
- g_assert_cmpint (result, ==, TRUE);
- result = tracker_db_journal_append_resource (writer, 17, "http://resource");
- g_assert_cmpint (result, ==, TRUE);
- result = tracker_db_journal_append_resource (writer, 18, "http://predicate");
- g_assert_cmpint (result, ==, TRUE);
- result = tracker_db_journal_append_resource (writer, 19, "http://resource");
- g_assert_cmpint (result, ==, TRUE);
- result = tracker_db_journal_append_insert_statement_id (writer, 0, 17, 18, 19);
- g_assert_cmpint (result, ==, TRUE);
- result = tracker_db_journal_commit_db_transaction (writer, &error);
- g_assert_no_error (error);
- g_assert_cmpint (result, ==, TRUE);
-
- /* Test fsync */
- result = tracker_db_journal_fsync (writer);
- g_assert_cmpint (result, ==, TRUE);
-
- tracker_db_journal_free (writer, &error);
- g_assert_no_error (error);
-
- g_free (path);
-}
-
-static void
-test_read_functions (void)
-{
- GError *error = NULL;
- gchar *path;
- gboolean result;
- TrackerDBJournalEntryType type;
- gint id, s_id, p_id, o_id;
- const gchar *uri, *str;
- GFile *data_location;
- TrackerDBJournalReader *reader;
-
- path = g_build_filename (TOP_BUILDDIR, "tests", "libtracker-db", NULL);
- data_location = g_file_new_for_path (path);
- g_free (path);
-
- /* NOTE: we don't unlink here so we can use the data from the write tests */
-
- /* Create an iterator */
- reader = tracker_db_journal_reader_new (data_location, &error);
- g_object_unref (data_location);
- g_assert_no_error (error);
- g_assert_nonnull (reader);
-
- type = tracker_db_journal_reader_get_entry_type (reader);
- g_assert_cmpint (type, ==, TRACKER_DB_JOURNAL_START);
-
- /* First transaction */
- result = tracker_db_journal_reader_next (reader, &error);
- g_assert_no_error (error);
- g_assert_cmpint (result, ==, TRUE);
-
- type = tracker_db_journal_reader_get_entry_type (reader);
- g_assert_cmpint (type, ==, TRACKER_DB_JOURNAL_START_TRANSACTION);
-
- result = tracker_db_journal_reader_next (reader, &error);
- g_assert_no_error (error);
- g_assert_cmpint (result, ==, TRUE);
-
- type = tracker_db_journal_reader_get_entry_type (reader);
- g_assert_cmpint (type, ==, TRACKER_DB_JOURNAL_RESOURCE);
-
- result = tracker_db_journal_reader_get_resource (reader, &id, &uri);
- g_assert_cmpint (result, ==, TRUE);
- g_assert_cmpint (id, ==, 12);
- g_assert_cmpstr (uri, ==, "http://resource");
-
- result = tracker_db_journal_reader_next (reader, &error);
- g_assert_no_error (error);
- g_assert_cmpint (result, ==, TRUE);
-
- type = tracker_db_journal_reader_get_entry_type (reader);
- g_assert_cmpint (type, ==, TRACKER_DB_JOURNAL_RESOURCE);
-
- result = tracker_db_journal_reader_get_resource (reader, &id, &uri);
- g_assert_cmpint (result, ==, TRUE);
- g_assert_cmpint (id, ==, 13);
- g_assert_cmpstr (uri, ==, "http://predicate");
-
- result = tracker_db_journal_reader_next (reader, &error);
- g_assert_no_error (error);
- g_assert_cmpint (result, ==, TRUE);
-
- type = tracker_db_journal_reader_get_entry_type (reader);
- g_assert_cmpint (type, ==, TRACKER_DB_JOURNAL_RESOURCE);
-
- result = tracker_db_journal_reader_get_resource (reader, &id, &uri);
- g_assert_cmpint (result, ==, TRUE);
- g_assert_cmpint (id, ==, 14);
- g_assert_cmpstr (uri, ==, "http://resource");
-
- result = tracker_db_journal_reader_next (reader, &error);
- g_assert_no_error (error);
- g_assert_cmpint (result, ==, TRUE);
-
- type = tracker_db_journal_reader_get_entry_type (reader);
- g_assert_cmpint (type, ==, TRACKER_DB_JOURNAL_DELETE_STATEMENT_ID);
-
- result = tracker_db_journal_reader_get_statement_id (reader, NULL, &s_id, &p_id, &o_id);
- g_assert_cmpint (result, ==, TRUE);
- g_assert_cmpint (s_id, ==, 12);
- g_assert_cmpint (p_id, ==, 13);
- g_assert_cmpint (o_id, ==, 14);
-
- result = tracker_db_journal_reader_next (reader, &error);
- g_assert_no_error (error);
- g_assert_cmpint (result, ==, TRUE);
-
- type = tracker_db_journal_reader_get_entry_type (reader);
- g_assert_cmpint (type, ==, TRACKER_DB_JOURNAL_END_TRANSACTION);
-
- /* Second transaction */
- result = tracker_db_journal_reader_next (reader, &error);
- g_assert_no_error (error);
- g_assert_cmpint (result, ==, TRUE);
-
- type = tracker_db_journal_reader_get_entry_type (reader);
- g_assert_cmpint (type, ==, TRACKER_DB_JOURNAL_START_TRANSACTION);
-
- result = tracker_db_journal_reader_next (reader, &error);
- g_assert_no_error (error);
- g_assert_cmpint (result, ==, TRUE);
-
- type = tracker_db_journal_reader_get_entry_type (reader);
- g_assert_cmpint (type, ==, TRACKER_DB_JOURNAL_RESOURCE);
-
- result = tracker_db_journal_reader_get_resource (reader, &id, &uri);
- g_assert_cmpint (result, ==, TRUE);
- g_assert_cmpint (id, ==, 15);
- g_assert_cmpstr (uri, ==, "http://resource");
-
- result = tracker_db_journal_reader_next (reader, &error);
- g_assert_no_error (error);
- g_assert_cmpint (result, ==, TRUE);
-
- type = tracker_db_journal_reader_get_entry_type (reader);
- g_assert_cmpint (type, ==, TRACKER_DB_JOURNAL_RESOURCE);
-
- result = tracker_db_journal_reader_get_resource (reader, &id, &uri);
- g_assert_cmpint (result, ==, TRUE);
- g_assert_cmpint (id, ==, 16);
- g_assert_cmpstr (uri, ==, "http://predicate");
-
- result = tracker_db_journal_reader_next (reader, &error);
- g_assert_no_error (error);
- g_assert_cmpint (result, ==, TRUE);
-
- type = tracker_db_journal_reader_get_entry_type (reader);
- g_assert_cmpint (type, ==, TRACKER_DB_JOURNAL_INSERT_STATEMENT);
-
- result = tracker_db_journal_reader_get_statement (reader, NULL, &s_id, &p_id, &str);
- g_assert_cmpint (result, ==, TRUE);
- g_assert_cmpint (s_id, ==, 15);
- g_assert_cmpint (p_id, ==, 16);
- g_assert_cmpstr (str, ==, "test");
-
- result = tracker_db_journal_reader_next (reader, &error);
- g_assert_no_error (error);
- g_assert_cmpint (result, ==, TRUE);
-
- type = tracker_db_journal_reader_get_entry_type (reader);
- g_assert_cmpint (type, ==, TRACKER_DB_JOURNAL_END_TRANSACTION);
-
- /* Third transaction */
- result = tracker_db_journal_reader_next (reader, &error);
- g_assert_no_error (error);
- g_assert_cmpint (result, ==, TRUE);
-
- type = tracker_db_journal_reader_get_entry_type (reader);
- g_assert_cmpint (type, ==, TRACKER_DB_JOURNAL_START_TRANSACTION);
-
- result = tracker_db_journal_reader_next (reader, &error);
- g_assert_no_error (error);
- g_assert_cmpint (result, ==, TRUE);
-
- type = tracker_db_journal_reader_get_entry_type (reader);
- g_assert_cmpint (type, ==, TRACKER_DB_JOURNAL_RESOURCE);
-
- result = tracker_db_journal_reader_get_resource (reader, &id, &uri);
- g_assert_cmpint (result, ==, TRUE);
- g_assert_cmpint (id, ==, 17);
- g_assert_cmpstr (uri, ==, "http://resource");
-
- result = tracker_db_journal_reader_next (reader, &error);
- g_assert_no_error (error);
- g_assert_cmpint (result, ==, TRUE);
-
- type = tracker_db_journal_reader_get_entry_type (reader);
- g_assert_cmpint (type, ==, TRACKER_DB_JOURNAL_RESOURCE);
-
- result = tracker_db_journal_reader_get_resource (reader, &id, &uri);
- g_assert_cmpint (result, ==, TRUE);
- g_assert_cmpint (id, ==, 18);
- g_assert_cmpstr (uri, ==, "http://predicate");
-
- result = tracker_db_journal_reader_next (reader, &error);
- g_assert_no_error (error);
- g_assert_cmpint (result, ==, TRUE);
-
- type = tracker_db_journal_reader_get_entry_type (reader);
- g_assert_cmpint (type, ==, TRACKER_DB_JOURNAL_RESOURCE);
-
- result = tracker_db_journal_reader_get_resource (reader, &id, &uri);
- g_assert_cmpint (result, ==, TRUE);
- g_assert_cmpint (id, ==, 19);
- g_assert_cmpstr (uri, ==, "http://resource");
-
- result = tracker_db_journal_reader_next (reader, &error);
- g_assert_no_error (error);
- g_assert_cmpint (result, ==, TRUE);
-
- type = tracker_db_journal_reader_get_entry_type (reader);
- g_assert_cmpint (type, ==, TRACKER_DB_JOURNAL_INSERT_STATEMENT_ID);
-
- result = tracker_db_journal_reader_get_statement_id (reader, NULL, &s_id, &p_id, &o_id);
- g_assert_cmpint (result, ==, TRUE);
- g_assert_cmpint (s_id, ==, 17);
- g_assert_cmpint (p_id, ==, 18);
- g_assert_cmpint (o_id, ==, 19);
-
- result = tracker_db_journal_reader_next (reader, &error);
- g_assert_no_error (error);
- g_assert_cmpint (result, ==, TRUE);
-
- type = tracker_db_journal_reader_get_entry_type (reader);
- g_assert_cmpint (type, ==, TRACKER_DB_JOURNAL_END_TRANSACTION);
-
- tracker_db_journal_reader_free (reader);
-}
-
-#endif /* DISABLE_JOURNAL */
-
-int
-main (int argc, char **argv)
-{
- gchar *path;
- int result;
-
- g_test_init (&argc, &argv, NULL);
-
-#ifndef DISABLE_JOURNAL
- /* None of these tests make sense in case of disabled journal */
- g_test_add_func ("/libtracker-db/tracker-db-journal/write-functions",
- test_write_functions);
- g_test_add_func ("/libtracker-db/tracker-db-journal/read-functions",
- test_read_functions);
- g_test_add_func ("/libtracker-db/tracker-db-journal/init-and-shutdown",
- test_init_and_shutdown);
-#endif /* DISABLE_JOURNAL */
-
- result = g_test_run ();
-
- /* Clean up */
- path = g_build_filename (TOP_BUILDDIR, "tests", "libtracker-db", "tracker-store.journal", NULL);
- g_unlink (path);
- g_free (path);
-
- return result;
-}
diff --git a/tests/libtracker-data/tracker-ontology-change-test.c b/tests/libtracker-data/tracker-ontology-change-test.c
index 190386f2a..85dc75063 100644
--- a/tests/libtracker-data/tracker-ontology-change-test.c
+++ b/tests/libtracker-data/tracker-ontology-change-test.c
@@ -212,7 +212,7 @@ test_ontology_change (void)
g_assert_cmpint (g_chmod (ontology_file, 0666), ==, 0);
manager = tracker_data_manager_new (0, data_location, data_location, test_schemas,
- FALSE, FALSE, 100, 100);
+ FALSE, 100, 100);
g_initable_init (G_INITABLE (manager), NULL, &error);
g_assert_no_error (error);
@@ -254,7 +254,7 @@ test_ontology_change (void)
}
manager = tracker_data_manager_new (0, data_location, data_location, test_schemas,
- TRUE, FALSE, 100, 100);
+ FALSE, 100, 100);
g_initable_init (G_INITABLE (manager), NULL, &error);
g_assert_no_error (error);
diff --git a/tests/libtracker-data/tracker-ontology-test.c b/tests/libtracker-data/tracker-ontology-test.c
index 6b1c47d1c..11a0e5eaa 100644
--- a/tests/libtracker-data/tracker-ontology-test.c
+++ b/tests/libtracker-data/tracker-ontology-test.c
@@ -183,22 +183,18 @@ test_ontology_init (TestInfo *test_info,
data_location = g_file_new_for_path (test_info->data_location);
- tracker_db_journal_set_rotating (FALSE, G_MAXSIZE, NULL);
-
/* first-time initialization */
manager = tracker_data_manager_new (TRACKER_DB_MANAGER_FORCE_REINDEX,
data_location, data_location, data_location,
- FALSE, FALSE, 100, 100);
+ FALSE, 100, 100);
g_initable_init (G_INITABLE (manager), NULL, &error);
g_assert_no_error (error);
g_object_unref (manager);
- tracker_db_journal_set_rotating (FALSE, G_MAXSIZE, NULL);
-
/* initialization from existing database */
manager = tracker_data_manager_new (0, data_location, data_location, data_location,
- FALSE, FALSE, 100, 100);
+ FALSE, 100, 100);
g_initable_init (G_INITABLE (manager), NULL, &error);
g_assert_no_error (error);
@@ -230,12 +226,10 @@ test_query (TestInfo *test_info,
ontology_location = g_file_new_for_path (ontology_path);
g_free (ontology_path);
- tracker_db_journal_set_rotating (FALSE, G_MAXSIZE, NULL);
-
/* initialization */
manager = tracker_data_manager_new (TRACKER_DB_MANAGER_FORCE_REINDEX,
data_location, data_location, ontology_location,
- FALSE, FALSE, 100, 100);
+ FALSE, 100, 100);
g_initable_init (G_INITABLE (manager), NULL, &error);
g_assert_no_error (error);
@@ -245,7 +239,7 @@ test_query (TestInfo *test_info,
data_filename = g_strconcat (data_prefix, ".ttl", NULL);
file = g_file_new_for_path (data_filename);
data_update = tracker_data_manager_get_data (manager);
- tracker_turtle_reader_load (file, data_update, &error);
+ tracker_data_load_turtle_file (data_update, file, NULL, &error);
g_assert_no_error (error);
g_object_unref (file);
diff --git a/tests/libtracker-data/tracker-sparql-blank-test.c b/tests/libtracker-data/tracker-sparql-blank-test.c
index fa4af4c30..96f798b8c 100644
--- a/tests/libtracker-data/tracker-sparql-blank-test.c
+++ b/tests/libtracker-data/tracker-sparql-blank-test.c
@@ -52,12 +52,10 @@ test_blank (TestInfo *info,
data_location = g_file_new_for_path (info->data_location);
- tracker_db_journal_set_rotating (FALSE, G_MAXSIZE, NULL);
-
/* initialization */
manager = tracker_data_manager_new (TRACKER_DB_MANAGER_FORCE_REINDEX,
data_location, data_location, data_location, /* loc, domain and ontology_name */
- FALSE, FALSE, 100, 100);
+ FALSE, 100, 100);
g_initable_init (G_INITABLE (manager), NULL, &error);
g_assert_no_error (error);
diff --git a/tests/libtracker-data/tracker-sparql-test.c b/tests/libtracker-data/tracker-sparql-test.c
index 2a10d4399..0e3f57fb7 100644
--- a/tests/libtracker-data/tracker-sparql-test.c
+++ b/tests/libtracker-data/tracker-sparql-test.c
@@ -45,6 +45,8 @@ const TestInfo tests[] = {
{ "aggregates/aggregate-1", "aggregates/data-1", FALSE },
{ "aggregates/aggregate-distinct-1", "aggregates/data-1", FALSE },
{ "aggregates/aggregate-group-1", "aggregates/data-1", FALSE },
+ { "aggregates/aggregate-group-2", "aggregates/data-1", FALSE },
+ { "aggregates/aggregate-group-as-1", "aggregates/data-1", FALSE },
{ "algebra/two-nested-opt", "algebra/two-nested-opt", FALSE },
{ "algebra/two-nested-opt-alt", "algebra/two-nested-opt", FALSE },
{ "algebra/opt-filter-3", "algebra/opt-filter-3", FALSE },
@@ -67,6 +69,7 @@ const TestInfo tests[] = {
{ "anon/query-4", "anon/data", FALSE },
{ "anon/query-5", "anon/data", FALSE },
{ "ask/ask-1", "ask/data", FALSE },
+ { "basic/base-1", "basic/data-1", FALSE },
{ "basic/base-prefix-3", "basic/data-1", FALSE },
{ "basic/compare-cast", "basic/data-1", FALSE },
{ "basic/predicate-variable", "basic/data-1", FALSE },
@@ -82,10 +85,20 @@ const TestInfo tests[] = {
{ "bnode/query-5", "bnode/data", FALSE },
{ "bnode-coreference/query", "bnode-coreference/data", FALSE },
{ "bound/bound1", "bound/data", FALSE },
+ { "construct/construct-where", "construct/data", FALSE },
+ { "construct/construct-pattern", "construct/data", FALSE },
+ { "construct/construct-with-modifiers", "construct/data", FALSE },
{ "datetime/delete-1", "datetime/data-3", FALSE },
{ "datetime/functions-localtime-1", "datetime/data-1", FALSE },
{ "datetime/functions-timezone-1", "datetime/data-2", FALSE },
{ "datetime/functions-timezone-2", "datetime/data-2", FALSE },
+ { "datetime/functions-timezone-3", "datetime/data-2", FALSE },
+ { "datetime/functions-tz-1", "datetime/data-2", FALSE },
+ { "describe/describe-single", "describe/data", FALSE },
+ { "describe/describe-non-existent", "describe/data", FALSE },
+ { "describe/describe-pattern", "describe/data", FALSE },
+ { "describe/describe-limit", "describe/data", FALSE },
+ { "describe/describe-multiple", "describe/data", FALSE },
{ "expr-ops/query-ge-1", "expr-ops/data", FALSE },
{ "expr-ops/query-le-1", "expr-ops/data", FALSE },
{ "expr-ops/query-minus-1", "expr-ops/data", FALSE },
@@ -113,11 +126,58 @@ const TestInfo tests[] = {
{ "functions/functions-xpath-13", "functions/data-4", FALSE },
{ "functions/functions-xpath-14", "functions/data-4", FALSE },
{ "functions/functions-coalesce-1", "functions/data-1", FALSE },
+ { "functions/functions-datatypes-1", "functions/data-1", FALSE },
+ { "functions/functions-datatypes-2", "functions/data-2", FALSE },
+ { "functions/functions-datatypes-3", "functions/data-3", FALSE },
+ { "functions/functions-datatypes-4", "functions/data-4", FALSE },
+ /* Graph semantics and operations */
{ "graph/graph-1", "graph/data-1", FALSE },
{ "graph/graph-2", "graph/data-2", FALSE },
{ "graph/graph-3", "graph/data-3", FALSE },
{ "graph/graph-4", "graph/data-3", FALSE },
{ "graph/graph-5", "graph/data-4", FALSE },
+ { "graph/graph-6", "graph/data-5", FALSE },
+ { "graph/non-existent-1", "graph/data-1", FALSE },
+ { "graph/drop", "graph/data-drop", FALSE },
+ { "graph/drop-non-existent", "graph/data-drop-non-existent", FALSE, TRUE },
+ { "graph/drop-default", "graph/data-drop-default", FALSE },
+ { "graph/drop-named", "graph/data-drop-named", FALSE },
+ { "graph/drop-all", "graph/data-drop-all", FALSE },
+ { "graph/drop-silent", "graph/data-drop-silent", FALSE },
+ { "graph/clear", "graph/data-clear", FALSE },
+ { "graph/clear-non-existent", "graph/data-clear-non-existent", FALSE, TRUE },
+ { "graph/clear-default", "graph/data-clear-default", FALSE },
+ { "graph/clear-named", "graph/data-clear-named", FALSE },
+ { "graph/clear-all", "graph/data-clear-all", FALSE },
+ { "graph/copy", "graph/data-copy", FALSE },
+ { "graph/copy-to-existent", "graph/data-copy-to-existent", FALSE },
+ { "graph/copy-to-non-existent", "graph/data-copy-to-non-existent", FALSE },
+ { "graph/copy-from-non-existent", "graph/data-copy-from-non-existent", FALSE },
+ { "graph/copy-into-self", "graph/data-copy-into-self", FALSE },
+ { "graph/copy-from-default", "graph/data-copy-from-default", FALSE },
+ { "graph/copy-to-default", "graph/data-copy-to-default", FALSE },
+ { "graph/move", "graph/data-move", FALSE },
+ { "graph/move-to-existent", "graph/data-move-to-existent", FALSE },
+ { "graph/move-from-non-existent", "graph/data-move-from-non-existent", FALSE },
+ { "graph/move-into-self", "graph/data-move-into-self", FALSE },
+ { "graph/move-from-default", "graph/data-move-from-default", FALSE },
+ { "graph/move-to-default", "graph/data-move-to-default", FALSE },
+ { "graph/add", "graph/data-add", FALSE },
+ { "graph/add-to-existent", "graph/data-add-to-existent", FALSE },
+ { "graph/add-to-non-existent", "graph/data-add-to-non-existent", FALSE },
+ { "graph/add-from-non-existent", "graph/data-add-from-non-existent", FALSE },
+ { "graph/add-into-self", "graph/data-add-into-self", FALSE },
+ { "graph/add-from-default", "graph/data-add-from-default", FALSE },
+ { "graph/add-to-default", "graph/data-add-to-default", FALSE },
+ { "langstring/match-with-non-langstring", "langstring/data", FALSE },
+ { "langstring/match-with-langstring", "langstring/data", FALSE },
+ { "langstring/match-non-langstring", "langstring/data", FALSE },
+ { "langstring/langmatches", "langstring/data", FALSE },
+ { "langstring/strlang", "langstring/data", FALSE },
+ { "lists/list-in-object", "lists/data-list-in-object", FALSE },
+ { "lists/list-in-subject", "lists/data-list-in-subject", FALSE },
+ { "lists/list-in-select", "lists/data-list-in-select", FALSE },
+ { "lists/list-nested", "lists/data-list-nested", FALSE },
{ "optional/q-opt-complex-1", "optional/complex-data-1", FALSE },
{ "optional/simple-optional-triple", "optional/simple-optional-triple", FALSE },
{ "regex/regex-query-001", "regex/regex-data-01", FALSE },
@@ -219,21 +279,39 @@ check_result (TrackerDBCursor *cursor,
gint col;
while (tracker_db_cursor_iter_next (cursor, NULL, &error)) {
+ GString *row_str = g_string_new (NULL);
+
for (col = 0; col < tracker_db_cursor_get_n_columns (cursor); col++) {
const gchar *str;
if (col > 0) {
- g_string_append (test_results, "\t");
+ g_string_append (row_str, "\t");
}
str = tracker_db_cursor_get_string (cursor, col, NULL);
+
+ /* Hack to avoid misc properties that might tamper with
+ * test reproduceability in DESCRIBE and other unrestricted
+ * queries.
+ */
+ if (g_strcmp0 (str, TRACKER_PREFIX_TRACKER "modified") == 0 ||
+ g_strcmp0 (str, TRACKER_PREFIX_TRACKER "added") == 0) {
+ g_string_free (row_str, TRUE);
+ row_str = NULL;
+ break;
+ }
+
if (str != NULL) {
/* bound variable */
- g_string_append_printf (test_results, "\"%s\"", str);
+ g_string_append_printf (row_str, "\"%s\"", str);
}
}
- g_string_append (test_results, "\n");
+ if (row_str) {
+ g_string_append (test_results, row_str->str);
+ g_string_free (row_str, TRUE);
+ g_string_append (test_results, "\n");
+ }
}
} else if (test_info->expect_query_error) {
g_assert (error != NULL && error->domain == TRACKER_SPARQL_ERROR);
@@ -298,11 +376,9 @@ test_sparql_query (TestInfo *test_info,
data_location = g_file_new_for_path (test_info->data_location);
- tracker_db_journal_set_rotating (FALSE, G_MAXSIZE, NULL);
-
manager = tracker_data_manager_new (TRACKER_DB_MANAGER_FORCE_REINDEX,
data_location, data_location, test_schemas, /* loc, domain and ontology_name */
- FALSE, FALSE, 100, 100);
+ FALSE, 100, 100);
g_initable_init (G_INITABLE (manager), NULL, &error);
g_assert_no_error (error);
@@ -314,7 +390,7 @@ test_sparql_query (TestInfo *test_info,
data_filename = g_strconcat (data_prefix, ".ttl", NULL);
if (g_file_test (data_filename, G_FILE_TEST_IS_REGULAR)) {
GFile *file = g_file_new_for_path (data_filename);
- tracker_turtle_reader_load (file, data_update, &error);
+ tracker_data_load_turtle_file (data_update, file, NULL, &error);
g_assert_no_error (error);
g_object_unref (file);
} else {
@@ -421,6 +497,8 @@ main (int argc, char **argv)
setlocale (LC_COLLATE, "en_US.utf8");
+ tracker_log_init (0, NULL);
+
current_dir = g_get_current_dir ();
tests_data_dir = g_build_filename (current_dir, "sparql-test-data-XXXXXX", NULL);
g_free (current_dir);
diff --git a/tests/libtracker-fts/meson.build b/tests/libtracker-fts/meson.build
index 7de35649b..536a227a7 100644
--- a/tests/libtracker-fts/meson.build
+++ b/tests/libtracker-fts/meson.build
@@ -7,5 +7,6 @@ fts_test = executable('tracker-fts-test',
tests += {
'name': 'fts',
'exe': fts_test,
+ 'suite': ['fts'],
'requires_dbus': true,
}
diff --git a/tests/libtracker-miner/meson.build b/tests/libtracker-miner/meson.build
index c441223ca..d01937469 100644
--- a/tests/libtracker-miner/meson.build
+++ b/tests/libtracker-miner/meson.build
@@ -27,7 +27,6 @@ libtracker_miner_test_deps = [tracker_common_dep, tracker_miner_dep, tracker_spa
foreach base_name: libtracker_miner_tests
source = 'tracker-@0@-test.c'.format(base_name)
binary_name = 'tracker-@0@-test'.format(base_name)
- test_name = 'miner-@0@'.format(base_name)
binary = executable(binary_name, source,
dependencies: libtracker_miner_test_deps,
@@ -35,15 +34,15 @@ foreach base_name: libtracker_miner_tests
link_with: [libtracker_miner_private])
tests += {
- 'name': test_name,
- 'exe': binary
+ 'name': base_name,
+ 'exe': binary,
+ 'suite': ['miner'],
}
endforeach
foreach base_name: libtracker_miner_slow_tests
source = 'tracker-@0@-test.c'.format(base_name)
binary_name = 'tracker-@0@-test'.format(base_name)
- test_name = 'miner-@0@'.format(base_name)
binary = executable(binary_name, source,
dependencies: libtracker_miner_test_deps,
@@ -51,8 +50,9 @@ foreach base_name: libtracker_miner_slow_tests
link_with: [libtracker_miner_private])
tests += {
- 'name': test_name,
+ 'name': base_name,
'exe': binary,
+ 'suite': ['miner', 'slow'],
'timeout': 180
}
endforeach
diff --git a/tests/libtracker-miner/tracker-miner-fs-test.c b/tests/libtracker-miner/tracker-miner-fs-test.c
index c57579b27..39a48f959 100644
--- a/tests/libtracker-miner/tracker-miner-fs-test.c
+++ b/tests/libtracker-miner/tracker-miner-fs-test.c
@@ -61,7 +61,7 @@ test_miner_process_file (TrackerMinerFS *miner,
tracker_resource_add_uri (resource, "rdf:type", "nfo:FileDataObject");
g_file_info_get_modification_time (info, &timeval);
- str = tracker_date_to_string (timeval.tv_sec);
+ str = tracker_date_to_string (timeval.tv_sec, 0);
tracker_resource_set_string (resource, "nfo:fileLastModified", str);
g_free (str);
@@ -78,7 +78,7 @@ test_miner_process_file (TrackerMinerFS *miner,
g_free (urn);
}
- sparql = tracker_resource_print_sparql_update (resource, NULL, NULL);
+ sparql = tracker_resource_print_sparql_update (resource, NULL, TRACKER_OWN_GRAPH_URN);
tracker_miner_fs_notify_finish (miner, task, sparql, NULL);
g_object_unref (resource);
g_free (sparql);
@@ -94,7 +94,8 @@ test_miner_remove_file (TrackerMinerFS *miner,
gchar *sparql, *uri;
uri = g_file_get_uri (file);
- sparql = g_strdup_printf ("DELETE {"
+ sparql = g_strdup_printf ("WITH <" TRACKER_OWN_GRAPH_URN "> "
+ "DELETE {"
" ?u a rdfs:Resource . "
"} WHERE {"
" ?u nie:url ?url ."
@@ -112,7 +113,8 @@ test_miner_remove_children (TrackerMinerFS *miner,
gchar *sparql, *uri;
uri = g_file_get_uri (file);
- sparql = g_strdup_printf ("DELETE {"
+ sparql = g_strdup_printf ("WITH <" TRACKER_OWN_GRAPH_URN "> "
+ "DELETE {"
" ?u a rdfs:Resource . "
"} WHERE {"
" ?u nie:url ?url ."
@@ -133,7 +135,8 @@ test_miner_move_file (TrackerMinerFS *miner,
uri = g_file_get_uri (source);
dest_uri = g_file_get_uri (dest);
- sparql = g_strdup_printf ("DELETE {"
+ sparql = g_strdup_printf ("WITH <" TRACKER_OWN_GRAPH_URN "> "
+ "DELETE {"
" ?u nie:url ?url . "
"} INSERT {"
" ?u nie:url '%s' . "
diff --git a/tests/libtracker-miner/tracker-monitor-test.c b/tests/libtracker-miner/tracker-monitor-test.c
index 26c2c1d26..26a56cbef 100644
--- a/tests/libtracker-miner/tracker-monitor-test.c
+++ b/tests/libtracker-miner/tracker-monitor-test.c
@@ -1442,6 +1442,8 @@ main (gint argc,
{
g_test_init (&argc, &argv, NULL);
+ tracker_log_init (0, NULL);
+
g_test_message ("Testing filesystem monitor");
/* Basic API tests */
diff --git a/tests/libtracker-sparql/meson.build b/tests/libtracker-sparql/meson.build
index 7dc9e9813..1eef9387f 100644
--- a/tests/libtracker-sparql/meson.build
+++ b/tests/libtracker-sparql/meson.build
@@ -14,8 +14,9 @@ tracker_resource_test = executable('tracker-resource-test',
c_args: libtracker_sparql_test_c_args)
tests += {
- 'name': 'tracker-resource-test',
+ 'name': 'resource',
'exe': tracker_resource_test,
+ 'suite': ['resource'],
}
tracker_sparql_test = executable('tracker-sparql-test',
@@ -24,8 +25,9 @@ tracker_sparql_test = executable('tracker-sparql-test',
c_args: libtracker_sparql_test_c_args)
tests += {
- 'name': 'tracker-sparql-test',
+ 'name': 'sparql',
'exe': tracker_sparql_test,
+ 'suite': ['sparql'],
'is_parallel': false,
'requires_dbus': true,
}
diff --git a/tests/libtracker-sparql/tracker-sparql-test.c b/tests/libtracker-sparql/tracker-sparql-test.c
index 3b60d18be..541031b26 100644
--- a/tests/libtracker-sparql/tracker-sparql-test.c
+++ b/tests/libtracker-sparql/tracker-sparql-test.c
@@ -24,6 +24,7 @@
#include <glib-object.h>
#include <libtracker-sparql/tracker-sparql.h>
+#include <libtracker-sparql/tracker-version.h>
typedef struct {
const gchar *input ;
@@ -354,6 +355,14 @@ test_tracker_sparql_connection_interleaved (void)
g_object_unref(cursor1);
}
+static void
+test_tracker_check_version (void)
+{
+ g_assert_true(tracker_check_version(TRACKER_MAJOR_VERSION,
+ TRACKER_MINOR_VERSION,
+ TRACKER_MICRO_VERSION) == NULL);
+}
+
gint
main (gint argc, gchar **argv)
{
@@ -385,6 +394,8 @@ main (gint argc, gchar **argv)
test_tracker_sparql_connection_locking_async);
g_test_add_func ("/libtracker-sparql/tracker-sparql/tracker_sparql_cursor_next_async",
test_tracker_sparql_cursor_next_async);
+ g_test_add_func ("/libtracker-sparql/tracker-sparql/tracker_check_version",
+ test_tracker_check_version);
result = g_test_run ();
diff --git a/tests/meson.build b/tests/meson.build
index 2e19b0623..c7467781a 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -28,6 +28,7 @@ test_bus_conf_file = configure_file(
foreach t: tests
test_name = t.get('name')
test_exe = t.get('exe')
+ test_suite = t.get('suite', [])
test_timeout = t.get('timeout', 30)
# This flag marks unit tests which can't run alongside other unit tests. It
@@ -47,17 +48,19 @@ foreach t: tests
test_env.set('LANG', 'en_US.UTF8')
if test_requires_dbus
- test (test_name, dbus_run_session,
+ test(test_name, dbus_run_session,
env: test_env,
args: ['--config-file=@0@'.format(join_paths(meson.current_build_dir(), 'test-bus.conf')),
'--',
test_exe],
+ suite: test_suite,
timeout: test_timeout,
is_parallel: test_is_parallel)
else
- test (test_name, test_exe,
- env: test_env,
- timeout: test_timeout,
- is_parallel: test_is_parallel)
+ test(test_name, test_exe,
+ env: test_env,
+ timeout: test_timeout,
+ suite: test_suite,
+ is_parallel: test_is_parallel)
endif
endforeach
diff --git a/tests/services/meson.build b/tests/services/meson.build
index 182ba1e74..0223867e9 100644
--- a/tests/services/meson.build
+++ b/tests/services/meson.build
@@ -2,3 +2,5 @@ test_dbus_service_file = configure_file(
input: 'org.freedesktop.Tracker1.service.in',
output: 'org.freedesktop.Tracker1.service',
configuration: conf)
+
+tracker_test_dbus_services_dir = meson.current_build_dir()
diff --git a/tests/test-bus.conf.in b/tests/test-bus.conf.in
index 5b4f51ff9..f2df619e3 100644
--- a/tests/test-bus.conf.in
+++ b/tests/test-bus.conf.in
@@ -7,6 +7,13 @@
<listen>unix:tmpdir=./</listen>
<servicedir>@abs_top_builddir@/tests/services/</servicedir>
+ <standard_session_servicedirs/>
+
+ <!-- This timeout is annoying when the service is being debugged. We rely on
+ the test harness to stop the test and kill the daemon if it does
+ get stuck.
+ -->
+ <limit name="service_start_timeout">1000000</limit>
<policy context="default">
<!-- Allow everything to be sent -->
@@ -16,5 +23,4 @@
<!-- Allow anyone to own anything -->
<allow own="*"/>
</policy>
-
</busconfig>
diff --git a/tests/tracker-steroids/meson.build b/tests/tracker-steroids/meson.build
index a9dff2d11..aac7e1941 100644
--- a/tests/tracker-steroids/meson.build
+++ b/tests/tracker-steroids/meson.build
@@ -10,5 +10,6 @@ steroids_test = executable('tracker-steroids-test',
tests += {
'name': 'steroids',
'exe': steroids_test,
+ 'suite': ['steroids'],
'requires_dbus': true,
}
diff --git a/utils/sandbox/tracker-sandbox.py b/utils/sandbox/tracker-sandbox.py
deleted file mode 100755
index 06bc6afb0..000000000
--- a/utils/sandbox/tracker-sandbox.py
+++ /dev/null
@@ -1,601 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2012-2013 Martyn Russell <martyn@lanedo.com>
-# Copyright (C) 2012 Sam Thursfield <sam.thursfield@codethink.co.uk>
-# Copyright (C) 2016 Sam Thursfield <sam@afuera.me.uk>
-#
-# This script allows a user to utilise Tracker for local instances by
-# specifying an index directory location where the Tracker data is
-# stored and a content directory location where the content to be
-# indexed is kept. From there, queries or a shell can be launched to
-# use that data.
-#
-# This was initially a shell script by Sam and later converted into a
-# more comprehensive python script by Martyn.
-#
-# Usage:
-# - Create or update an index stored in tracker/ subdir with content in html/
-# tracker-sandbox.py -i tracker -c html -u
-# - Query for 'foo'
-# tracker-sandbox.py -i tracker -c html -q foo
-# - List files in index
-# tracker-sandbox.py -i tracker -c html -l
-# - Start shell with environment set up
-# tracker-sandbox.py -i tracker -c html -s
-# - Test with different prefixes, e.g. /usr/local installs
-# tracker-sandbox.py -i tracker -c html -s -p /usr/local
-# ...
-#
-# Changes:
-# - If you make _ANY_ changes, please send them in so I can incorporate them.
-#
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-#
-
-import locale
-import os
-import subprocess
-import optparse
-import signal
-import sys
-import errno
-
-import configparser
-
-import gi
-gi.require_version('Tracker', '2.0')
-from gi.repository import Tracker, GLib
-
-# Script
-script_name = 'tracker-sandbox'
-script_version = '0.1'
-script_about = 'Localised Tracker sandbox for content indexing and search'
-
-index_location_abs = ''
-
-default_prefix = '/usr'
-default_debug_verbosity = 2
-
-# Session
-dbus_session_pid = -1
-dbus_session_address = ''
-dbus_session_file = ''
-
-store_pid = -1
-store_proc = None
-
-original_xdg_data_home = GLib.get_user_data_dir()
-
-# Template config file
-config_template = """
-[General]
-verbosity=0
-sched-idle=0
-initial-sleep=0
-
-[Monitors]
-enable-monitors=false
-
-[Indexing]
-throttle=0
-index-on-battery=true
-index-on-battery-first-time=true
-index-removable-media=false
-index-optical-discs=false
-low-disk-space-limit=-1
-index-recursive-directories=;
-index-single-directories=;
-ignored-directories=;
-ignored-directories-with-content=;
-ignored-files=
-crawling-interval=-1
-removable-days-threshold=3
-
-[Writeback]
-enable-writeback=false
-"""
-
-# Utilities
-
-
-def mkdir_p(path):
- try:
- os.makedirs(path)
- except OSError as exc:
- if exc.errno == errno.EEXIST:
- pass
- else:
- raise
-
-
-def debug(message):
- if opts.debug:
- print(message)
-
-# DB functions (sync for now)
-
-
-def db_query_have_files():
- # Set this here in case we used 'bus' for an update() before this.
- # os.environ['TRACKER_SPARQL_BACKEND'] = 'direct'
-
- print('Using query to check index has data in it...')
-
- conn = Tracker.SparqlConnection.get(None)
- cursor = conn.query(
- 'select count(?urn) where { ?urn a nfo:FileDataObject }', None)
-
- # Only expect one result here...
- while (cursor.next(None)):
- print(' Currently %d file(s) exist in our index' %
- (cursor.get_integer(0)))
-
-
-def db_query_list_files():
- # Set this here in case we used 'bus' for an update() before this.
- # os.environ['TRACKER_SPARQL_BACKEND'] = 'direct'
-
- print('Using query to list files indexed...')
-
- conn = Tracker.SparqlConnection.get(None)
- cursor = conn.query(
- 'select nie:url(?urn) where { ?urn a nfo:FileDataObject }', None)
-
- # Only expect one result here...
- while (cursor.next(None)):
- print(' ' + cursor.get_string(0)[0])
-
-
-def db_search(search_text):
- conn = Tracker.SparqlConnection.get(None)
- query = ('select nie:url(?urn) where { ?urn a nfo:FileDataObject . '
- '?urn fts:match "%s" }')
- cursor = conn.query(query % (search_text), None)
-
- print('Found:')
-
- while (cursor.next(None)):
- print(' ' + cursor.get_string(0)[0])
-
-
-def db_sparql_query(sparql):
- conn = Tracker.SparqlConnection.get(None)
- cursor = conn.query(sparql)
-
- print('Results:')
-
- while (cursor.next(None)):
- row = []
- for column in range(0, cursor.get_n_columns()):
- row.append(cursor.get_string(column)[0])
- print(' ' + '\t'.join(row))
-
-
-# Index functions
-
-def index_clean():
- # tracker reset --hard
- debug('Cleaning index, FIXME: Does nothing.')
-
-
-def find_libexec_binaries(command):
- binary = os.path.join(opts.prefix, 'libexec', command)
- if not os.path.exists(binary):
- binary = os.path.join(opts.prefix, 'libexec', command)
- if not os.path.exists(binary):
- return None
-
- return binary
-
-
-def index_update():
- debug('Updating index ...')
- debug('--')
-
- # FIXME: Need to start tracker-extract to make sure extended
- # metadata is created, but the problem is, after miner-fs
- # stops, we return to the prompt, so how do we handle that?
- #
- # We need to listen to signals from tracker-extract and then
- # quit after some inactivity I think ... OR listen to
- # GraphUpdated and when there are no more objects without a
- # data-source, we know all data was indexed.
-
- # Start tracker-miner-fs
- binary = find_libexec_binaries('tracker-miner-fs')
- if binary is None:
- print('Could not find "tracker-miner-fs" in $prefix/lib{exec} '
- 'directories', file=sys.stderr)
- print('Is Tracker installed properly?', file=sys.stderr)
- sys.exit(1)
-
- try:
- # Mine data WITHOUT being a daemon, exit when done. Ignore desktop
- # files
- subprocess.check_output([binary, "--no-daemon"]).decode('utf-8')
- except subprocess.CalledProcessError as e:
- print('Could not run %s, %s' % (binary, e.output))
- sys.exit(1)
-
- debug('--')
-
- # We've now finished updating the index now OR we completely failed
- print('Index now up to date!')
-
- # Check we have data in our index...
- db_query_have_files()
-
-
-def index_shell():
- print('Starting shell... (type "exit" to finish)')
- print()
-
- os.system("/bin/bash")
-
-# Environment / Clean up
-
-
-def dbus_session_get_from_content(content):
- global dbus_session_address
- global dbus_session_pid
-
- if len(content) < 1:
- print('Content was empty ... can not get DBus session information from'
- ' empty string', file=sys.stderr)
- return False
-
- dbus_session_address = content.splitlines()[0]
- dbus_session_pid = int(content.splitlines()[1])
-
- err_msg = 'DBus session file was corrupt (%s), please remove "%s"'
- if dbus_session_address == '':
- print(err_msg % ("no address", dbus_session_file), file=sys.stderr)
- sys.exit(1)
- if dbus_session_pid < 0:
- print(err_msg % ("no PID", dbus_session_file), file=sys.stderr)
- sys.exit(1)
-
- return True
-
-
-def dbus_session_file_get():
- try:
- with open(dbus_session_file, 'r') as f:
- content = f.read()
- return dbus_session_get_from_content(content)
- except FileNotFoundError as e:
- # Expect this if we have a new session to set up
- return False
-
-
-def dbus_session_file_set():
- mkdir_p(os.environ['XDG_RUNTIME_DIR'])
-
- content = '%s\n%s' % (dbus_session_address, dbus_session_pid)
- with open(dbus_session_file, 'w') as f:
- f.write(content)
-
-
-def environment_unset():
- debug('Cleaning up files ...')
-
- if not dbus_session_file == '':
- debug(' Removing DBus session file')
- os.unlink(dbus_session_file)
-
- debug('Cleaning up processes ...')
-
- if dbus_session_pid > 0:
- debug(' Killing DBus session')
- try:
- os.kill(dbus_session_pid, signal.SIGTERM)
- # (3, 'No such process') old python-schedutils incorrectly
- # raised SystemError
- except (SystemError, OSError):
- debug(' Process %d not found', dbus_session_pid)
-
- if not opts.update:
- return
-
- # FIXME: clean up tracker-store, can't use 'tracker daemon ...' for this,
- # that kills everything it finds in /proc sadly.
- if store_pid > 0:
- debug(' Killing Tracker store')
- os.kill(store_pid, signal.SIGTERM)
-
-
-def environment_set_and_add_path(env, prefix, suffix):
- new = os.path.join(prefix, suffix)
-
- if env in os.environ:
- existing = os.environ[env]
- full = '%s:%s' % (new, existing)
- else:
- full = new
-
- os.environ[env] = full
-
-
-def environment_set():
- # Environment
- global dbus_session_address
- global dbus_session_pid
- global dbus_session_file
- global index_location_abs
- global default_debug_verbosity
-
- index_location_abs = os.path.abspath(opts.index_location)
-
- # Data
- os.environ['XDG_DATA_HOME'] = '%s/data/' % index_location_abs
- os.environ['XDG_CONFIG_HOME'] = '%s/config/' % index_location_abs
- os.environ['XDG_CACHE_HOME'] = '%s/cache/' % index_location_abs
- os.environ['XDG_RUNTIME_DIR'] = '%s/run/' % index_location_abs
-
- # Prefix - only set if non-standard
- if opts.prefix != default_prefix:
- environment_set_and_add_path('PATH', opts.prefix, 'bin')
- environment_set_and_add_path('LD_LIBRARY_PATH', opts.prefix, 'lib')
- environment_set_and_add_path('XDG_DATA_DIRS', opts.prefix, 'share')
-
- # Preferences
- os.environ['TRACKER_USE_CONFIG_FILES'] = 'yes'
-
- # if opts.debug:
- # os.environ['TRACKER_USE_LOG_FILES'] = 'yes'
-
- if opts.debug:
- os.environ['G_MESSAGES_DEBUG'] = 'all'
- os.environ['TRACKER_VERBOSITY'] = '%d' % default_debug_verbosity
- os.environ['DBUS_VERBOSE'] = '1'
- else:
- os.environ['TRACKER_VERBOSITY'] = '0'
-
- debug('Using prefix location "%s"' % opts.prefix)
- debug('Using index location "%s"' % index_location_abs)
-
- # Ensure directory exists
- # DBus specific instance
- dbus_session_file = os.path.join(
- os.environ['XDG_RUNTIME_DIR'], 'dbus-session')
-
- if dbus_session_file_get() is False:
- output = subprocess.check_output(["dbus-daemon",
- "--session",
- "--print-address=1",
- "--print-pid=1",
- "--fork"]).decode('utf-8')
-
- dbus_session_get_from_content(output)
- dbus_session_file_set()
- debug('Using new D-Bus session with address "%s" with PID %d' %
- (dbus_session_address, dbus_session_pid))
- else:
- debug('Using existing D-Bus session from file "%s" with address "%s"'
- ' with PID %d' %
- (dbus_session_file, dbus_session_address, dbus_session_pid))
-
- # Important, other subprocesses must use our new bus
- os.environ['DBUS_SESSION_BUS_ADDRESS'] = dbus_session_address
-
-
-def config_set():
- # Make sure File System miner is configured correctly
- config_dir = os.path.join(os.environ['XDG_CONFIG_HOME'], 'tracker')
- config_filename = os.path.join(config_dir, 'tracker-miner-fs.cfg')
-
- debug('Using config file "%s"' % config_filename)
-
- # Only update config if we're updating the database
- mkdir_p(config_dir)
-
- if not os.path.exists(config_filename):
- f = open(config_filename, 'w')
- f.write(config_template)
- f.close()
-
- debug(' Miner config file written')
-
- # Set content path
- config = configparser.ConfigParser()
- config.optionxform = str
- config.read(config_filename)
-
- if opts.content_locations_recursive:
- debug("Using content locations: %s" %
- opts.content_locations_recursive)
- if opts.content_locations_single:
- debug("Using non-recursive content locations: %s" %
- opts.content_locations_single)
-
- def locations_gsetting(locations):
- locations = [dir if dir.startswith('&') else os.path.abspath(dir)
- for dir in locations]
- return GLib.Variant('as', locations).print_(False)
-
- if not config.has_section('General'):
- config.add_section('General')
-
- config.set('General', 'index-recursive-directories',
- locations_gsetting(opts.content_locations_recursive or []))
- config.set('General', 'index-single-directories',
- locations_gsetting(opts.content_locations_single or []))
-
- with open(config_filename, 'w') as f:
- config.write(f)
-
-
-def link_to_mime_data():
- '''Create symlink to $XDG_DATA_HOME/mime in our custom data home dir.
-
- Mimetype detection seems to break horribly if the $XDG_DATA_HOME/mime
- directory is missing. Since we have to override the normal XDG_DATA_HOME
- path, we need to work around this by linking back to the real mime data.
-
- '''
- new_xdg_data_home = os.environ['XDG_DATA_HOME']
- old_mime_dir = os.path.join(original_xdg_data_home, 'mime')
- if os.path.exists(old_mime_dir):
- new_mime_dir = os.path.join(new_xdg_data_home, 'mime')
- if (not os.path.exists(new_mime_dir)
- and not os.path.islink(new_mime_dir)):
- mkdir_p(new_xdg_data_home)
- os.symlink(
- os.path.join(original_xdg_data_home, 'mime'), new_mime_dir)
-
-
-# Entry point/start
-if __name__ == "__main__":
- locale.setlocale(locale.LC_ALL, '')
-
- # Parse command line
- usage_oneline = '%s -i <DIR> -c <DIR> [OPTION...]' % (
- os.path.basename(sys.argv[0]))
- usage = '\n %s - %s' % (usage_oneline, script_about)
- usage_invalid = 'Usage:\n %s' % (usage_oneline)
-
- popt = optparse.OptionParser(usage)
- popt.add_option('-v', '--version',
- action='count',
- dest='version',
- help='show version information')
- popt.add_option('-d', '--debug',
- action='count',
- dest='debug',
- help='show additional debugging')
- popt.add_option('-p', '--prefix',
- action='store',
- metavar='PATH',
- dest='prefix',
- default=default_prefix,
- help='use a non-standard prefix (default="%s")' %
- (default_prefix))
- popt.add_option('-i', '--index',
- action='store',
- metavar='DIR',
- dest='index_location',
- help='directory storing the index')
- popt.add_option('-c', '--content',
- action='append',
- metavar='DIR',
- dest='content_locations_recursive',
- help='directory storing the content which is indexed (can '
- 'be specified multiple times)')
- popt.add_option('-C', '--content-non-recursive',
- action='append',
- metavar='DIR',
- dest='content_locations_single',
- help='directory storing the content which is indexed, '
- 'non-recursive variant (can be specified multiple times)')
- popt.add_option('-u', '--update',
- action='count',
- dest='update',
- help='update index/database from content')
- popt.add_option('-l', '--list-files',
- action='count',
- dest='list_files',
- help='list files indexed')
- popt.add_option('-s', '--shell',
- action='count',
- dest='shell',
- help='start a shell with the environment set up')
- popt.add_option('--search',
- action='store',
- metavar='CRITERIA',
- dest='search',
- help='what content to look for in files')
- popt.add_option('-q', '--sparql',
- action='store',
- metavar='CRITERIA',
- dest='sparql_query',
- help='SPARQL query to execute')
-
- (opts, args) = popt.parse_args()
-
- if opts.version:
- print('%s %s\n%s\n' % (script_name, script_version, script_about))
- sys.exit(0)
-
- if not opts.index_location:
- if not opts.content_locations_recursive and not \
- opts.content_locations_single:
- print('Expected index (-i) or content (-c) locations to be '
- 'specified', file=sys.stderr)
- print(usage_invalid)
- sys.exit(1)
-
- if opts.update:
- if not opts.index_location or not (opts.content_locations_recursive or
- opts.content_locations_single):
- print('Expected index (-i) and content (-c) locations to be '
- 'specified', file=sys.stderr)
- print('These arguments are required to update the index databases',
- file=sys.stderr)
- sys.exit(1)
-
- if ((opts.sparql_query or opts.search or opts.list_files or opts.shell)
- and not opts.index_location):
- print('Expected index location (-i) to be specified', file=sys.stderr)
- print('This arguments is required to use the content that has been '
- 'indexed', file=sys.stderr)
- sys.exit(1)
-
- if (not opts.update
- and not opts.sparql_query
- and not opts.search
- and not opts.list_files
- and not opts.shell):
- print('No action specified (e.g. update (-u), shell (-s), '
- 'list files (-l), etc)\n', file=sys.stderr)
- print('%s %s\n%s\n' % (script_name, script_version, script_about),
- file=sys.stderr)
- print(usage_invalid, file=sys.stderr)
- sys.exit(1)
-
- # Set up environment variables and foo needed to get started.
- environment_set()
- config_set()
-
- link_to_mime_data()
-
- try:
- if opts.update:
- index_update()
-
- if opts.list_files:
- db_query_list_files()
-
- if opts.shell:
- index_shell()
- sys.exit(0)
-
- if opts.search or opts.sparql_query:
- if not os.path.exists(index_location_abs):
- print('Can not query yet, index has not been created, see '
- '--update or -u', file=sys.stderr)
- print(usage_invalid, file=sys.stderr)
- sys.exit(1)
-
- if opts.search:
- db_search(opts.search)
-
- if opts.sparql_query:
- db_sparql_query(opts.sparql_query)
-
- except KeyboardInterrupt:
- print('Handling Ctrl+C')
-
- environment_unset()
diff --git a/utils/trackertestutils/__main__.py b/utils/trackertestutils/__main__.py
new file mode 100644
index 000000000..9828e102f
--- /dev/null
+++ b/utils/trackertestutils/__main__.py
@@ -0,0 +1,479 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2012-2013 Martyn Russell <martyn@lanedo.com>
+# Copyright (C) 2012 Sam Thursfield <sam.thursfield@codethink.co.uk>
+# Copyright (C) 2016,2019 Sam Thursfield <sam@afuera.me.uk>
+#
+# This is a tool for running development versions of Tracker.
+#
+# See README.md for usage information.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+import argparse
+import collections
+import configparser
+import contextlib
+import locale
+import logging
+import os
+import shlex
+import shutil
+import signal
+import subprocess
+import sys
+import tempfile
+import time
+
+from gi.repository import Gio
+from gi.repository import GLib
+
+from . import dbusdaemon
+from . import helpers
+from . import mainloop
+
+# Script
+script_name = 'tracker-sandbox'
+script_about = "Tracker Sandbox developer tool."
+
+default_index_location = '/tmp/tracker-sandbox'
+
+store_pid = -1
+store_proc = None
+
+original_xdg_data_home = GLib.get_user_data_dir()
+
+# Template config file
+config_template = """
+[General]
+verbosity=0
+sched-idle=0
+initial-sleep=0
+
+[Monitors]
+enable-monitors=false
+
+[Indexing]
+throttle=0
+index-on-battery=true
+index-on-battery-first-time=true
+index-removable-media=false
+index-optical-discs=false
+low-disk-space-limit=-1
+index-recursive-directories=;
+index-single-directories=;
+ignored-directories=;
+ignored-directories-with-content=;
+ignored-files=
+crawling-interval=-1
+removable-days-threshold=3
+
+[Writeback]
+enable-writeback=false
+"""
+
+
+log = logging.getLogger('sandbox')
+
+
+# Environment / Clean up
+
+def environment_unset(dbus):
+ log.debug('Cleaning up processes ...')
+
+ dbus.stop()
+
+ # FIXME: clean up tracker-store, can't use 'tracker daemon ...' for this,
+ # that kills everything it finds in /proc sadly.
+ if store_pid > 0:
+ log.debug(' Killing Tracker store')
+ os.kill(store_pid, signal.SIGTERM)
+
+
+def environment_set_and_add_path(env, var, prefix, suffix):
+ new = os.path.join(prefix, suffix)
+
+ if var in os.environ:
+ existing = os.environ[var]
+ full = '%s:%s' % (new, existing)
+ else:
+ full = new
+
+ env[var] = full
+
+
+def create_sandbox(index_location, prefix=None, verbosity=0, dbus_config=None,
+ interactive=False):
+ assert prefix is None or dbus_config is None
+
+ extra_env = {}
+
+ # Data
+ extra_env['XDG_DATA_HOME'] = '%s/data/' % index_location
+ extra_env['XDG_CONFIG_HOME'] = '%s/config/' % index_location
+ extra_env['XDG_CACHE_HOME'] = '%s/cache/' % index_location
+ extra_env['XDG_RUNTIME_DIR'] = '%s/run/' % index_location
+
+ # Prefix - only set if non-standard
+ if prefix and prefix != '/usr':
+ environment_set_and_add_path(extra_env, 'PATH', prefix, 'bin')
+ environment_set_and_add_path(extra_env, 'LD_LIBRARY_PATH', prefix, 'lib')
+ environment_set_and_add_path(extra_env, 'XDG_DATA_DIRS', prefix, 'share')
+
+ # Preferences
+ extra_env['TRACKER_USE_CONFIG_FILES'] = 'yes'
+
+ extra_env['G_MESSAGES_PREFIXED'] = 'all'
+
+ extra_env['TRACKER_VERBOSITY'] = str(verbosity)
+
+ log.debug('Using prefix location "%s"' % prefix)
+ log.debug('Using index location "%s"' % index_location)
+
+ sandbox = helpers.TrackerDBusSandbox(dbus_config, extra_env=extra_env)
+ sandbox.start(new_session=(interactive == True))
+
+ # Update our own environment, so when we launch a subprocess it has the
+ # same settings as the Tracker daemons.
+ os.environ.update(extra_env)
+ os.environ['DBUS_SESSION_BUS_ADDRESS'] = sandbox.daemon.get_address()
+ os.environ['TRACKER_SANDBOX'] = '1'
+
+ return sandbox
+
+
+def config_set(content_locations_recursive=None, content_locations_single=None):
+ # Make sure File System miner is configured correctly
+ config_dir = os.path.join(os.environ['XDG_CONFIG_HOME'], 'tracker')
+ config_filename = os.path.join(config_dir, 'tracker-miner-fs.cfg')
+
+ log.debug('Using config file "%s"' % config_filename)
+
+ # Only update config if we're updating the database
+ os.makedirs(config_dir, exist_ok=True)
+
+ if not os.path.exists(config_filename):
+ f = open(config_filename, 'w')
+ f.write(config_template)
+ f.close()
+
+ log.debug(' Miner config file written')
+
+ # Set content path
+ config = configparser.ConfigParser()
+ config.optionxform = str
+ config.read(config_filename)
+
+ if content_locations_recursive:
+ log.debug("Using content locations: %s" %
+ content_locations_recursive)
+ if content_locations_single:
+ log.debug("Using non-recursive content locations: %s" %
+ content_locations_single)
+
+ def locations_gsetting(locations):
+ locations = [dir if dir.startswith('&') else os.path.abspath(dir)
+ for dir in locations]
+ return GLib.Variant('as', locations).print_(False)
+
+ if not config.has_section('General'):
+ config.add_section('General')
+
+ config.set('General', 'index-recursive-directories',
+ locations_gsetting(content_locations_recursive or []))
+ config.set('General', 'index-single-directories',
+ locations_gsetting(content_locations_single or []))
+
+ with open(config_filename, 'w') as f:
+ config.write(f)
+
+
+def link_to_mime_data():
+ '''Create symlink to $XDG_DATA_HOME/mime in our custom data home dir.
+
+ Mimetype detection seems to break horribly if the $XDG_DATA_HOME/mime
+ directory is missing. Since we have to override the normal XDG_DATA_HOME
+ path, we need to work around this by linking back to the real mime data.
+
+ '''
+ new_xdg_data_home = os.environ['XDG_DATA_HOME']
+ old_mime_dir = os.path.join(original_xdg_data_home, 'mime')
+ if os.path.exists(old_mime_dir):
+ new_mime_dir = os.path.join(new_xdg_data_home, 'mime')
+ if (not os.path.exists(new_mime_dir)
+ and not os.path.islink(new_mime_dir)):
+ os.makedirs(new_xdg_data_home, exist_ok=True)
+ os.symlink(
+ os.path.join(original_xdg_data_home, 'mime'), new_mime_dir)
+
+
+def argument_parser():
+ class expand_path(argparse.Action):
+ """Expand user- and relative-paths in filenames."""
+ # From https://gist.github.com/brantfaircloth/1443543
+ def __call__(self, parser, namespace, values, option_string=None):
+ setattr(namespace, self.dest, os.path.abspath(os.path.expanduser(values)))
+
+ parser = argparse.ArgumentParser(description=script_about)
+ parser.add_argument('--dbus-config', metavar='FILE', action=expand_path,
+ help="use a custom D-Bus config file to locate the "
+ "Tracker daemons. This can be used to run Tracker "
+ "from a build tree of tracker-miners.git, by "
+ "using the generated file ./tests/test-bus.conf")
+ parser.add_argument('-p', '--prefix', metavar='DIR', action=expand_path,
+ help="run Tracker from the given install prefix. You "
+ "can run the system version of Tracker by "
+ "specifying --prefix=/usr")
+ parser.add_argument('-v', '--verbosity', default=None,
+ choices=['0', '1', '2', '3', 'errors', 'minimal', 'detailed', 'debug'],
+ help="show debugging info from Tracker processes")
+ parser.add_argument('-i', '--index', metavar='DIR', action=expand_path,
+ default=default_index_location, dest='index_location',
+ help=f"directory to the index (default={default_index_location})")
+ parser.add_argument('--index-tmpdir', action='store_true',
+ help="create index in a temporary directory and "
+ "delete it on exit (useful for automated testing)")
+ parser.add_argument('--wait-for-miner', type=str, action='append',
+ help="wait for one or more daemons to start, and "
+ "return to idle for at least 1 second, before "
+ "exiting. Usually used with `tracker index` where "
+ "you should pass --wait-for-miner=Files and "
+ "--wait-for-miner=Extract")
+ parser.add_argument('--debug-dbus', action='store_true',
+ help="show stdout and stderr messages from every daemon "
+ "running on the sandbox session bus. By default we "
+ "only show messages logged by Tracker daemons.")
+ parser.add_argument('--debug-sandbox', action='store_true',
+ help="show debugging info from tracker-sandbox")
+ parser.add_argument('command', type=str, nargs='*', help="Command to run inside the shell")
+
+ return parser
+
+
+def verbosity_as_int(verbosity):
+ verbosity_map = {
+ 'errors': 0,
+ 'minimal': 1,
+ 'detailed': 2,
+ 'debug': 3
+ }
+ return verbosity_map.get(verbosity, int(verbosity))
+
+
+def init_logging(debug_sandbox, debug_dbus):
+ SANDBOX_FORMAT = "%(name)s: %(message)s"
+ DBUS_FORMAT = "%(message)s"
+
+ if debug_sandbox:
+ sandbox_log_handler = logging.StreamHandler()
+ sandbox_log_handler.setFormatter(logging.Formatter(SANDBOX_FORMAT))
+
+ root = logging.getLogger()
+ root.setLevel(logging.DEBUG)
+ root.addHandler(sandbox_log_handler)
+ else:
+ dbus_stderr = logging.getLogger('trackertestutils.dbusdaemon.stderr')
+ dbus_stdout = logging.getLogger('trackertestutils.dbusdaemon.stdout')
+
+ dbus_handler = logging.StreamHandler(stream=sys.stderr)
+ dbus_handler.setFormatter(logging.Formatter(DBUS_FORMAT))
+
+ if debug_dbus:
+ dbus_stderr.setLevel(logging.DEBUG)
+ dbus_stdout.setLevel(logging.DEBUG)
+ else:
+ dbus_stderr.setLevel(logging.INFO)
+ dbus_stdout.setLevel(logging.INFO)
+
+ dbus_stderr.addHandler(dbus_handler)
+ dbus_stdout.addHandler(dbus_handler)
+
+
+class MinerStatusWatch():
+ """This class provides a way to block waiting for miners to finish.
+
+ This is needed because of a deficiency in `tracker index`, see:
+ https://gitlab.gnome.org/GNOME/tracker/issues/122
+
+ """
+ def __init__(self, sandbox, miner_name):
+ self.dbus_name = 'org.freedesktop.Tracker1.Miner.' + miner_name
+ self.object_path = '/org/freedesktop/Tracker1/Miner/' + miner_name
+
+ self._sandbox = sandbox
+
+ # Stores a list of (time, status) pairs. This is used to determine
+ # if the miner has been idle continuously over a time peroid.
+ self._status_log = collections.deque()
+
+ def _log_status(self, time, status):
+ self._status_log.append((time, status))
+ if len(self._status_log) > 100:
+ self._status_log.popleft()
+
+ def setup(self):
+ log.debug(f"Set up status watch on {self.dbus_name}")
+ self._proxy = Gio.DBusProxy.new_sync(
+ self._sandbox.get_connection(),
+ Gio.DBusProxyFlags.NONE, None,
+ self.dbus_name, self.object_path, 'org.freedesktop.Tracker1.Miner',
+ None)
+
+ # FIXME: this doesn't appear to work, so we have to use polling.
+ #proxy.connect('g-signal', miner_signal_cb)
+
+ # This call will raise GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown
+ # if the miner name is invalid.
+ status = self._proxy.GetStatus()
+ self._log_status(time.time(), status)
+ log.debug(f"{self.dbus_name}: Current status: {status}")
+
+ def check_was_idle_for_time_period(self, period_seconds):
+ now = time.time()
+
+ status = self._proxy.GetStatus()
+ self._log_status(now, status)
+ log.debug(f"{self.dbus_name}: Current status: {status}")
+
+ cursor = len(self._status_log) - 1
+ previous_delta_from_now = 0
+ while True:
+ if cursor < 0 or self._status_log[cursor][1] != 'Idle':
+ if previous_delta_from_now >= period_seconds:
+ return True
+ else:
+ return False
+ previous_delta_from_now = (now - self._status_log[cursor][0])
+ cursor -= 1
+
+
+def wait_for_miners(watches):
+ # We wait 1 second after "Idle" status is seen before exiting, because the
+ # extractor goes to/from Idle frequently.
+ wait_for_idle_time = 1
+ while True:
+ status = [watch.check_was_idle_for_time_period(wait_for_idle_time) for watch in watches.values()]
+ if all(status):
+ break
+ else:
+ log.debug(f"Waiting for idle.")
+ time.sleep(0.1)
+
+
+@contextlib.contextmanager
+def ignore_sigint():
+ handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
+ yield
+ signal.signal(signal.SIGINT, handler)
+
+
+def main():
+ locale.setlocale(locale.LC_ALL, '')
+
+ parser = argument_parser()
+ args = parser.parse_args()
+
+ init_logging(args.debug_sandbox, args.debug_dbus)
+
+ shell = os.environ.get('SHELL', '/bin/bash')
+
+ if args.prefix is None and args.dbus_config is None:
+ parser.print_help()
+ print("\nYou must specify either --dbus-config (to run Tracker from "
+ "a build tree) or --prefix (to run an installed Tracker).")
+ sys.exit(1)
+
+ if args.prefix is not None and args.dbus_config is not None:
+ raise RuntimeError(
+ "You cannot specify --dbus-config and --prefix at the same time. "
+ "Note that running Tracker from the build tree implies "
+ "--dbus-config.")
+
+ if args.verbosity is None:
+ verbosity = verbosity_as_int(os.environ.get('TRACKER_VERBOSITY', 0))
+ else:
+ verbosity = verbosity_as_int(args.verbosity)
+ if 'TRACKER_VERBOSITY' in os.environ:
+ if verbosity != int(os.environ['TRACKER_VERBOSITY']):
+ raise RuntimeError("Incompatible values for TRACKER_VERBOSITY "
+ "from environment and from --verbosity "
+ "parameter.")
+
+ if args.command is None and args.wait_for_miner is not None:
+ raise RuntimeError("--wait-for-miner cannot be used when opening an "
+ "interactive shell.")
+
+ index_location = None
+ index_tmpdir = None
+
+ if args.index_location != default_index_location and args.index_tmpdir:
+ raise RuntimeError("The --index-tmpdir flag is enabled, but --index= was also passed.")
+ if args.index_tmpdir:
+ index_location = index_tmpdir = tempfile.mkdtemp(prefix='tracker-sandbox')
+ else:
+ index_location = args.index_location
+
+ interactive = not (args.command)
+
+ # Set up environment variables and foo needed to get started.
+ sandbox = create_sandbox(index_location, args.prefix, verbosity,
+ dbus_config=args.dbus_config,
+ interactive=interactive)
+ config_set()
+
+ link_to_mime_data()
+
+ miner_watches = {}
+ for miner in (args.wait_for_miner or []):
+ watch = MinerStatusWatch(sandbox, miner)
+ watch.setup()
+ miner_watches[miner] = watch
+
+ try:
+ if interactive:
+ if args.dbus_config:
+ print(f"Using Tracker daemons from build tree with D-Bus config {args.dbus_config}")
+ else:
+ print(f"Using Tracker daemons from prefix {args.prefix}")
+ print("Starting interactive Tracker sandbox shell... (type 'exit' to finish)")
+ print()
+
+ with ignore_sigint():
+ subprocess.run(shell)
+ else:
+ command = [shell, '-c', ' '.join(shlex.quote(c) for c in args.command)]
+
+ log.debug("Running: %s", command)
+ result = subprocess.run(command)
+
+ if len(miner_watches) > 0:
+ wait_for_miners(miner_watches)
+
+ log.debug("Process finished with returncode %i", result.returncode)
+ sys.exit(result.returncode)
+ finally:
+ sandbox.stop()
+ if index_tmpdir:
+ shutil.rmtree(index_tmpdir, ignore_errors=True)
+
+
+# Entry point/start
+if __name__ == "__main__":
+ try:
+ main()
+ except RuntimeError as e:
+ sys.stderr.write(f"ERROR: {e}\n")
+ sys.exit(1)
diff --git a/utils/trackertestutils/dbusdaemon.py b/utils/trackertestutils/dbusdaemon.py
new file mode 100644
index 000000000..abd8c6b02
--- /dev/null
+++ b/utils/trackertestutils/dbusdaemon.py
@@ -0,0 +1,182 @@
+# Copyright (C) 2018,2019, Sam Thursfield <sam@afuera.me.uk>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+
+from gi.repository import Gio
+from gi.repository import GLib
+
+import logging
+import os
+import shutil
+import signal
+import subprocess
+import threading
+
+log = logging.getLogger(__name__)
+dbus_stderr_log = logging.getLogger(__name__ + '.stderr')
+dbus_stdout_log = logging.getLogger(__name__ + '.stdout')
+
+
+class DaemonNotStartedError(Exception):
+ pass
+
+
+class DBusDaemon:
+ """The private D-Bus instance that provides the sandbox's session bus."""
+
+ def __init__(self):
+ self.process = None
+
+ self.address = None
+ self.pid = None
+
+ self._gdbus_connection = None
+ self._previous_sigterm_handler = None
+
+ self._threads = []
+
+ def get_address(self):
+ if self.address is None:
+ raise DaemonNotStartedError()
+ return self.address
+
+ def get_connection(self):
+ if self._gdbus_connection is None:
+ raise DaemonNotStartedError()
+ return self._gdbus_connection
+
+ def _dbus_daemon_path(self):
+ dbus_daemon = shutil.which('dbus-daemon')
+
+ if dbus_daemon is None:
+ raise RuntimeError("Could not find `dbus-daemon` binary in PATH (%s)." % os.environ.get('PATH'))
+
+ return dbus_daemon
+
+ def start(self, config_file=None, env=None, new_session=False):
+ dbus_command = [self._dbus_daemon_path(), '--print-address=1', '--print-pid=1']
+ if config_file:
+ dbus_command += ['--config-file=' + config_file]
+ else:
+ dbus_command += ['--session']
+ log.debug("Running: %s", dbus_command)
+ self.process = subprocess.Popen(
+ dbus_command, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ start_new_session=new_session)
+
+ self._previous_sigterm_handler = signal.signal(
+ signal.SIGTERM, self._sigterm_handler)
+
+ try:
+ self.address = self.process.stdout.readline().strip().decode('ascii')
+ self.pid = int(self.process.stdout.readline().strip().decode('ascii'))
+ except ValueError:
+ error = self.process.stderr.read().strip().decode('unicode-escape')
+ raise RuntimeError(f"Failed to start D-Bus daemon.\n{error}")
+
+ log.debug("Using new D-Bus session with address '%s' with PID %d",
+ self.address, self.pid)
+
+ # We must read from the pipes continuously, otherwise the daemon
+ # process will block.
+ self._threads=[threading.Thread(target=self.pipe_to_log, args=(self.process.stdout, dbus_stdout_log), daemon=True),
+ threading.Thread(target=self.pipe_to_log, args=(self.process.stderr, dbus_stdout_log), daemon=True)]
+ self._threads[0].start()
+ self._threads[1].start()
+
+ self._gdbus_connection = Gio.DBusConnection.new_for_address_sync(
+ self.address,
+ Gio.DBusConnectionFlags.AUTHENTICATION_CLIENT |
+ Gio.DBusConnectionFlags.MESSAGE_BUS_CONNECTION, None, None)
+
+ log.debug("Pinging the new D-Bus daemon...")
+ self.ping_sync()
+
+ def stop(self):
+ if self.process:
+ log.debug(" Stopping DBus daemon")
+ self.process.terminate()
+ self.process.wait()
+ self.process = None
+ if len(self._threads) > 0:
+ log.debug(" Stopping %i pipe reader threads", len(self._threads))
+ for thread in self._threads:
+ thread.join()
+ self.threads = []
+ if self._previous_sigterm_handler:
+ signal.signal(signal.SIGTERM, self._previous_sigterm_handler)
+ self._previous_sigterm_handler = None
+ log.debug("DBus daemon stopped")
+
+ def pipe_to_log(self, pipe, dbuslog):
+ """This function processes the output from our dbus-daemon instance."""
+ while True:
+ line_raw = pipe.readline()
+
+ if len(line_raw) == 0:
+ break
+
+ line = line_raw.decode('utf-8').rstrip()
+
+ if line.startswith('(tracker-'):
+ # We set G_MESSAGES_PREFIXED=all, meaning that all log messages
+ # output by Tracker processes have a prefix. Note that
+ # g_print() will NOT be captured here.
+ dbuslog.info(line)
+ else:
+ # Log messages from other daemons, including the dbus-daemon
+ # itself, go here. Any g_print() messages also end up here.
+ dbuslog.debug(line)
+ log.debug("Thread stopped")
+
+ # I'm not sure why this is needed, or if it's correct, but without it
+ # we see warnings like this:
+ #
+ # ResourceWarning: unclosed file <_io.BufferedReader name=3>
+ pipe.close()
+
+ def _sigterm_handler(self, signal, frame):
+ log.info("Received signal %s", signal)
+ self.stop()
+
+ def ping_sync(self):
+ """Call the daemon Ping() method to check that it is alive."""
+ self._gdbus_connection.call_sync(
+ 'org.freedesktop.DBus', '/', 'org.freedesktop.DBus', 'GetId',
+ None, None, Gio.DBusCallFlags.NONE, 10000, None)
+
+ def list_names_sync(self):
+ """Get the name of every client connected to the bus."""
+ conn = self.get_connection()
+ result = conn.call_sync('org.freedesktop.DBus',
+ '/org/freedesktop/DBus',
+ 'org.freedesktop.DBus', 'ListNames', None,
+ GLib.VariantType('(as)'),
+ Gio.DBusCallFlags.NONE, -1, None)
+ return result[0]
+
+ def get_connection_unix_process_id_sync(self, name):
+ """Get the process ID for one of the names connected to the bus."""
+ conn = self.get_connection()
+ result = conn.call_sync('org.freedesktop.DBus',
+ '/org/freedesktop/DBus',
+ 'org.freedesktop.DBus',
+ 'GetConnectionUnixProcessID',
+ GLib.Variant('(s)', [name]),
+ GLib.VariantType('(u)'),
+ Gio.DBusCallFlags.NONE, -1, None)
+ return result[0]
diff --git a/utils/trackertestutils/dconf.py b/utils/trackertestutils/dconf.py
index 4ad0e88e9..fe6d981fb 100644
--- a/utils/trackertestutils/dconf.py
+++ b/utils/trackertestutils/dconf.py
@@ -18,11 +18,9 @@
# 02110-1301, USA.
#
-from gi.repository import GLib
-from gi.repository import Gio
-
import logging
import os
+import subprocess
log = logging.getLogger(__name__)
@@ -36,28 +34,23 @@ class DConfClient(object):
this reason, and the constructor will fail if this isn't the profile in
use, to avoid any risk of modifying or removing your real configuration.
- The constructor will fail if DConf is not the default backend, because this
- probably indicates that the memory backend is in use. Without DConf the
- required configuration changes will not take effect, causing many tests to
- break.
+ We use the `gsettings` binary rather than using the Gio.Settings API.
+ This is to avoid the need to set DCONF_PROFILE in our own process
+ environment.
"""
- def __init__(self, schema):
- self._settings = Gio.Settings.new(schema)
-
- backend = self._settings.get_property('backend')
- self._check_settings_backend_is_dconf(backend)
- self._check_using_correct_dconf_profile()
-
- def _check_settings_backend_is_dconf(self, backend):
- typename = type(backend).__name__.split('.')[-1]
- if typename != 'DConfSettingsBackend':
- raise Exception(
- "The functional tests require DConf to be the default "
- "GSettings backend. Got %s instead." % typename)
+ def __init__(self, sandbox):
+ self.env = os.environ
+ self.env.update(sandbox.extra_env)
+ self.env['DBUS_SESSION_BUS_ADDRESS'] = sandbox.daemon.get_address()
def _check_using_correct_dconf_profile(self):
- profile = os.environ["DCONF_PROFILE"]
+ profile = self.env.get("DCONF_PROFILE")
+ if not profile:
+ raise Exception(
+ "DCONF_PROFILE is not set in the environment. This class must "
+ "be created inside a TrackerDBussandbox to avoid risk of "
+ "interfering with real settings.")
if not os.path.exists(profile):
raise Exception(
"Unable to find DConf profile '%s'. Check that Tracker and "
@@ -66,35 +59,11 @@ class DConfClient(object):
assert os.path.basename(profile) == "trackertest"
- def write(self, key, value):
+ def write(self, schema, key, value):
"""
Write a settings value.
"""
- self._settings.set_value(key, value)
-
- def read(self, schema, key):
- """
- Read a settings value.
- """
- return self._settings.get_value(key)
-
- def reset(self):
- """
- Remove all stored values, resetting configuration to the default.
-
- This can be done by removing the entire 'trackertest' configuration
- database.
- """
-
- self._check_using_correct_dconf_profile()
-
- # XDG_CONFIG_HOME is useless, so we use HOME. This code should not be
- # needed unless for some reason the test is not being run via the
- # 'test-runner.sh' script.
- dconf_db = os.path.join(os.environ["HOME"],
- ".config",
- "dconf",
- "trackertest")
- if os.path.exists(dconf_db):
- log.debug("[Conf] Removing dconf database: %s", dconf_db)
- os.remove(dconf_db)
+ subprocess.run(['gsettings', 'set', schema, key, value.print_(False)],
+ env=self.env,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
diff --git a/utils/trackertestutils/helpers.py b/utils/trackertestutils/helpers.py
index 2b218e5d0..07b176b9a 100644
--- a/utils/trackertestutils/helpers.py
+++ b/utils/trackertestutils/helpers.py
@@ -24,9 +24,12 @@ from gi.repository import GLib
import atexit
import logging
import os
-import subprocess
+import signal
+from . import dbusdaemon
from . import mainloop
+from . import psutil_mini as psutil
+
log = logging.getLogger(__name__)
@@ -54,171 +57,9 @@ def _cleanup_processes():
atexit.register(_cleanup_processes)
-class Helper:
+class StoreHelper():
"""
- Abstract helper for Tracker processes. Launches the process
- and waits for it to appear on the session bus.
-
- The helper will fail if the process is already running. Use
- test-runner.sh to ensure the processes run inside a separate DBus
- session bus.
-
- The process is watched using a timed GLib main loop source. If the process
- exits with an error code, the test will abort the next time the main loop
- is entered (or straight away if currently running the main loop).
- """
-
- STARTUP_TIMEOUT = 200 # milliseconds
- SHUTDOWN_TIMEOUT = 200 #
-
- def __init__(self, helper_name, bus_name, process_path):
- self.name = helper_name
- self.bus_name = bus_name
- self.process_path = process_path
-
- self.log = logging.getLogger(f'{__name__}.{self.name}')
-
- self.process = None
- self.available = False
-
- self.loop = mainloop.MainLoop()
-
- self.bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
-
- def _start_process(self, command_args=None, extra_env=None):
- global _process_list
- _process_list.append(self)
-
- command = [self.process_path] + (command_args or [])
- self.log.debug("Starting %s.", ' '.join(command))
-
- env = os.environ
- if extra_env:
- self.log.debug(" starting with extra environment: %s", extra_env)
- env.update(extra_env)
-
- try:
- return subprocess.Popen(command, env=env)
- except OSError as e:
- raise RuntimeError("Error starting %s: %s" % (self.process_path, e))
-
- def _bus_name_appeared(self, connection, name, owner):
- self.log.debug("%s appeared on the message bus, owned by %s", name, owner)
- self.available = True
- self.loop.quit()
-
- def _bus_name_vanished(self, connection, name):
- self.log.debug("%s vanished from the message bus", name)
- self.available = False
- self.loop.quit()
-
- def _process_watch_cb(self):
- if self.process_watch_timeout == 0:
- # GLib seems to call the timeout after we've removed it
- # sometimes, which causes errors unless we detect it.
- return False
-
- status = self.process.poll()
-
- if status is None:
- return True # continue
- elif status == 0 and not self.abort_if_process_exits_with_status_0:
- return True # continue
- else:
- self.process_watch_timeout = 0
- raise RuntimeError(f"{self.name} exited with status: {self.status}")
-
- def _process_startup_timeout_cb(self):
- self.log.debug(f"Process timeout of {self.STARTUP_TIMEOUT}ms was called")
- self.loop.quit()
- self.timeout_id = None
- return False
-
- def start(self, command_args=None, extra_env=None):
- """
- Start an instance of process and wait for it to appear on the bus.
- """
- if self.process is not None:
- raise RuntimeError("%s: already started" % self.name)
-
- self._bus_name_watch_id = Gio.bus_watch_name_on_connection(
- self.bus, self.bus_name, Gio.BusNameWatcherFlags.NONE,
- self._bus_name_appeared, self._bus_name_vanished)
-
- # We expect the _bus_name_vanished callback to be called here,
- # causing the loop to exit again.
- self.loop.run_checked()
-
- if self.available:
- # It's running, but we didn't start it...
- raise RuntimeError("Unable to start test instance of %s: "
- "already running" % self.name)
-
- self.process = self._start_process(command_args=command_args,
- extra_env=extra_env)
- self.log.debug('Started with PID %i', self.process.pid)
-
- self.process_startup_timeout = GLib.timeout_add(
- self.STARTUP_TIMEOUT, self._process_startup_timeout_cb)
-
- self.abort_if_process_exits_with_status_0 = True
-
- # Run the loop until the bus name appears, or the process dies.
- self.loop.run_checked()
-
- self.abort_if_process_exits_with_status_0 = False
-
- def stop(self):
- global _process_list
-
- if self.process is None:
- # Seems that it didn't even start...
- return
-
- if self.process.poll() == None:
- GLib.source_remove(self.process_startup_timeout)
- self.process_startup_timeout = 0
-
- self.process.terminate()
- returncode = self.process.wait(timeout=self.SHUTDOWN_TIMEOUT * 1000)
- if returncode is None:
- self.log.debug("Process failed to terminate in time, sending kill!")
- self.process.kill()
- self.process.wait()
- elif returncode > 0:
- self.log.warn("Process returned error code %s", returncode)
-
- self.log.debug("Process stopped.")
-
- # Run the loop to handle the expected name_vanished signal.
- self.loop.run_checked()
- Gio.bus_unwatch_name(self._bus_name_watch_id)
-
- self.process = None
- _process_list.remove(self)
-
- def kill(self):
- global _process_list
-
- if self.process_watch_timeout != 0:
- GLib.source_remove(self.process_watch_timeout)
- self.process_watch_timeout = 0
-
- self.process.kill()
-
- # Name owner changed callback should take us out from this loop
- self.loop.run_checked()
- Gio.bus_unwatch_name(self._bus_name_watch_id)
-
- self.process = None
- _process_list.remove(self)
-
- self.log.debug("Process killed.")
-
-
-class StoreHelper (Helper):
- """
- Helper for starting and testing the tracker-store daemon.
+ Helper for testing the tracker-store daemon.
"""
TRACKER_BUSNAME = 'org.freedesktop.Tracker1'
@@ -234,32 +75,41 @@ class StoreHelper (Helper):
TRACKER_STATUS_OBJ_PATH = "/org/freedesktop/Tracker1/Status"
STATUS_IFACE = "org.freedesktop.Tracker1.Status"
- def __init__(self, process_path):
- Helper.__init__(self, "tracker-store", self.TRACKER_BUSNAME, process_path)
+ def __init__(self, dbus_connection):
+ self.log = logging.getLogger(__name__)
+ self.loop = mainloop.MainLoop()
- def start(self, command_args=None, extra_env=None):
- Helper.start(self, command_args, extra_env)
+ self.bus = dbus_connection
+ self.graph_updated_handler_id = 0
self.resources = Gio.DBusProxy.new_sync(
- self.bus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None,
+ self.bus, Gio.DBusProxyFlags.DO_NOT_AUTO_START_AT_CONSTRUCTION, None,
self.TRACKER_BUSNAME, self.TRACKER_OBJ_PATH, self.RESOURCES_IFACE)
self.backup_iface = Gio.DBusProxy.new_sync(
- self.bus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None,
+ self.bus, Gio.DBusProxyFlags.DO_NOT_AUTO_START_AT_CONSTRUCTION, None,
self.TRACKER_BUSNAME, self.TRACKER_BACKUP_OBJ_PATH, self.BACKUP_IFACE)
self.stats_iface = Gio.DBusProxy.new_sync(
- self.bus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None,
+ self.bus, Gio.DBusProxyFlags.DO_NOT_AUTO_START_AT_CONSTRUCTION, None,
self.TRACKER_BUSNAME, self.TRACKER_STATS_OBJ_PATH, self.STATS_IFACE)
self.status_iface = Gio.DBusProxy.new_sync(
- self.bus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None,
+ self.bus, Gio.DBusProxyFlags.DO_NOT_AUTO_START_AT_CONSTRUCTION, None,
self.TRACKER_BUSNAME, self.TRACKER_STATUS_OBJ_PATH, self.STATUS_IFACE)
+ def start_and_wait_for_ready(self):
+ # The daemon is autostarted as soon as a method is called.
+ #
+ # We set a big timeout to avoid interfering when a daemon is being
+ # interactively debugged.
self.log.debug("Calling %s.Wait() method", self.STATUS_IFACE)
- self.status_iface.Wait()
+ self.status_iface.call_sync('Wait', None, Gio.DBusCallFlags.NONE, 1000000, None)
self.log.debug("Ready")
+ def start_watching_updates(self):
+ assert self.graph_updated_handler_id == 0
+
self.reset_graph_updates_tracking()
def signal_handler(proxy, sender_name, signal_name, parameters):
@@ -268,12 +118,13 @@ class StoreHelper (Helper):
self.graph_updated_handler_id = self.resources.connect(
'g-signal', signal_handler)
+ self.log.debug("Watching for updates from Resources interface")
- def stop(self):
- Helper.stop(self)
-
+ def stop_watching_updates(self):
if self.graph_updated_handler_id != 0:
+ self.log.debug("No longer watching for updates from Resources interface")
self.resources.disconnect(self.graph_updated_handler_id)
+ self.graph_updated_handler_id = 0
# A system to follow GraphUpdated and make sure all changes are tracked.
# This code saves every change notification received, and exposes methods
@@ -328,6 +179,7 @@ class StoreHelper (Helper):
"""
assert (self.inserts_match_function == None)
assert (self.class_to_track == None), "Already waiting for resource of type %s" % self.class_to_track
+ assert (self.graph_updated_handler_id != 0), "You must call start_watching_updates() first."
self.class_to_track = rdf_class
@@ -412,6 +264,7 @@ class StoreHelper (Helper):
"""
assert (self.deletes_match_function == None)
assert (self.class_to_track == None)
+ assert (self.graph_updated_handler_id != 0), "You must call start_watching_updates() first."
def find_resource_deletion(deletes_list):
self.log.debug("find_resource_deletion: looking for %i in %s", id, deletes_list)
@@ -443,8 +296,8 @@ class StoreHelper (Helper):
# Run the event loop until the correct notification arrives
try:
self.loop.run_checked()
- except GraphUpdateTimeoutException:
- raise GraphUpdateTimeoutException("Resource %i has not been deleted." % id)
+ except GraphUpdateTimeoutException as e:
+ raise GraphUpdateTimeoutException("Resource %i has not been deleted." % id) from e
self.deletes_match_function = None
self.class_to_track = None
@@ -457,6 +310,7 @@ class StoreHelper (Helper):
assert (self.inserts_match_function == None)
assert (self.deletes_match_function == None)
assert (self.class_to_track == None)
+ assert (self.graph_updated_handler_id != 0), "You must call start_watching_updates() first."
self.log.debug("Await change to %i %s (%i, %i existing)", subject_id, property_uri, len(self.inserts_list), len(self.deletes_list))
@@ -504,14 +358,14 @@ class StoreHelper (Helper):
# is useful for testing this API surface, but we recommand that all regular
# applications use libtracker-sparql library to talk to the database.
- def query(self, query, timeout=5000, **kwargs):
- return self.resources.SparqlQuery('(s)', query, timeout=timeout, **kwargs)
+ def query(self, query, **kwargs):
+ return self.resources.SparqlQuery('(s)', query, **kwargs)
- def update(self, update_sparql, timeout=5000, **kwargs):
- return self.resources.SparqlUpdate('(s)', update_sparql, timeout=timeout, **kwargs)
+ def update(self, update_sparql, **kwargs):
+ return self.resources.SparqlUpdate('(s)', update_sparql, **kwargs)
- def load(self, ttl_uri, timeout=5000, **kwargs):
- return self.resources.Load('(s)', ttl_uri, timeout=timeout, **kwargs)
+ def load(self, ttl_uri, **kwargs):
+ return self.resources.Load('(s)', ttl_uri, **kwargs)
def batch_update(self, update_sparql, **kwargs):
return self.resources.BatchSparqlUpdate('(s)', update_sparql, **kwargs)
@@ -581,3 +435,69 @@ class StoreHelper (Helper):
return False
else:
raise Exception("Something fishy is going on")
+
+
+class TrackerDBusSandbox:
+ """
+ Private D-Bus session bus which executes a sandboxed Tracker instance.
+
+ """
+ def __init__(self, dbus_daemon_config_file, extra_env=None):
+ self.dbus_daemon_config_file = dbus_daemon_config_file
+ self.extra_env = extra_env or {}
+
+ self.daemon = dbusdaemon.DBusDaemon()
+
+ def start(self, new_session=False):
+ env = os.environ
+ env.update(self.extra_env)
+ env['G_MESSAGES_PREFIXED'] = 'all'
+
+ # This avoids an issue where gvfsd-fuse can start up while the bus is
+ # shutting down. If it fails to connect to the bus, it continues to
+ # run anyway which leads to our dbus-daemon failing to shut down.
+ #
+ # Since https://gitlab.gnome.org/GNOME/gvfs/issues/323 was implemented
+ # in GVFS 1.42 this problem may have gone away.
+ env['GVFS_DISABLE_FUSE'] = '1'
+
+ # Precreate runtime dir, to avoid this warning from dbus-daemon:
+ #
+ # Unable to set up transient service directory: XDG_RUNTIME_DIR "/home/sam/tracker-tests/tmp_59i3ev1/run" not available: No such file or directory
+ #
+ xdg_runtime_dir = env.get('XDG_RUNTIME_DIR')
+ if xdg_runtime_dir:
+ os.makedirs(xdg_runtime_dir, exist_ok=True)
+
+ log.info("Starting D-Bus daemon for sandbox.")
+ log.debug("Added environment variables: %s", self.extra_env)
+ self.daemon.start(self.dbus_daemon_config_file, env=env, new_session=new_session)
+
+ def stop(self):
+ tracker_processes = []
+
+ log.info("Looking for active Tracker processes on the bus")
+ for busname in self.daemon.list_names_sync():
+ if busname.startswith('org.freedesktop.Tracker1'):
+ pid = self.daemon.get_connection_unix_process_id_sync(busname)
+ tracker_processes.append(pid)
+
+ log.info("Terminating %i Tracker processes", len(tracker_processes))
+ for pid in tracker_processes:
+ os.kill(pid, signal.SIGTERM)
+
+ log.info("Waiting for %i Tracker processes", len(tracker_processes))
+ for pid in tracker_processes:
+ psutil.wait_pid(pid)
+
+ # We need to wait until Tracker processes have stopped before we
+ # terminate the D-Bus daemon, otherwise lots of criticals like this
+ # appear in the log output:
+ #
+ # (tracker-miner-fs:14955): GLib-GIO-CRITICAL **: 11:38:40.386: Error while sending AddMatch() message: The connection is closed
+
+ log.info("Stopping D-Bus daemon for sandbox.")
+ self.daemon.stop()
+
+ def get_connection(self):
+ return self.daemon.get_connection()
diff --git a/utils/trackertestutils/meson.build b/utils/trackertestutils/meson.build
index 99573e323..e5132b066 100644
--- a/utils/trackertestutils/meson.build
+++ b/utils/trackertestutils/meson.build
@@ -1,9 +1,39 @@
sources = [
'__init__.py',
+ '__main__.py',
+ 'dbusdaemon.py',
'dconf.py',
'helpers.py',
- 'mainloop.py'
+ 'mainloop.py',
+ 'psutil_mini.py',
]
-install_data(sources,
- install_dir: join_paths(tracker_internal_libs_dir, 'trackertestutils'))
+if get_option('test_utils')
+ testutils_dir = get_option('test_utils_dir')
+
+ if testutils_dir == ''
+ arch_independent_libdir = \
+ get_option('prefix') / 'lib' / 'tracker-' + tracker_api_version
+ testutils_dir = join_paths(arch_independent_libdir)
+ endif
+
+ install_data(sources, install_dir: testutils_dir / 'trackertestutils')
+
+ script_conf = configuration_data()
+ script_conf.set('prefix', get_option('prefix'))
+ script_conf.set('testutils_dir', testutils_dir)
+ configure_file(
+ input: 'tracker-sandbox.in',
+ output: 'tracker-sandbox',
+ configuration: script_conf,
+ install_dir: testutils_dir / 'trackertestutils')
+
+ pkg.generate(
+ name: 'tracker-testutils-' + tracker_api_version,
+ description: 'tracker test utilities',
+ variables: [
+ 'python_path=' + testutils_dir,
+ 'command=' + testutils_dir / 'trackertestutils' / 'tracker-sandbox',
+ ]
+ )
+endif
diff --git a/utils/trackertestutils/psutil_mini.py b/utils/trackertestutils/psutil_mini.py
new file mode 100644
index 000000000..d0c93565d
--- /dev/null
+++ b/utils/trackertestutils/psutil_mini.py
@@ -0,0 +1,98 @@
+# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found at https://github.com/giampaolo/psutil/blob/master/LICENSE
+#
+# Taken from https://github.com/giampaolo/psutil/blob/master/psutil/_psposix.py
+# by Sam Thursfield to avoid adding a dependency between the Tracker testsuite
+# and the 'psutil' module.
+
+
+import os
+import time
+
+
+class TimeoutExpired(Exception):
+ pass
+
+
+def pid_exists(pid):
+ """Check whether pid exists in the current process table."""
+ if pid == 0:
+ # According to "man 2 kill" PID 0 has a special meaning:
+ # it refers to <<every process in the process group of the
+ # calling process>> so we don't want to go any further.
+ # If we get here it means this UNIX platform *does* have
+ # a process with id 0.
+ return True
+ try:
+ os.kill(pid, 0)
+ except ProcessLookupError:
+ return False
+ except PermissionError:
+ # EPERM clearly means there's a process to deny access to
+ return True
+ # According to "man 2 kill" possible error values are
+ # (EINVAL, EPERM, ESRCH)
+ else:
+ return True
+
+
+def wait_pid(pid, timeout=None, proc_name=None):
+ """Wait for process with pid 'pid' to terminate and return its
+ exit status code as an integer.
+ If pid is not a children of os.getpid() (current process) just
+ waits until the process disappears and return None.
+ If pid does not exist at all return None immediately.
+ Raise TimeoutExpired on timeout expired.
+ """
+ def check_timeout(delay):
+ if timeout is not None:
+ if timer() >= stop_at:
+ raise TimeoutExpired(timeout, pid=pid, name=proc_name)
+ time.sleep(delay)
+ return min(delay * 2, 0.04)
+
+ timer = getattr(time, 'monotonic', time.time)
+ if timeout is not None:
+ def waitcall():
+ return os.waitpid(pid, os.WNOHANG)
+ stop_at = timer() + timeout
+ else:
+ def waitcall():
+ return os.waitpid(pid, 0)
+
+ delay = 0.0001
+ while True:
+ try:
+ retpid, status = waitcall()
+ except InterruptedError:
+ delay = check_timeout(delay)
+ except ChildProcessError:
+ # This has two meanings:
+ # - pid is not a child of os.getpid() in which case
+ # we keep polling until it's gone
+ # - pid never existed in the first place
+ # In both cases we'll eventually return None as we
+ # can't determine its exit status code.
+ while True:
+ if pid_exists(pid):
+ delay = check_timeout(delay)
+ else:
+ return
+ else:
+ if retpid == 0:
+ # WNOHANG was used, pid is still running
+ delay = check_timeout(delay)
+ continue
+ # process exited due to a signal; return the integer of
+ # that signal
+ if os.WIFSIGNALED(status):
+ return -os.WTERMSIG(status)
+ # process exited using exit(2) system call; return the
+ # integer exit(2) system call has been called with
+ elif os.WIFEXITED(status):
+ return os.WEXITSTATUS(status)
+ else:
+ # should never happen
+ raise ValueError("unknown process exit status %r" % status)
diff --git a/utils/trackertestutils/tracker-sandbox.in b/utils/trackertestutils/tracker-sandbox.in
new file mode 100755
index 000000000..4c47755a0
--- /dev/null
+++ b/utils/trackertestutils/tracker-sandbox.in
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+export PYTHONPATH=@testutils_dir@
+
+python3 -m trackertestutils --prefix=@prefix@ $@