From f462d63679f3af4f73f91b9ed6dc46d5b6f83cd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Thu, 21 Nov 2024 21:53:43 +0100 Subject: [PATCH 01/46] set correct version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Håvard Ottestad --- assembly-descriptors/pom.xml | 2 +- assembly/pom.xml | 2 +- bom/pom.xml | 2 +- compliance/elasticsearch/pom.xml | 2 +- compliance/geosparql/pom.xml | 2 +- compliance/lucene/pom.xml | 2 +- compliance/model/pom.xml | 2 +- compliance/pom.xml | 2 +- compliance/repository/pom.xml | 2 +- compliance/rio/pom.xml | 2 +- compliance/solr/pom.xml | 2 +- compliance/sparql/pom.xml | 2 +- core/client/pom.xml | 2 +- core/collection-factory/api/pom.xml | 2 +- core/collection-factory/mapdb/pom.xml | 2 +- core/collection-factory/mapdb3/pom.xml | 2 +- core/collection-factory/pom.xml | 2 +- core/common/annotation/pom.xml | 2 +- core/common/exception/pom.xml | 2 +- core/common/io/pom.xml | 2 +- core/common/iterator/pom.xml | 2 +- core/common/order/pom.xml | 2 +- core/common/pom.xml | 2 +- core/common/text/pom.xml | 2 +- core/common/transaction/pom.xml | 2 +- core/common/xml/pom.xml | 2 +- core/http/client/pom.xml | 2 +- core/http/pom.xml | 2 +- core/http/protocol/pom.xml | 2 +- core/model-api/pom.xml | 2 +- core/model-vocabulary/pom.xml | 2 +- core/model/pom.xml | 2 +- core/pom.xml | 2 +- core/query/pom.xml | 2 +- core/queryalgebra/evaluation/pom.xml | 2 +- core/queryalgebra/geosparql/pom.xml | 2 +- core/queryalgebra/model/pom.xml | 2 +- core/queryalgebra/pom.xml | 2 +- core/queryparser/api/pom.xml | 2 +- core/queryparser/pom.xml | 2 +- core/queryparser/sparql/pom.xml | 2 +- core/queryrender/pom.xml | 2 +- core/queryresultio/api/pom.xml | 2 +- core/queryresultio/binary/pom.xml | 2 +- core/queryresultio/pom.xml | 2 +- core/queryresultio/sparqljson/pom.xml | 2 +- core/queryresultio/sparqlxml/pom.xml | 2 +- core/queryresultio/text/pom.xml | 2 +- core/repository/api/pom.xml | 2 +- core/repository/contextaware/pom.xml | 2 +- core/repository/dataset/pom.xml | 2 +- core/repository/event/pom.xml | 2 +- core/repository/http/pom.xml | 2 +- core/repository/manager/pom.xml | 2 +- core/repository/pom.xml | 2 +- core/repository/sail/pom.xml | 2 +- core/repository/sparql/pom.xml | 2 +- core/rio/api/pom.xml | 2 +- core/rio/binary/pom.xml | 2 +- core/rio/datatypes/pom.xml | 2 +- core/rio/hdt/pom.xml | 2 +- core/rio/jsonld-legacy/pom.xml | 2 +- core/rio/jsonld/pom.xml | 2 +- core/rio/languages/pom.xml | 2 +- core/rio/n3/pom.xml | 2 +- core/rio/nquads/pom.xml | 2 +- core/rio/ntriples/pom.xml | 2 +- core/rio/pom.xml | 2 +- core/rio/rdfjson/pom.xml | 2 +- core/rio/rdfxml/pom.xml | 2 +- core/rio/trig/pom.xml | 2 +- core/rio/trix/pom.xml | 2 +- core/rio/turtle/pom.xml | 2 +- core/sail/api/pom.xml | 2 +- core/sail/base/pom.xml | 2 +- core/sail/elasticsearch-store/pom.xml | 2 +- core/sail/elasticsearch/pom.xml | 2 +- core/sail/extensible-store/pom.xml | 2 +- core/sail/inferencer/pom.xml | 2 +- core/sail/lmdb/pom.xml | 2 +- core/sail/lucene-api/pom.xml | 2 +- core/sail/lucene/pom.xml | 2 +- core/sail/memory/pom.xml | 2 +- core/sail/model/pom.xml | 2 +- core/sail/nativerdf/pom.xml | 2 +- core/sail/pom.xml | 2 +- core/sail/shacl/pom.xml | 2 +- core/sail/solr/pom.xml | 2 +- core/sparqlbuilder/pom.xml | 2 +- core/spin/pom.xml | 2 +- core/storage/pom.xml | 2 +- examples/pom.xml | 2 +- pom.xml | 2 +- spring-components/pom.xml | 2 +- spring-components/rdf4j-spring-demo/pom.xml | 2 +- spring-components/rdf4j-spring/pom.xml | 2 +- spring-components/spring-boot-sparql-web/pom.xml | 2 +- testsuites/benchmark/pom.xml | 2 +- testsuites/geosparql/pom.xml | 2 +- testsuites/lucene/pom.xml | 2 +- testsuites/model/pom.xml | 2 +- testsuites/pom.xml | 2 +- testsuites/queryresultio/pom.xml | 2 +- testsuites/repository/pom.xml | 2 +- testsuites/rio/pom.xml | 2 +- testsuites/sail/pom.xml | 2 +- testsuites/sparql/pom.xml | 2 +- tools/config/pom.xml | 2 +- tools/console/pom.xml | 2 +- tools/federation/pom.xml | 2 +- tools/pom.xml | 2 +- tools/runtime-osgi/pom.xml | 2 +- tools/runtime/pom.xml | 2 +- tools/server-spring/pom.xml | 2 +- tools/server/pom.xml | 2 +- tools/workbench/pom.xml | 2 +- 116 files changed, 116 insertions(+), 116 deletions(-) diff --git a/assembly-descriptors/pom.xml b/assembly-descriptors/pom.xml index 121ed8e2ba2..adb16157dc8 100644 --- a/assembly-descriptors/pom.xml +++ b/assembly-descriptors/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-assembly-descriptors RDF4J: Assembly Descriptors diff --git a/assembly/pom.xml b/assembly/pom.xml index cde559e9013..a88d567f0e5 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-assembly pom diff --git a/bom/pom.xml b/bom/pom.xml index 2ce91988ace..f9a2a634d30 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-bom pom diff --git a/compliance/elasticsearch/pom.xml b/compliance/elasticsearch/pom.xml index d7b0a797728..794dacb7bd7 100644 --- a/compliance/elasticsearch/pom.xml +++ b/compliance/elasticsearch/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-elasticsearch-compliance RDF4J: Elasticsearch Sail Tests diff --git a/compliance/geosparql/pom.xml b/compliance/geosparql/pom.xml index 661b3c56a7b..41e8785595b 100644 --- a/compliance/geosparql/pom.xml +++ b/compliance/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-geosparql-compliance RDF4J: GeoSPARQL compliance tests diff --git a/compliance/lucene/pom.xml b/compliance/lucene/pom.xml index 629a0fd3f12..dcf84054c6d 100644 --- a/compliance/lucene/pom.xml +++ b/compliance/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-lucene-compliance RDF4J: Lucene Sail Tests diff --git a/compliance/model/pom.xml b/compliance/model/pom.xml index eebee2b81c4..3d3ac105fa9 100644 --- a/compliance/model/pom.xml +++ b/compliance/model/pom.xml @@ -3,7 +3,7 @@ rdf4j-compliance org.eclipse.rdf4j - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT 4.0.0 rdf4j-model-compliance diff --git a/compliance/pom.xml b/compliance/pom.xml index 94f579c9c81..7a3f7a3e7ff 100644 --- a/compliance/pom.xml +++ b/compliance/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-compliance pom diff --git a/compliance/repository/pom.xml b/compliance/repository/pom.xml index 9ea4fcccea7..069ade53eec 100644 --- a/compliance/repository/pom.xml +++ b/compliance/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-compliance war diff --git a/compliance/rio/pom.xml b/compliance/rio/pom.xml index f9938e1ea54..850062bf2f4 100644 --- a/compliance/rio/pom.xml +++ b/compliance/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-compliance RDF4J: Rio compliance tests diff --git a/compliance/solr/pom.xml b/compliance/solr/pom.xml index 9a84204d508..454f5295ca6 100644 --- a/compliance/solr/pom.xml +++ b/compliance/solr/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-solr-compliance RDF4J: Solr Sail Tests diff --git a/compliance/sparql/pom.xml b/compliance/sparql/pom.xml index 613f2f6e6c7..3340661c18d 100644 --- a/compliance/sparql/pom.xml +++ b/compliance/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparql-compliance war diff --git a/core/client/pom.xml b/core/client/pom.xml index cd3d86508f3..845360a26cc 100644 --- a/core/client/pom.xml +++ b/core/client/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-client RDF4J: Client Libraries diff --git a/core/collection-factory/api/pom.xml b/core/collection-factory/api/pom.xml index 990859fd57b..5ba6df646b7 100644 --- a/core/collection-factory/api/pom.xml +++ b/core/collection-factory/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-api RDF4J: Collection Factory - API diff --git a/core/collection-factory/mapdb/pom.xml b/core/collection-factory/mapdb/pom.xml index 60f0528a761..c8e843b984e 100644 --- a/core/collection-factory/mapdb/pom.xml +++ b/core/collection-factory/mapdb/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-mapdb RDF4J: Collection Factory - Map DB backed diff --git a/core/collection-factory/mapdb3/pom.xml b/core/collection-factory/mapdb3/pom.xml index ad93b88719f..a00d4c8e811 100644 --- a/core/collection-factory/mapdb3/pom.xml +++ b/core/collection-factory/mapdb3/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-mapdb3 RDF4J: Collection Factory - Map DB v3 backed diff --git a/core/collection-factory/pom.xml b/core/collection-factory/pom.xml index e7d267776a6..bb1df7bb226 100644 --- a/core/collection-factory/pom.xml +++ b/core/collection-factory/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory pom diff --git a/core/common/annotation/pom.xml b/core/common/annotation/pom.xml index 3cacb78b6bc..f5f53a65e21 100644 --- a/core/common/annotation/pom.xml +++ b/core/common/annotation/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-annotation RDF4J: common annotation diff --git a/core/common/exception/pom.xml b/core/common/exception/pom.xml index e97c2df723a..bbd533f1adf 100644 --- a/core/common/exception/pom.xml +++ b/core/common/exception/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-exception RDF4J: common exception diff --git a/core/common/io/pom.xml b/core/common/io/pom.xml index ee1a5e4a9e4..f99f7f2af4e 100644 --- a/core/common/io/pom.xml +++ b/core/common/io/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-io RDF4J: common IO diff --git a/core/common/iterator/pom.xml b/core/common/iterator/pom.xml index 96f1e18d2a9..c8b62012574 100644 --- a/core/common/iterator/pom.xml +++ b/core/common/iterator/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-iterator RDF4J: common iterators diff --git a/core/common/order/pom.xml b/core/common/order/pom.xml index 3554c30c63d..5228081fc0e 100644 --- a/core/common/order/pom.xml +++ b/core/common/order/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-order RDF4J: common order diff --git a/core/common/pom.xml b/core/common/pom.xml index 985b19438fa..fc821867500 100644 --- a/core/common/pom.xml +++ b/core/common/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common pom diff --git a/core/common/text/pom.xml b/core/common/text/pom.xml index 7f00f956c68..f26391a3d11 100644 --- a/core/common/text/pom.xml +++ b/core/common/text/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-text RDF4J: common text diff --git a/core/common/transaction/pom.xml b/core/common/transaction/pom.xml index 8008d0db56b..2a327eba54c 100644 --- a/core/common/transaction/pom.xml +++ b/core/common/transaction/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-transaction RDF4J: common transaction diff --git a/core/common/xml/pom.xml b/core/common/xml/pom.xml index b8cb3ce0003..76184d6e127 100644 --- a/core/common/xml/pom.xml +++ b/core/common/xml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-xml RDF4J: common XML diff --git a/core/http/client/pom.xml b/core/http/client/pom.xml index be12562215c..8e26e6c8d3f 100644 --- a/core/http/client/pom.xml +++ b/core/http/client/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-http - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-client RDF4J: HTTP client diff --git a/core/http/pom.xml b/core/http/pom.xml index 5e29435fbe2..ee3e0261ad0 100644 --- a/core/http/pom.xml +++ b/core/http/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http pom diff --git a/core/http/protocol/pom.xml b/core/http/protocol/pom.xml index c6921f2d604..a5d66a693e0 100644 --- a/core/http/protocol/pom.xml +++ b/core/http/protocol/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-http - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-protocol RDF4J: HTTP protocol diff --git a/core/model-api/pom.xml b/core/model-api/pom.xml index 314e96933c7..cf07e38798c 100644 --- a/core/model-api/pom.xml +++ b/core/model-api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-api RDF4J: Model API diff --git a/core/model-vocabulary/pom.xml b/core/model-vocabulary/pom.xml index 751cd7dc29b..bd27791c1d8 100644 --- a/core/model-vocabulary/pom.xml +++ b/core/model-vocabulary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-vocabulary RDF4J: RDF Vocabularies diff --git a/core/model/pom.xml b/core/model/pom.xml index 15ff94455c0..17a4deb868b 100644 --- a/core/model/pom.xml +++ b/core/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model RDF4J: Model diff --git a/core/pom.xml b/core/pom.xml index e4665863f33..ed94faedcde 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-core pom diff --git a/core/query/pom.xml b/core/query/pom.xml index 4d2802c29ee..6600bd51a4e 100644 --- a/core/query/pom.xml +++ b/core/query/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-query RDF4J: Query diff --git a/core/queryalgebra/evaluation/pom.xml b/core/queryalgebra/evaluation/pom.xml index 48a649fa85c..a29c5f1ab29 100644 --- a/core/queryalgebra/evaluation/pom.xml +++ b/core/queryalgebra/evaluation/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-evaluation RDF4J: Query algebra - evaluation diff --git a/core/queryalgebra/geosparql/pom.xml b/core/queryalgebra/geosparql/pom.xml index 45245e73201..10fcbc837ce 100644 --- a/core/queryalgebra/geosparql/pom.xml +++ b/core/queryalgebra/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-geosparql RDF4J: Query algebra - GeoSPARQL diff --git a/core/queryalgebra/model/pom.xml b/core/queryalgebra/model/pom.xml index 2151bf0d2e5..c561441a066 100644 --- a/core/queryalgebra/model/pom.xml +++ b/core/queryalgebra/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-model RDF4J: Query algebra - model diff --git a/core/queryalgebra/pom.xml b/core/queryalgebra/pom.xml index aed17b203e1..fb04d8339cd 100644 --- a/core/queryalgebra/pom.xml +++ b/core/queryalgebra/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra pom diff --git a/core/queryparser/api/pom.xml b/core/queryparser/api/pom.xml index ae2c9c77a82..6450c375f42 100644 --- a/core/queryparser/api/pom.xml +++ b/core/queryparser/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryparser - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser-api RDF4J: Query parser - API diff --git a/core/queryparser/pom.xml b/core/queryparser/pom.xml index 88d5e5a1824..0dfcb8573d3 100644 --- a/core/queryparser/pom.xml +++ b/core/queryparser/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser pom diff --git a/core/queryparser/sparql/pom.xml b/core/queryparser/sparql/pom.xml index 8500eefdba0..d21ecf2b1b2 100644 --- a/core/queryparser/sparql/pom.xml +++ b/core/queryparser/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryparser - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser-sparql RDF4J: Query parser - SPARQL diff --git a/core/queryrender/pom.xml b/core/queryrender/pom.xml index 5866fe2e2d9..8db12169a80 100644 --- a/core/queryrender/pom.xml +++ b/core/queryrender/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryrender RDF4J: Query Rendering diff --git a/core/queryresultio/api/pom.xml b/core/queryresultio/api/pom.xml index b55c344b6fa..273fe3b9801 100644 --- a/core/queryresultio/api/pom.xml +++ b/core/queryresultio/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-api RDF4J: Query result IO - API diff --git a/core/queryresultio/binary/pom.xml b/core/queryresultio/binary/pom.xml index 565077db965..34df6c0b64f 100644 --- a/core/queryresultio/binary/pom.xml +++ b/core/queryresultio/binary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-binary RDF4J: Query result IO - binary diff --git a/core/queryresultio/pom.xml b/core/queryresultio/pom.xml index d395651273f..c1ba95a8c53 100644 --- a/core/queryresultio/pom.xml +++ b/core/queryresultio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio pom diff --git a/core/queryresultio/sparqljson/pom.xml b/core/queryresultio/sparqljson/pom.xml index 959b1ebc3a9..c5f589b6457 100644 --- a/core/queryresultio/sparqljson/pom.xml +++ b/core/queryresultio/sparqljson/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-sparqljson RDF4J: Query result IO - SPARQL/JSON diff --git a/core/queryresultio/sparqlxml/pom.xml b/core/queryresultio/sparqlxml/pom.xml index f6860dc9aa1..8d7bee2745b 100644 --- a/core/queryresultio/sparqlxml/pom.xml +++ b/core/queryresultio/sparqlxml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-sparqlxml RDF4J: Query result IO - SPARQL/XML diff --git a/core/queryresultio/text/pom.xml b/core/queryresultio/text/pom.xml index 277b0c4cd47..e5f67e0b32e 100644 --- a/core/queryresultio/text/pom.xml +++ b/core/queryresultio/text/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-text RDF4J: Query result IO - plain text booleans diff --git a/core/repository/api/pom.xml b/core/repository/api/pom.xml index 6a1e117778e..48fdeecfad7 100644 --- a/core/repository/api/pom.xml +++ b/core/repository/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-api RDF4J: Repository - API diff --git a/core/repository/contextaware/pom.xml b/core/repository/contextaware/pom.xml index bd030ce07a0..39bcbf0668c 100644 --- a/core/repository/contextaware/pom.xml +++ b/core/repository/contextaware/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-contextaware RDF4J: Repository - context aware (wrapper) diff --git a/core/repository/dataset/pom.xml b/core/repository/dataset/pom.xml index 0d120efd71d..c843dccda42 100644 --- a/core/repository/dataset/pom.xml +++ b/core/repository/dataset/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-dataset RDF4J: DatasetRepository (wrapper) diff --git a/core/repository/event/pom.xml b/core/repository/event/pom.xml index 26a18558a5a..3a3109cf967 100644 --- a/core/repository/event/pom.xml +++ b/core/repository/event/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-event RDF4J: Repository - event (wrapper) diff --git a/core/repository/http/pom.xml b/core/repository/http/pom.xml index 92374d71583..4fc07dc06e9 100644 --- a/core/repository/http/pom.xml +++ b/core/repository/http/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-http RDF4J: HTTPRepository diff --git a/core/repository/manager/pom.xml b/core/repository/manager/pom.xml index 80b4283777f..bad53d7101e 100644 --- a/core/repository/manager/pom.xml +++ b/core/repository/manager/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-manager RDF4J: Repository manager diff --git a/core/repository/pom.xml b/core/repository/pom.xml index b38d3640caf..873757a9580 100644 --- a/core/repository/pom.xml +++ b/core/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository pom diff --git a/core/repository/sail/pom.xml b/core/repository/sail/pom.xml index 5869a977f90..fb8fc483b2b 100644 --- a/core/repository/sail/pom.xml +++ b/core/repository/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-sail RDF4J: SailRepository diff --git a/core/repository/sparql/pom.xml b/core/repository/sparql/pom.xml index b8a3da42db0..66d5ee263ce 100644 --- a/core/repository/sparql/pom.xml +++ b/core/repository/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-sparql RDF4J: SPARQL Repository diff --git a/core/rio/api/pom.xml b/core/rio/api/pom.xml index 8c4f66f7d3c..3733b074423 100644 --- a/core/rio/api/pom.xml +++ b/core/rio/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-api RDF4J: Rio - API diff --git a/core/rio/binary/pom.xml b/core/rio/binary/pom.xml index 2bf630ed46b..4ff5469eddf 100644 --- a/core/rio/binary/pom.xml +++ b/core/rio/binary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-binary RDF4J: Rio - Binary diff --git a/core/rio/datatypes/pom.xml b/core/rio/datatypes/pom.xml index f5216b744e8..b9d9dcadcfb 100644 --- a/core/rio/datatypes/pom.xml +++ b/core/rio/datatypes/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-datatypes RDF4J: Rio - Datatypes diff --git a/core/rio/hdt/pom.xml b/core/rio/hdt/pom.xml index 9c615992ba2..f3647663a65 100644 --- a/core/rio/hdt/pom.xml +++ b/core/rio/hdt/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-hdt jar diff --git a/core/rio/jsonld-legacy/pom.xml b/core/rio/jsonld-legacy/pom.xml index 5e95c64650c..7509ef4b834 100644 --- a/core/rio/jsonld-legacy/pom.xml +++ b/core/rio/jsonld-legacy/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-jsonld-legacy RDF4J: Rio - JSON-LD 1.0 (legacy) diff --git a/core/rio/jsonld/pom.xml b/core/rio/jsonld/pom.xml index fc6882180f0..c817e4db0af 100644 --- a/core/rio/jsonld/pom.xml +++ b/core/rio/jsonld/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-jsonld RDF4J: Rio - JSON-LD diff --git a/core/rio/languages/pom.xml b/core/rio/languages/pom.xml index 9d3508f4ea5..b35931702bb 100644 --- a/core/rio/languages/pom.xml +++ b/core/rio/languages/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-languages RDF4J: Rio - Languages diff --git a/core/rio/n3/pom.xml b/core/rio/n3/pom.xml index 3a5958f2321..54ae5fe573c 100644 --- a/core/rio/n3/pom.xml +++ b/core/rio/n3/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-n3 RDF4J: Rio - N3 (writer-only) diff --git a/core/rio/nquads/pom.xml b/core/rio/nquads/pom.xml index 987bf7fe866..044216524fb 100644 --- a/core/rio/nquads/pom.xml +++ b/core/rio/nquads/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-nquads RDF4J: Rio - N-Quads diff --git a/core/rio/ntriples/pom.xml b/core/rio/ntriples/pom.xml index bf41da86c0d..37553504ed5 100644 --- a/core/rio/ntriples/pom.xml +++ b/core/rio/ntriples/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-ntriples RDF4J: Rio - N-Triples diff --git a/core/rio/pom.xml b/core/rio/pom.xml index 9c37e852179..77fc1cee292 100644 --- a/core/rio/pom.xml +++ b/core/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio pom diff --git a/core/rio/rdfjson/pom.xml b/core/rio/rdfjson/pom.xml index 1c41846a2ae..55a3e3ad374 100644 --- a/core/rio/rdfjson/pom.xml +++ b/core/rio/rdfjson/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-rdfjson RDF4J: Rio - RDF/JSON diff --git a/core/rio/rdfxml/pom.xml b/core/rio/rdfxml/pom.xml index f070ad4772d..08a1e0f6eb3 100644 --- a/core/rio/rdfxml/pom.xml +++ b/core/rio/rdfxml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-rdfxml RDF4J: Rio - RDF/XML diff --git a/core/rio/trig/pom.xml b/core/rio/trig/pom.xml index 69729d05015..2736b12a916 100644 --- a/core/rio/trig/pom.xml +++ b/core/rio/trig/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-trig RDF4J: Rio - TriG diff --git a/core/rio/trix/pom.xml b/core/rio/trix/pom.xml index c4b8b777c63..a8b99df7634 100644 --- a/core/rio/trix/pom.xml +++ b/core/rio/trix/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-trix RDF4J: Rio - TriX diff --git a/core/rio/turtle/pom.xml b/core/rio/turtle/pom.xml index f9dd21f0e02..087f4221601 100644 --- a/core/rio/turtle/pom.xml +++ b/core/rio/turtle/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-turtle RDF4J: Rio - Turtle diff --git a/core/sail/api/pom.xml b/core/sail/api/pom.xml index ca2d05d95f1..94ba13deba0 100644 --- a/core/sail/api/pom.xml +++ b/core/sail/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-api RDF4J: Sail API diff --git a/core/sail/base/pom.xml b/core/sail/base/pom.xml index f0a4959b2ad..37f440d24a5 100644 --- a/core/sail/base/pom.xml +++ b/core/sail/base/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-base RDF4J: Sail base implementations diff --git a/core/sail/elasticsearch-store/pom.xml b/core/sail/elasticsearch-store/pom.xml index ec7d4eacdea..8542f4a1aaa 100644 --- a/core/sail/elasticsearch-store/pom.xml +++ b/core/sail/elasticsearch-store/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-elasticsearch-store RDF4J: Elasticsearch Store diff --git a/core/sail/elasticsearch/pom.xml b/core/sail/elasticsearch/pom.xml index 56bfadca70b..022319c4697 100644 --- a/core/sail/elasticsearch/pom.xml +++ b/core/sail/elasticsearch/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-elasticsearch RDF4J: Elastic Search Sail Index diff --git a/core/sail/extensible-store/pom.xml b/core/sail/extensible-store/pom.xml index 3ef69efb649..e8d6a1af491 100644 --- a/core/sail/extensible-store/pom.xml +++ b/core/sail/extensible-store/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-extensible-store RDF4J: Extensible Store diff --git a/core/sail/inferencer/pom.xml b/core/sail/inferencer/pom.xml index 780457d19b8..45babf4a3e2 100644 --- a/core/sail/inferencer/pom.xml +++ b/core/sail/inferencer/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-inferencer RDF4J: Inferencer Sails diff --git a/core/sail/lmdb/pom.xml b/core/sail/lmdb/pom.xml index c6afed34356..9a494fe3521 100644 --- a/core/sail/lmdb/pom.xml +++ b/core/sail/lmdb/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lmdb RDF4J: LmdbStore diff --git a/core/sail/lucene-api/pom.xml b/core/sail/lucene-api/pom.xml index 96d6d53bb22..a52d440fc00 100644 --- a/core/sail/lucene-api/pom.xml +++ b/core/sail/lucene-api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lucene-api RDF4J: Lucene Sail API diff --git a/core/sail/lucene/pom.xml b/core/sail/lucene/pom.xml index 971ad816c89..530c6ac3c5d 100644 --- a/core/sail/lucene/pom.xml +++ b/core/sail/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lucene RDF4J: Lucene Sail Index diff --git a/core/sail/memory/pom.xml b/core/sail/memory/pom.xml index 84425cef452..ae81a8c5531 100644 --- a/core/sail/memory/pom.xml +++ b/core/sail/memory/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-memory RDF4J: MemoryStore diff --git a/core/sail/model/pom.xml b/core/sail/model/pom.xml index 1b0e401a09c..531c473d10a 100644 --- a/core/sail/model/pom.xml +++ b/core/sail/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-model RDF4J: Sail Model diff --git a/core/sail/nativerdf/pom.xml b/core/sail/nativerdf/pom.xml index a3f55944c9e..17dbcc1c962 100644 --- a/core/sail/nativerdf/pom.xml +++ b/core/sail/nativerdf/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-nativerdf RDF4J: NativeStore diff --git a/core/sail/pom.xml b/core/sail/pom.xml index c9466bd0cc3..cbce9f38bc7 100644 --- a/core/sail/pom.xml +++ b/core/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail pom diff --git a/core/sail/shacl/pom.xml b/core/sail/shacl/pom.xml index d9497b91c97..9cae94fe046 100644 --- a/core/sail/shacl/pom.xml +++ b/core/sail/shacl/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-shacl RDF4J: SHACL diff --git a/core/sail/solr/pom.xml b/core/sail/solr/pom.xml index 1377fa73fe3..c79bfa393e7 100644 --- a/core/sail/solr/pom.xml +++ b/core/sail/solr/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-solr RDF4J: Solr Sail Index diff --git a/core/sparqlbuilder/pom.xml b/core/sparqlbuilder/pom.xml index ff715db4f41..2e589196e67 100644 --- a/core/sparqlbuilder/pom.xml +++ b/core/sparqlbuilder/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparqlbuilder RDF4J: SparqlBuilder diff --git a/core/spin/pom.xml b/core/spin/pom.xml index afd2d6e07aa..b80e9d40e04 100644 --- a/core/spin/pom.xml +++ b/core/spin/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spin RDF4J: SPIN diff --git a/core/storage/pom.xml b/core/storage/pom.xml index f4a418e5293..6775e4bf837 100644 --- a/core/storage/pom.xml +++ b/core/storage/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-storage RDF4J: Storage Libraries diff --git a/examples/pom.xml b/examples/pom.xml index 80b443faef4..5610e589454 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -7,7 +7,7 @@ org.eclipse.rdf4j rdf4j - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/pom.xml b/pom.xml index e72a8ef60f8..76b87b2bb27 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.eclipse.rdf4j rdf4j - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT pom Eclipse RDF4J An extensible Java framework for RDF and SPARQL diff --git a/spring-components/pom.xml b/spring-components/pom.xml index fb94cb97caa..571eebb5bce 100644 --- a/spring-components/pom.xml +++ b/spring-components/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT pom diff --git a/spring-components/rdf4j-spring-demo/pom.xml b/spring-components/rdf4j-spring-demo/pom.xml index c8451f1b164..e3376a0dd51 100644 --- a/spring-components/rdf4j-spring-demo/pom.xml +++ b/spring-components/rdf4j-spring-demo/pom.xml @@ -7,7 +7,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/spring-components/rdf4j-spring/pom.xml b/spring-components/rdf4j-spring/pom.xml index 3ac836d96bc..b3616e04951 100644 --- a/spring-components/rdf4j-spring/pom.xml +++ b/spring-components/rdf4j-spring/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spring RDF4J: Spring diff --git a/spring-components/spring-boot-sparql-web/pom.xml b/spring-components/spring-boot-sparql-web/pom.xml index b285f3502d3..222e9020799 100644 --- a/spring-components/spring-boot-sparql-web/pom.xml +++ b/spring-components/spring-boot-sparql-web/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spring-boot-sparql-web RDF4J: Spring boot component for a HTTP sparql server diff --git a/testsuites/benchmark/pom.xml b/testsuites/benchmark/pom.xml index e90aa3e94e0..d2d51f0daa4 100644 --- a/testsuites/benchmark/pom.xml +++ b/testsuites/benchmark/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-benchmark RDF4J: benchmarks diff --git a/testsuites/geosparql/pom.xml b/testsuites/geosparql/pom.xml index 6ec011ac0f1..3ebb0735c3e 100644 --- a/testsuites/geosparql/pom.xml +++ b/testsuites/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-geosparql-testsuite RDF4J: GeoSPARQL compliance test suite diff --git a/testsuites/lucene/pom.xml b/testsuites/lucene/pom.xml index 49f4151081e..66488c8e819 100644 --- a/testsuites/lucene/pom.xml +++ b/testsuites/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-lucene-testsuite RDF4J: Lucene Sail Tests diff --git a/testsuites/model/pom.xml b/testsuites/model/pom.xml index 8b3e02afa9a..cc4dc7ab767 100644 --- a/testsuites/model/pom.xml +++ b/testsuites/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-testsuite RDF4J: Model API testsuite diff --git a/testsuites/pom.xml b/testsuites/pom.xml index b600f042cfc..42088f21a4d 100644 --- a/testsuites/pom.xml +++ b/testsuites/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-testsuites pom diff --git a/testsuites/queryresultio/pom.xml b/testsuites/queryresultio/pom.xml index ba96d29e939..feaf586b953 100644 --- a/testsuites/queryresultio/pom.xml +++ b/testsuites/queryresultio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-testsuite RDF4J: QueryResultIO testsuite diff --git a/testsuites/repository/pom.xml b/testsuites/repository/pom.xml index 389c729ea12..d58184a0a20 100644 --- a/testsuites/repository/pom.xml +++ b/testsuites/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-testsuite RDF4J: Repository API testsuite diff --git a/testsuites/rio/pom.xml b/testsuites/rio/pom.xml index 282c782377e..8bab2f967d5 100644 --- a/testsuites/rio/pom.xml +++ b/testsuites/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-testsuite RDF4J: Rio compliance test suite diff --git a/testsuites/sail/pom.xml b/testsuites/sail/pom.xml index 260f4921d0d..4b9b7d6c2d1 100644 --- a/testsuites/sail/pom.xml +++ b/testsuites/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-testsuite RDF4J: Sail API testsuite diff --git a/testsuites/sparql/pom.xml b/testsuites/sparql/pom.xml index 5b8b46249e6..6d81c721943 100644 --- a/testsuites/sparql/pom.xml +++ b/testsuites/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparql-testsuite RDF4J: SPARQL compliance test suite diff --git a/tools/config/pom.xml b/tools/config/pom.xml index 9f0dd4650f0..c6e819ebcc0 100644 --- a/tools/config/pom.xml +++ b/tools/config/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-config RDF4J: application configuration diff --git a/tools/console/pom.xml b/tools/console/pom.xml index 29db9c66647..a342f03ad96 100644 --- a/tools/console/pom.xml +++ b/tools/console/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-console RDF4J: Console diff --git a/tools/federation/pom.xml b/tools/federation/pom.xml index 45d75471e14..5430f89d375 100644 --- a/tools/federation/pom.xml +++ b/tools/federation/pom.xml @@ -8,7 +8,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/tools/pom.xml b/tools/pom.xml index 5f58818122d..08778066c03 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-tools pom diff --git a/tools/runtime-osgi/pom.xml b/tools/runtime-osgi/pom.xml index bca4db78d89..642959844af 100644 --- a/tools/runtime-osgi/pom.xml +++ b/tools/runtime-osgi/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-runtime-osgi bundle diff --git a/tools/runtime/pom.xml b/tools/runtime/pom.xml index 465176955b7..ccb07aa2992 100644 --- a/tools/runtime/pom.xml +++ b/tools/runtime/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-runtime RDF4J: Runtime diff --git a/tools/server-spring/pom.xml b/tools/server-spring/pom.xml index ae3db72a775..a3cba434f29 100644 --- a/tools/server-spring/pom.xml +++ b/tools/server-spring/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-server-spring RDF4J: HTTP server - core diff --git a/tools/server/pom.xml b/tools/server/pom.xml index f0925f16833..5e6fc517570 100644 --- a/tools/server/pom.xml +++ b/tools/server/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-server war diff --git a/tools/workbench/pom.xml b/tools/workbench/pom.xml index ad146145268..cabca3a9a48 100644 --- a/tools/workbench/pom.xml +++ b/tools/workbench/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.1-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-workbench war From e7a04f50ac7602a296d1be087d17bec9a4e618e0 Mon Sep 17 00:00:00 2001 From: bherber1 Date: Tue, 26 Nov 2024 15:01:20 -0600 Subject: [PATCH 02/46] GH-5210 Enable implementing sails to benefit from parent sail buffering --- .../sail/helpers/AbstractSailConnection.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/core/sail/api/src/main/java/org/eclipse/rdf4j/sail/helpers/AbstractSailConnection.java b/core/sail/api/src/main/java/org/eclipse/rdf4j/sail/helpers/AbstractSailConnection.java index ca9bb7b60fb..01d9c659a6d 100644 --- a/core/sail/api/src/main/java/org/eclipse/rdf4j/sail/helpers/AbstractSailConnection.java +++ b/core/sail/api/src/main/java/org/eclipse/rdf4j/sail/helpers/AbstractSailConnection.java @@ -24,6 +24,7 @@ import java.util.concurrent.locks.LockSupport; import java.util.concurrent.locks.ReentrantLock; +import org.eclipse.rdf4j.common.annotation.Experimental; import org.eclipse.rdf4j.common.annotation.InternalUseOnly; import org.eclipse.rdf4j.common.concurrent.locks.Lock; import org.eclipse.rdf4j.common.concurrent.locks.diagnostics.ConcurrentCleaner; @@ -733,10 +734,8 @@ protected void endUpdateInternal(UpdateContext op) throws SailException { synchronized (added) { model = added.remove(op); } - if (model != null) { - for (Statement st : model) { - addStatementInternal(st.getSubject(), st.getPredicate(), st.getObject(), st.getContext()); - } + if (model != null && !model.isEmpty()) { + bulkAddStatementsInternal(model); } } @@ -973,6 +972,14 @@ protected void prepareInternal() throws SailException { protected abstract void addStatementInternal(Resource subj, IRI pred, Value obj, Resource... contexts) throws SailException; + @Experimental + protected void bulkAddStatementsInternal(final Collection statements) + throws SailException { + for (final Statement st : statements) { + addStatementInternal(st.getSubject(), st.getPredicate(), st.getObject(), st.getContext()); + } + } + protected abstract void removeStatementsInternal(Resource subj, IRI pred, Value obj, Resource... contexts) throws SailException; From b79d828d43a934edf6b2cf703783ccf9b06aad0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Tue, 28 Jan 2025 05:27:18 +0100 Subject: [PATCH 03/46] set correct version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Håvard Ottestad --- assembly-descriptors/pom.xml | 2 +- assembly/pom.xml | 2 +- bom/pom.xml | 2 +- compliance/elasticsearch/pom.xml | 2 +- compliance/geosparql/pom.xml | 2 +- compliance/lucene/pom.xml | 2 +- compliance/model/pom.xml | 2 +- compliance/pom.xml | 2 +- compliance/repository/pom.xml | 2 +- compliance/rio/pom.xml | 2 +- compliance/solr/pom.xml | 2 +- compliance/sparql/pom.xml | 2 +- core/client/pom.xml | 2 +- core/collection-factory/api/pom.xml | 2 +- core/collection-factory/mapdb/pom.xml | 2 +- core/collection-factory/mapdb3/pom.xml | 2 +- core/collection-factory/pom.xml | 2 +- core/common/annotation/pom.xml | 2 +- core/common/exception/pom.xml | 2 +- core/common/io/pom.xml | 2 +- core/common/iterator/pom.xml | 2 +- core/common/order/pom.xml | 2 +- core/common/pom.xml | 2 +- core/common/text/pom.xml | 2 +- core/common/transaction/pom.xml | 2 +- core/common/xml/pom.xml | 2 +- core/http/client/pom.xml | 2 +- core/http/pom.xml | 2 +- core/http/protocol/pom.xml | 2 +- core/model-api/pom.xml | 2 +- core/model-vocabulary/pom.xml | 2 +- core/model/pom.xml | 2 +- core/pom.xml | 2 +- core/query/pom.xml | 2 +- core/queryalgebra/evaluation/pom.xml | 2 +- core/queryalgebra/geosparql/pom.xml | 2 +- core/queryalgebra/model/pom.xml | 2 +- core/queryalgebra/pom.xml | 2 +- core/queryparser/api/pom.xml | 2 +- core/queryparser/pom.xml | 2 +- core/queryparser/sparql/pom.xml | 2 +- core/queryrender/pom.xml | 2 +- core/queryresultio/api/pom.xml | 2 +- core/queryresultio/binary/pom.xml | 2 +- core/queryresultio/pom.xml | 2 +- core/queryresultio/sparqljson/pom.xml | 2 +- core/queryresultio/sparqlxml/pom.xml | 2 +- core/queryresultio/text/pom.xml | 2 +- core/repository/api/pom.xml | 2 +- core/repository/contextaware/pom.xml | 2 +- core/repository/dataset/pom.xml | 2 +- core/repository/event/pom.xml | 2 +- core/repository/http/pom.xml | 2 +- core/repository/manager/pom.xml | 2 +- core/repository/pom.xml | 2 +- core/repository/sail/pom.xml | 2 +- core/repository/sparql/pom.xml | 2 +- core/rio/api/pom.xml | 2 +- core/rio/binary/pom.xml | 2 +- core/rio/datatypes/pom.xml | 2 +- core/rio/hdt/pom.xml | 2 +- core/rio/jsonld-legacy/pom.xml | 2 +- core/rio/jsonld/pom.xml | 2 +- core/rio/languages/pom.xml | 2 +- core/rio/n3/pom.xml | 2 +- core/rio/nquads/pom.xml | 2 +- core/rio/ntriples/pom.xml | 2 +- core/rio/pom.xml | 2 +- core/rio/rdfjson/pom.xml | 2 +- core/rio/rdfxml/pom.xml | 2 +- core/rio/trig/pom.xml | 2 +- core/rio/trix/pom.xml | 2 +- core/rio/turtle/pom.xml | 2 +- core/sail/api/pom.xml | 2 +- core/sail/base/pom.xml | 2 +- core/sail/elasticsearch-store/pom.xml | 2 +- core/sail/elasticsearch/pom.xml | 2 +- core/sail/extensible-store/pom.xml | 2 +- core/sail/inferencer/pom.xml | 2 +- core/sail/lmdb/pom.xml | 2 +- core/sail/lucene-api/pom.xml | 2 +- core/sail/lucene/pom.xml | 2 +- core/sail/memory/pom.xml | 2 +- core/sail/model/pom.xml | 2 +- core/sail/nativerdf/pom.xml | 2 +- core/sail/pom.xml | 2 +- core/sail/shacl/pom.xml | 2 +- core/sail/solr/pom.xml | 2 +- core/sparqlbuilder/pom.xml | 2 +- core/spin/pom.xml | 2 +- core/storage/pom.xml | 2 +- examples/pom.xml | 2 +- pom.xml | 2 +- spring-components/pom.xml | 2 +- spring-components/rdf4j-spring-demo/pom.xml | 2 +- spring-components/rdf4j-spring/pom.xml | 2 +- spring-components/spring-boot-sparql-web/pom.xml | 2 +- testsuites/benchmark/pom.xml | 2 +- testsuites/geosparql/pom.xml | 2 +- testsuites/lucene/pom.xml | 2 +- testsuites/model/pom.xml | 2 +- testsuites/pom.xml | 2 +- testsuites/queryresultio/pom.xml | 2 +- testsuites/repository/pom.xml | 2 +- testsuites/rio/pom.xml | 2 +- testsuites/sail/pom.xml | 2 +- testsuites/sparql/pom.xml | 2 +- tools/config/pom.xml | 2 +- tools/console/pom.xml | 2 +- tools/federation/pom.xml | 2 +- tools/pom.xml | 2 +- tools/runtime-osgi/pom.xml | 2 +- tools/runtime/pom.xml | 2 +- tools/server-spring/pom.xml | 2 +- tools/server/pom.xml | 2 +- tools/workbench/pom.xml | 2 +- 116 files changed, 116 insertions(+), 116 deletions(-) diff --git a/assembly-descriptors/pom.xml b/assembly-descriptors/pom.xml index 2fbb6b00055..adb16157dc8 100644 --- a/assembly-descriptors/pom.xml +++ b/assembly-descriptors/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-assembly-descriptors RDF4J: Assembly Descriptors diff --git a/assembly/pom.xml b/assembly/pom.xml index 1358dc49ae1..a88d567f0e5 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-assembly pom diff --git a/bom/pom.xml b/bom/pom.xml index e7576dd0f63..8056082ccc1 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-bom pom diff --git a/compliance/elasticsearch/pom.xml b/compliance/elasticsearch/pom.xml index 0d0d0f1d423..794dacb7bd7 100644 --- a/compliance/elasticsearch/pom.xml +++ b/compliance/elasticsearch/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-elasticsearch-compliance RDF4J: Elasticsearch Sail Tests diff --git a/compliance/geosparql/pom.xml b/compliance/geosparql/pom.xml index 5ca63a92a92..41e8785595b 100644 --- a/compliance/geosparql/pom.xml +++ b/compliance/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-geosparql-compliance RDF4J: GeoSPARQL compliance tests diff --git a/compliance/lucene/pom.xml b/compliance/lucene/pom.xml index a8ba5050a29..dcf84054c6d 100644 --- a/compliance/lucene/pom.xml +++ b/compliance/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-lucene-compliance RDF4J: Lucene Sail Tests diff --git a/compliance/model/pom.xml b/compliance/model/pom.xml index d79740b4095..3d3ac105fa9 100644 --- a/compliance/model/pom.xml +++ b/compliance/model/pom.xml @@ -3,7 +3,7 @@ rdf4j-compliance org.eclipse.rdf4j - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT 4.0.0 rdf4j-model-compliance diff --git a/compliance/pom.xml b/compliance/pom.xml index 37a94c5c17f..7a3f7a3e7ff 100644 --- a/compliance/pom.xml +++ b/compliance/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-compliance pom diff --git a/compliance/repository/pom.xml b/compliance/repository/pom.xml index a06214fbca3..069ade53eec 100644 --- a/compliance/repository/pom.xml +++ b/compliance/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-compliance war diff --git a/compliance/rio/pom.xml b/compliance/rio/pom.xml index e5e4f7762a3..850062bf2f4 100644 --- a/compliance/rio/pom.xml +++ b/compliance/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-compliance RDF4J: Rio compliance tests diff --git a/compliance/solr/pom.xml b/compliance/solr/pom.xml index 288f56f3ea8..454f5295ca6 100644 --- a/compliance/solr/pom.xml +++ b/compliance/solr/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-solr-compliance RDF4J: Solr Sail Tests diff --git a/compliance/sparql/pom.xml b/compliance/sparql/pom.xml index 967c04284e3..3340661c18d 100644 --- a/compliance/sparql/pom.xml +++ b/compliance/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparql-compliance war diff --git a/core/client/pom.xml b/core/client/pom.xml index 754202c6af5..845360a26cc 100644 --- a/core/client/pom.xml +++ b/core/client/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-client RDF4J: Client Libraries diff --git a/core/collection-factory/api/pom.xml b/core/collection-factory/api/pom.xml index 52c8d8ddb4a..5ba6df646b7 100644 --- a/core/collection-factory/api/pom.xml +++ b/core/collection-factory/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-api RDF4J: Collection Factory - API diff --git a/core/collection-factory/mapdb/pom.xml b/core/collection-factory/mapdb/pom.xml index 021ede2ae0a..c8e843b984e 100644 --- a/core/collection-factory/mapdb/pom.xml +++ b/core/collection-factory/mapdb/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-mapdb RDF4J: Collection Factory - Map DB backed diff --git a/core/collection-factory/mapdb3/pom.xml b/core/collection-factory/mapdb3/pom.xml index c7e19c660d1..a00d4c8e811 100644 --- a/core/collection-factory/mapdb3/pom.xml +++ b/core/collection-factory/mapdb3/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-mapdb3 RDF4J: Collection Factory - Map DB v3 backed diff --git a/core/collection-factory/pom.xml b/core/collection-factory/pom.xml index 921b427e139..bb1df7bb226 100644 --- a/core/collection-factory/pom.xml +++ b/core/collection-factory/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory pom diff --git a/core/common/annotation/pom.xml b/core/common/annotation/pom.xml index 9db7a775bbc..f5f53a65e21 100644 --- a/core/common/annotation/pom.xml +++ b/core/common/annotation/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-annotation RDF4J: common annotation diff --git a/core/common/exception/pom.xml b/core/common/exception/pom.xml index 32343327681..bbd533f1adf 100644 --- a/core/common/exception/pom.xml +++ b/core/common/exception/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-exception RDF4J: common exception diff --git a/core/common/io/pom.xml b/core/common/io/pom.xml index ab867a77557..f99f7f2af4e 100644 --- a/core/common/io/pom.xml +++ b/core/common/io/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-io RDF4J: common IO diff --git a/core/common/iterator/pom.xml b/core/common/iterator/pom.xml index 8944d6c580d..c8b62012574 100644 --- a/core/common/iterator/pom.xml +++ b/core/common/iterator/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-iterator RDF4J: common iterators diff --git a/core/common/order/pom.xml b/core/common/order/pom.xml index 31b5d67cb71..5228081fc0e 100644 --- a/core/common/order/pom.xml +++ b/core/common/order/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-order RDF4J: common order diff --git a/core/common/pom.xml b/core/common/pom.xml index b8a4c182f01..fc821867500 100644 --- a/core/common/pom.xml +++ b/core/common/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common pom diff --git a/core/common/text/pom.xml b/core/common/text/pom.xml index f1b254c8b84..f26391a3d11 100644 --- a/core/common/text/pom.xml +++ b/core/common/text/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-text RDF4J: common text diff --git a/core/common/transaction/pom.xml b/core/common/transaction/pom.xml index fe83ae7ebe5..2a327eba54c 100644 --- a/core/common/transaction/pom.xml +++ b/core/common/transaction/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-transaction RDF4J: common transaction diff --git a/core/common/xml/pom.xml b/core/common/xml/pom.xml index f17d0c61e27..76184d6e127 100644 --- a/core/common/xml/pom.xml +++ b/core/common/xml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-xml RDF4J: common XML diff --git a/core/http/client/pom.xml b/core/http/client/pom.xml index 9fb3abea679..8e26e6c8d3f 100644 --- a/core/http/client/pom.xml +++ b/core/http/client/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-http - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-client RDF4J: HTTP client diff --git a/core/http/pom.xml b/core/http/pom.xml index 01f9cebdf42..ee3e0261ad0 100644 --- a/core/http/pom.xml +++ b/core/http/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http pom diff --git a/core/http/protocol/pom.xml b/core/http/protocol/pom.xml index 99208cdb401..a5d66a693e0 100644 --- a/core/http/protocol/pom.xml +++ b/core/http/protocol/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-http - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-protocol RDF4J: HTTP protocol diff --git a/core/model-api/pom.xml b/core/model-api/pom.xml index dffa76212f4..cf07e38798c 100644 --- a/core/model-api/pom.xml +++ b/core/model-api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-api RDF4J: Model API diff --git a/core/model-vocabulary/pom.xml b/core/model-vocabulary/pom.xml index 72f384c8fe5..bd27791c1d8 100644 --- a/core/model-vocabulary/pom.xml +++ b/core/model-vocabulary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-vocabulary RDF4J: RDF Vocabularies diff --git a/core/model/pom.xml b/core/model/pom.xml index e72b78585a7..17a4deb868b 100644 --- a/core/model/pom.xml +++ b/core/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model RDF4J: Model diff --git a/core/pom.xml b/core/pom.xml index d5c5715feb3..ed94faedcde 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-core pom diff --git a/core/query/pom.xml b/core/query/pom.xml index b26d97633a1..6600bd51a4e 100644 --- a/core/query/pom.xml +++ b/core/query/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-query RDF4J: Query diff --git a/core/queryalgebra/evaluation/pom.xml b/core/queryalgebra/evaluation/pom.xml index 7221c1c612b..a29c5f1ab29 100644 --- a/core/queryalgebra/evaluation/pom.xml +++ b/core/queryalgebra/evaluation/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-evaluation RDF4J: Query algebra - evaluation diff --git a/core/queryalgebra/geosparql/pom.xml b/core/queryalgebra/geosparql/pom.xml index c2bd3c9d5ee..10fcbc837ce 100644 --- a/core/queryalgebra/geosparql/pom.xml +++ b/core/queryalgebra/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-geosparql RDF4J: Query algebra - GeoSPARQL diff --git a/core/queryalgebra/model/pom.xml b/core/queryalgebra/model/pom.xml index 22b0ef499fc..c561441a066 100644 --- a/core/queryalgebra/model/pom.xml +++ b/core/queryalgebra/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-model RDF4J: Query algebra - model diff --git a/core/queryalgebra/pom.xml b/core/queryalgebra/pom.xml index 51b27bfecb8..fb04d8339cd 100644 --- a/core/queryalgebra/pom.xml +++ b/core/queryalgebra/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra pom diff --git a/core/queryparser/api/pom.xml b/core/queryparser/api/pom.xml index 96ce143d209..6450c375f42 100644 --- a/core/queryparser/api/pom.xml +++ b/core/queryparser/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryparser - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser-api RDF4J: Query parser - API diff --git a/core/queryparser/pom.xml b/core/queryparser/pom.xml index 4a2fb7ff244..0dfcb8573d3 100644 --- a/core/queryparser/pom.xml +++ b/core/queryparser/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser pom diff --git a/core/queryparser/sparql/pom.xml b/core/queryparser/sparql/pom.xml index df76f81bd38..d21ecf2b1b2 100644 --- a/core/queryparser/sparql/pom.xml +++ b/core/queryparser/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryparser - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser-sparql RDF4J: Query parser - SPARQL diff --git a/core/queryrender/pom.xml b/core/queryrender/pom.xml index 3f564ba28f4..8db12169a80 100644 --- a/core/queryrender/pom.xml +++ b/core/queryrender/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryrender RDF4J: Query Rendering diff --git a/core/queryresultio/api/pom.xml b/core/queryresultio/api/pom.xml index 0bc1bb4cdee..273fe3b9801 100644 --- a/core/queryresultio/api/pom.xml +++ b/core/queryresultio/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-api RDF4J: Query result IO - API diff --git a/core/queryresultio/binary/pom.xml b/core/queryresultio/binary/pom.xml index 1980bd8146f..34df6c0b64f 100644 --- a/core/queryresultio/binary/pom.xml +++ b/core/queryresultio/binary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-binary RDF4J: Query result IO - binary diff --git a/core/queryresultio/pom.xml b/core/queryresultio/pom.xml index b1f7d9c7c67..c1ba95a8c53 100644 --- a/core/queryresultio/pom.xml +++ b/core/queryresultio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio pom diff --git a/core/queryresultio/sparqljson/pom.xml b/core/queryresultio/sparqljson/pom.xml index fb430470942..c5f589b6457 100644 --- a/core/queryresultio/sparqljson/pom.xml +++ b/core/queryresultio/sparqljson/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-sparqljson RDF4J: Query result IO - SPARQL/JSON diff --git a/core/queryresultio/sparqlxml/pom.xml b/core/queryresultio/sparqlxml/pom.xml index c5e3e084e61..8d7bee2745b 100644 --- a/core/queryresultio/sparqlxml/pom.xml +++ b/core/queryresultio/sparqlxml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-sparqlxml RDF4J: Query result IO - SPARQL/XML diff --git a/core/queryresultio/text/pom.xml b/core/queryresultio/text/pom.xml index a65e35756e2..e5f67e0b32e 100644 --- a/core/queryresultio/text/pom.xml +++ b/core/queryresultio/text/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-text RDF4J: Query result IO - plain text booleans diff --git a/core/repository/api/pom.xml b/core/repository/api/pom.xml index c927da7ef81..48fdeecfad7 100644 --- a/core/repository/api/pom.xml +++ b/core/repository/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-api RDF4J: Repository - API diff --git a/core/repository/contextaware/pom.xml b/core/repository/contextaware/pom.xml index 49cd4bf9a57..39bcbf0668c 100644 --- a/core/repository/contextaware/pom.xml +++ b/core/repository/contextaware/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-contextaware RDF4J: Repository - context aware (wrapper) diff --git a/core/repository/dataset/pom.xml b/core/repository/dataset/pom.xml index ab3e4dcf21c..c843dccda42 100644 --- a/core/repository/dataset/pom.xml +++ b/core/repository/dataset/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-dataset RDF4J: DatasetRepository (wrapper) diff --git a/core/repository/event/pom.xml b/core/repository/event/pom.xml index 703e26bd376..3a3109cf967 100644 --- a/core/repository/event/pom.xml +++ b/core/repository/event/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-event RDF4J: Repository - event (wrapper) diff --git a/core/repository/http/pom.xml b/core/repository/http/pom.xml index 7e8c280892a..4fc07dc06e9 100644 --- a/core/repository/http/pom.xml +++ b/core/repository/http/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-http RDF4J: HTTPRepository diff --git a/core/repository/manager/pom.xml b/core/repository/manager/pom.xml index 941107914b9..bad53d7101e 100644 --- a/core/repository/manager/pom.xml +++ b/core/repository/manager/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-manager RDF4J: Repository manager diff --git a/core/repository/pom.xml b/core/repository/pom.xml index d2ce3b841b9..873757a9580 100644 --- a/core/repository/pom.xml +++ b/core/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository pom diff --git a/core/repository/sail/pom.xml b/core/repository/sail/pom.xml index 7f04e4d7115..fb8fc483b2b 100644 --- a/core/repository/sail/pom.xml +++ b/core/repository/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-sail RDF4J: SailRepository diff --git a/core/repository/sparql/pom.xml b/core/repository/sparql/pom.xml index 73657f7033f..66d5ee263ce 100644 --- a/core/repository/sparql/pom.xml +++ b/core/repository/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-sparql RDF4J: SPARQL Repository diff --git a/core/rio/api/pom.xml b/core/rio/api/pom.xml index 5eccc28ef51..3733b074423 100644 --- a/core/rio/api/pom.xml +++ b/core/rio/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-api RDF4J: Rio - API diff --git a/core/rio/binary/pom.xml b/core/rio/binary/pom.xml index 1c9b4507e77..4ff5469eddf 100644 --- a/core/rio/binary/pom.xml +++ b/core/rio/binary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-binary RDF4J: Rio - Binary diff --git a/core/rio/datatypes/pom.xml b/core/rio/datatypes/pom.xml index 3d6c72e588c..b9d9dcadcfb 100644 --- a/core/rio/datatypes/pom.xml +++ b/core/rio/datatypes/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-datatypes RDF4J: Rio - Datatypes diff --git a/core/rio/hdt/pom.xml b/core/rio/hdt/pom.xml index f9a0edf0ff0..f3647663a65 100644 --- a/core/rio/hdt/pom.xml +++ b/core/rio/hdt/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-hdt jar diff --git a/core/rio/jsonld-legacy/pom.xml b/core/rio/jsonld-legacy/pom.xml index 3fdc57c06f0..7509ef4b834 100644 --- a/core/rio/jsonld-legacy/pom.xml +++ b/core/rio/jsonld-legacy/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-jsonld-legacy RDF4J: Rio - JSON-LD 1.0 (legacy) diff --git a/core/rio/jsonld/pom.xml b/core/rio/jsonld/pom.xml index 89bd05c6c67..c817e4db0af 100644 --- a/core/rio/jsonld/pom.xml +++ b/core/rio/jsonld/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-jsonld RDF4J: Rio - JSON-LD diff --git a/core/rio/languages/pom.xml b/core/rio/languages/pom.xml index cecc03dd5b2..b35931702bb 100644 --- a/core/rio/languages/pom.xml +++ b/core/rio/languages/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-languages RDF4J: Rio - Languages diff --git a/core/rio/n3/pom.xml b/core/rio/n3/pom.xml index df944373b46..54ae5fe573c 100644 --- a/core/rio/n3/pom.xml +++ b/core/rio/n3/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-n3 RDF4J: Rio - N3 (writer-only) diff --git a/core/rio/nquads/pom.xml b/core/rio/nquads/pom.xml index 8709008c291..044216524fb 100644 --- a/core/rio/nquads/pom.xml +++ b/core/rio/nquads/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-nquads RDF4J: Rio - N-Quads diff --git a/core/rio/ntriples/pom.xml b/core/rio/ntriples/pom.xml index 43a40303103..37553504ed5 100644 --- a/core/rio/ntriples/pom.xml +++ b/core/rio/ntriples/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-ntriples RDF4J: Rio - N-Triples diff --git a/core/rio/pom.xml b/core/rio/pom.xml index 8fb737bb122..77fc1cee292 100644 --- a/core/rio/pom.xml +++ b/core/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio pom diff --git a/core/rio/rdfjson/pom.xml b/core/rio/rdfjson/pom.xml index 6a7f3231661..55a3e3ad374 100644 --- a/core/rio/rdfjson/pom.xml +++ b/core/rio/rdfjson/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-rdfjson RDF4J: Rio - RDF/JSON diff --git a/core/rio/rdfxml/pom.xml b/core/rio/rdfxml/pom.xml index e818e3c28f9..08a1e0f6eb3 100644 --- a/core/rio/rdfxml/pom.xml +++ b/core/rio/rdfxml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-rdfxml RDF4J: Rio - RDF/XML diff --git a/core/rio/trig/pom.xml b/core/rio/trig/pom.xml index 8dbb8fe6b5e..2736b12a916 100644 --- a/core/rio/trig/pom.xml +++ b/core/rio/trig/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-trig RDF4J: Rio - TriG diff --git a/core/rio/trix/pom.xml b/core/rio/trix/pom.xml index e43437a9337..a8b99df7634 100644 --- a/core/rio/trix/pom.xml +++ b/core/rio/trix/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-trix RDF4J: Rio - TriX diff --git a/core/rio/turtle/pom.xml b/core/rio/turtle/pom.xml index 0f8bce287fe..087f4221601 100644 --- a/core/rio/turtle/pom.xml +++ b/core/rio/turtle/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-turtle RDF4J: Rio - Turtle diff --git a/core/sail/api/pom.xml b/core/sail/api/pom.xml index 9ddc2f76dd2..94ba13deba0 100644 --- a/core/sail/api/pom.xml +++ b/core/sail/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-api RDF4J: Sail API diff --git a/core/sail/base/pom.xml b/core/sail/base/pom.xml index 71d9a30f8bb..37f440d24a5 100644 --- a/core/sail/base/pom.xml +++ b/core/sail/base/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-base RDF4J: Sail base implementations diff --git a/core/sail/elasticsearch-store/pom.xml b/core/sail/elasticsearch-store/pom.xml index cea5704f074..8542f4a1aaa 100644 --- a/core/sail/elasticsearch-store/pom.xml +++ b/core/sail/elasticsearch-store/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-elasticsearch-store RDF4J: Elasticsearch Store diff --git a/core/sail/elasticsearch/pom.xml b/core/sail/elasticsearch/pom.xml index b0bbb5eac74..022319c4697 100644 --- a/core/sail/elasticsearch/pom.xml +++ b/core/sail/elasticsearch/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-elasticsearch RDF4J: Elastic Search Sail Index diff --git a/core/sail/extensible-store/pom.xml b/core/sail/extensible-store/pom.xml index 5d1ae0ac7b8..e8d6a1af491 100644 --- a/core/sail/extensible-store/pom.xml +++ b/core/sail/extensible-store/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-extensible-store RDF4J: Extensible Store diff --git a/core/sail/inferencer/pom.xml b/core/sail/inferencer/pom.xml index 6708bd33947..45babf4a3e2 100644 --- a/core/sail/inferencer/pom.xml +++ b/core/sail/inferencer/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-inferencer RDF4J: Inferencer Sails diff --git a/core/sail/lmdb/pom.xml b/core/sail/lmdb/pom.xml index 2571ee37d44..787fb19c4d4 100644 --- a/core/sail/lmdb/pom.xml +++ b/core/sail/lmdb/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lmdb RDF4J: LmdbStore diff --git a/core/sail/lucene-api/pom.xml b/core/sail/lucene-api/pom.xml index 282be11d0b7..a52d440fc00 100644 --- a/core/sail/lucene-api/pom.xml +++ b/core/sail/lucene-api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lucene-api RDF4J: Lucene Sail API diff --git a/core/sail/lucene/pom.xml b/core/sail/lucene/pom.xml index 3958efec38a..530c6ac3c5d 100644 --- a/core/sail/lucene/pom.xml +++ b/core/sail/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lucene RDF4J: Lucene Sail Index diff --git a/core/sail/memory/pom.xml b/core/sail/memory/pom.xml index 0cbe06812f8..ae81a8c5531 100644 --- a/core/sail/memory/pom.xml +++ b/core/sail/memory/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-memory RDF4J: MemoryStore diff --git a/core/sail/model/pom.xml b/core/sail/model/pom.xml index f1e35605c59..531c473d10a 100644 --- a/core/sail/model/pom.xml +++ b/core/sail/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-model RDF4J: Sail Model diff --git a/core/sail/nativerdf/pom.xml b/core/sail/nativerdf/pom.xml index d8025689e1f..17dbcc1c962 100644 --- a/core/sail/nativerdf/pom.xml +++ b/core/sail/nativerdf/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-nativerdf RDF4J: NativeStore diff --git a/core/sail/pom.xml b/core/sail/pom.xml index c161069a761..cbce9f38bc7 100644 --- a/core/sail/pom.xml +++ b/core/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail pom diff --git a/core/sail/shacl/pom.xml b/core/sail/shacl/pom.xml index bc713aa0fa3..9cae94fe046 100644 --- a/core/sail/shacl/pom.xml +++ b/core/sail/shacl/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-shacl RDF4J: SHACL diff --git a/core/sail/solr/pom.xml b/core/sail/solr/pom.xml index e235b2e08ff..c79bfa393e7 100644 --- a/core/sail/solr/pom.xml +++ b/core/sail/solr/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-solr RDF4J: Solr Sail Index diff --git a/core/sparqlbuilder/pom.xml b/core/sparqlbuilder/pom.xml index 5c29d1c364a..2e589196e67 100644 --- a/core/sparqlbuilder/pom.xml +++ b/core/sparqlbuilder/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparqlbuilder RDF4J: SparqlBuilder diff --git a/core/spin/pom.xml b/core/spin/pom.xml index 4846134d7f9..b80e9d40e04 100644 --- a/core/spin/pom.xml +++ b/core/spin/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spin RDF4J: SPIN diff --git a/core/storage/pom.xml b/core/storage/pom.xml index 8d26426058e..6775e4bf837 100644 --- a/core/storage/pom.xml +++ b/core/storage/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-storage RDF4J: Storage Libraries diff --git a/examples/pom.xml b/examples/pom.xml index b5364742438..5610e589454 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -7,7 +7,7 @@ org.eclipse.rdf4j rdf4j - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/pom.xml b/pom.xml index 329ca303ff9..7a1279635cd 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.eclipse.rdf4j rdf4j - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT pom Eclipse RDF4J An extensible Java framework for RDF and SPARQL diff --git a/spring-components/pom.xml b/spring-components/pom.xml index d8c88659955..571eebb5bce 100644 --- a/spring-components/pom.xml +++ b/spring-components/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT pom diff --git a/spring-components/rdf4j-spring-demo/pom.xml b/spring-components/rdf4j-spring-demo/pom.xml index cd9f067df6d..e3376a0dd51 100644 --- a/spring-components/rdf4j-spring-demo/pom.xml +++ b/spring-components/rdf4j-spring-demo/pom.xml @@ -7,7 +7,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/spring-components/rdf4j-spring/pom.xml b/spring-components/rdf4j-spring/pom.xml index 0871362d75c..b3616e04951 100644 --- a/spring-components/rdf4j-spring/pom.xml +++ b/spring-components/rdf4j-spring/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spring RDF4J: Spring diff --git a/spring-components/spring-boot-sparql-web/pom.xml b/spring-components/spring-boot-sparql-web/pom.xml index 0cb6db79674..222e9020799 100644 --- a/spring-components/spring-boot-sparql-web/pom.xml +++ b/spring-components/spring-boot-sparql-web/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spring-boot-sparql-web RDF4J: Spring boot component for a HTTP sparql server diff --git a/testsuites/benchmark/pom.xml b/testsuites/benchmark/pom.xml index 47ec5295b71..96c07db5f28 100644 --- a/testsuites/benchmark/pom.xml +++ b/testsuites/benchmark/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-benchmark RDF4J: benchmarks diff --git a/testsuites/geosparql/pom.xml b/testsuites/geosparql/pom.xml index 7a598405a10..3ebb0735c3e 100644 --- a/testsuites/geosparql/pom.xml +++ b/testsuites/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-geosparql-testsuite RDF4J: GeoSPARQL compliance test suite diff --git a/testsuites/lucene/pom.xml b/testsuites/lucene/pom.xml index e69a03fe7e4..66488c8e819 100644 --- a/testsuites/lucene/pom.xml +++ b/testsuites/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-lucene-testsuite RDF4J: Lucene Sail Tests diff --git a/testsuites/model/pom.xml b/testsuites/model/pom.xml index 28a8c326488..cc4dc7ab767 100644 --- a/testsuites/model/pom.xml +++ b/testsuites/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-testsuite RDF4J: Model API testsuite diff --git a/testsuites/pom.xml b/testsuites/pom.xml index 84bd848da80..42088f21a4d 100644 --- a/testsuites/pom.xml +++ b/testsuites/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-testsuites pom diff --git a/testsuites/queryresultio/pom.xml b/testsuites/queryresultio/pom.xml index 098f0bdbe68..feaf586b953 100644 --- a/testsuites/queryresultio/pom.xml +++ b/testsuites/queryresultio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-testsuite RDF4J: QueryResultIO testsuite diff --git a/testsuites/repository/pom.xml b/testsuites/repository/pom.xml index f9f690ccac5..d58184a0a20 100644 --- a/testsuites/repository/pom.xml +++ b/testsuites/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-testsuite RDF4J: Repository API testsuite diff --git a/testsuites/rio/pom.xml b/testsuites/rio/pom.xml index a34edf8c926..8bab2f967d5 100644 --- a/testsuites/rio/pom.xml +++ b/testsuites/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-testsuite RDF4J: Rio compliance test suite diff --git a/testsuites/sail/pom.xml b/testsuites/sail/pom.xml index fd7a9c4eafd..4b9b7d6c2d1 100644 --- a/testsuites/sail/pom.xml +++ b/testsuites/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-testsuite RDF4J: Sail API testsuite diff --git a/testsuites/sparql/pom.xml b/testsuites/sparql/pom.xml index 23039e0786f..6d81c721943 100644 --- a/testsuites/sparql/pom.xml +++ b/testsuites/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparql-testsuite RDF4J: SPARQL compliance test suite diff --git a/tools/config/pom.xml b/tools/config/pom.xml index edbb719748e..c6e819ebcc0 100644 --- a/tools/config/pom.xml +++ b/tools/config/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-config RDF4J: application configuration diff --git a/tools/console/pom.xml b/tools/console/pom.xml index 45ae6c57155..a342f03ad96 100644 --- a/tools/console/pom.xml +++ b/tools/console/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-console RDF4J: Console diff --git a/tools/federation/pom.xml b/tools/federation/pom.xml index f5f6f3fbe8f..5430f89d375 100644 --- a/tools/federation/pom.xml +++ b/tools/federation/pom.xml @@ -8,7 +8,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/tools/pom.xml b/tools/pom.xml index 1823f25dfff..08778066c03 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-tools pom diff --git a/tools/runtime-osgi/pom.xml b/tools/runtime-osgi/pom.xml index b25430032e3..642959844af 100644 --- a/tools/runtime-osgi/pom.xml +++ b/tools/runtime-osgi/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-runtime-osgi bundle diff --git a/tools/runtime/pom.xml b/tools/runtime/pom.xml index 42ae1b32ad9..ccb07aa2992 100644 --- a/tools/runtime/pom.xml +++ b/tools/runtime/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-runtime RDF4J: Runtime diff --git a/tools/server-spring/pom.xml b/tools/server-spring/pom.xml index 3298eb4012f..a3cba434f29 100644 --- a/tools/server-spring/pom.xml +++ b/tools/server-spring/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-server-spring RDF4J: HTTP server - core diff --git a/tools/server/pom.xml b/tools/server/pom.xml index 4bf364e24d1..5e6fc517570 100644 --- a/tools/server/pom.xml +++ b/tools/server/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-server war diff --git a/tools/workbench/pom.xml b/tools/workbench/pom.xml index 9f31bbbaf60..cabca3a9a48 100644 --- a/tools/workbench/pom.xml +++ b/tools/workbench/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.2-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-workbench war From 1ea5b9c520cf8e2b182e4c3e3e3a37688f4efa17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Tue, 11 Feb 2025 12:20:50 +0100 Subject: [PATCH 04/46] set correct version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Håvard Ottestad --- assembly-descriptors/pom.xml | 2 +- assembly/pom.xml | 2 +- bom/pom.xml | 2 +- compliance/elasticsearch/pom.xml | 2 +- compliance/geosparql/pom.xml | 2 +- compliance/lucene/pom.xml | 2 +- compliance/model/pom.xml | 2 +- compliance/pom.xml | 2 +- compliance/repository/pom.xml | 2 +- compliance/rio/pom.xml | 2 +- compliance/solr/pom.xml | 2 +- compliance/sparql/pom.xml | 2 +- core/client/pom.xml | 2 +- core/collection-factory/api/pom.xml | 2 +- core/collection-factory/mapdb/pom.xml | 2 +- core/collection-factory/mapdb3/pom.xml | 2 +- core/collection-factory/pom.xml | 2 +- core/common/annotation/pom.xml | 2 +- core/common/exception/pom.xml | 2 +- core/common/io/pom.xml | 2 +- core/common/iterator/pom.xml | 2 +- core/common/order/pom.xml | 2 +- core/common/pom.xml | 2 +- core/common/text/pom.xml | 2 +- core/common/transaction/pom.xml | 2 +- core/common/xml/pom.xml | 2 +- core/http/client/pom.xml | 2 +- core/http/pom.xml | 2 +- core/http/protocol/pom.xml | 2 +- core/model-api/pom.xml | 2 +- core/model-vocabulary/pom.xml | 2 +- core/model/pom.xml | 2 +- core/pom.xml | 2 +- core/query/pom.xml | 2 +- core/queryalgebra/evaluation/pom.xml | 2 +- core/queryalgebra/geosparql/pom.xml | 2 +- core/queryalgebra/model/pom.xml | 2 +- core/queryalgebra/pom.xml | 2 +- core/queryparser/api/pom.xml | 2 +- core/queryparser/pom.xml | 2 +- core/queryparser/sparql/pom.xml | 2 +- core/queryrender/pom.xml | 2 +- core/queryresultio/api/pom.xml | 2 +- core/queryresultio/binary/pom.xml | 2 +- core/queryresultio/pom.xml | 2 +- core/queryresultio/sparqljson/pom.xml | 2 +- core/queryresultio/sparqlxml/pom.xml | 2 +- core/queryresultio/text/pom.xml | 2 +- core/repository/api/pom.xml | 2 +- core/repository/contextaware/pom.xml | 2 +- core/repository/dataset/pom.xml | 2 +- core/repository/event/pom.xml | 2 +- core/repository/http/pom.xml | 2 +- core/repository/manager/pom.xml | 2 +- core/repository/pom.xml | 2 +- core/repository/sail/pom.xml | 2 +- core/repository/sparql/pom.xml | 2 +- core/rio/api/pom.xml | 2 +- core/rio/binary/pom.xml | 2 +- core/rio/datatypes/pom.xml | 2 +- core/rio/hdt/pom.xml | 2 +- core/rio/jsonld-legacy/pom.xml | 2 +- core/rio/jsonld/pom.xml | 2 +- core/rio/languages/pom.xml | 2 +- core/rio/n3/pom.xml | 2 +- core/rio/nquads/pom.xml | 2 +- core/rio/ntriples/pom.xml | 2 +- core/rio/pom.xml | 2 +- core/rio/rdfjson/pom.xml | 2 +- core/rio/rdfxml/pom.xml | 2 +- core/rio/trig/pom.xml | 2 +- core/rio/trix/pom.xml | 2 +- core/rio/turtle/pom.xml | 2 +- core/sail/api/pom.xml | 2 +- core/sail/base/pom.xml | 2 +- core/sail/elasticsearch-store/pom.xml | 2 +- core/sail/elasticsearch/pom.xml | 2 +- core/sail/extensible-store/pom.xml | 2 +- core/sail/inferencer/pom.xml | 2 +- core/sail/lmdb/pom.xml | 2 +- core/sail/lucene-api/pom.xml | 2 +- core/sail/lucene/pom.xml | 2 +- core/sail/memory/pom.xml | 2 +- core/sail/model/pom.xml | 2 +- core/sail/nativerdf/pom.xml | 2 +- core/sail/pom.xml | 2 +- core/sail/shacl/pom.xml | 2 +- core/sail/solr/pom.xml | 2 +- core/sparqlbuilder/pom.xml | 2 +- core/spin/pom.xml | 2 +- core/storage/pom.xml | 2 +- examples/pom.xml | 2 +- pom.xml | 2 +- spring-components/pom.xml | 2 +- spring-components/rdf4j-spring-demo/pom.xml | 2 +- spring-components/rdf4j-spring/pom.xml | 2 +- spring-components/spring-boot-sparql-web/pom.xml | 2 +- testsuites/benchmark/pom.xml | 2 +- testsuites/geosparql/pom.xml | 2 +- testsuites/lucene/pom.xml | 2 +- testsuites/model/pom.xml | 2 +- testsuites/pom.xml | 2 +- testsuites/queryresultio/pom.xml | 2 +- testsuites/repository/pom.xml | 2 +- testsuites/rio/pom.xml | 2 +- testsuites/sail/pom.xml | 2 +- testsuites/sparql/pom.xml | 2 +- tools/config/pom.xml | 2 +- tools/console/pom.xml | 2 +- tools/federation/pom.xml | 2 +- tools/pom.xml | 2 +- tools/runtime-osgi/pom.xml | 2 +- tools/runtime/pom.xml | 2 +- tools/server-spring/pom.xml | 2 +- tools/server/pom.xml | 2 +- tools/workbench/pom.xml | 2 +- 116 files changed, 116 insertions(+), 116 deletions(-) diff --git a/assembly-descriptors/pom.xml b/assembly-descriptors/pom.xml index ee036b47892..adb16157dc8 100644 --- a/assembly-descriptors/pom.xml +++ b/assembly-descriptors/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-assembly-descriptors RDF4J: Assembly Descriptors diff --git a/assembly/pom.xml b/assembly/pom.xml index 114d643f18e..a88d567f0e5 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-assembly pom diff --git a/bom/pom.xml b/bom/pom.xml index d1c34074a0c..8056082ccc1 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-bom pom diff --git a/compliance/elasticsearch/pom.xml b/compliance/elasticsearch/pom.xml index 5e45c974917..794dacb7bd7 100644 --- a/compliance/elasticsearch/pom.xml +++ b/compliance/elasticsearch/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-elasticsearch-compliance RDF4J: Elasticsearch Sail Tests diff --git a/compliance/geosparql/pom.xml b/compliance/geosparql/pom.xml index bab124e9d92..41e8785595b 100644 --- a/compliance/geosparql/pom.xml +++ b/compliance/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-geosparql-compliance RDF4J: GeoSPARQL compliance tests diff --git a/compliance/lucene/pom.xml b/compliance/lucene/pom.xml index 8cdd7240d03..dcf84054c6d 100644 --- a/compliance/lucene/pom.xml +++ b/compliance/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-lucene-compliance RDF4J: Lucene Sail Tests diff --git a/compliance/model/pom.xml b/compliance/model/pom.xml index 76621e0b24c..3d3ac105fa9 100644 --- a/compliance/model/pom.xml +++ b/compliance/model/pom.xml @@ -3,7 +3,7 @@ rdf4j-compliance org.eclipse.rdf4j - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT 4.0.0 rdf4j-model-compliance diff --git a/compliance/pom.xml b/compliance/pom.xml index 5ef5e0ea869..7a3f7a3e7ff 100644 --- a/compliance/pom.xml +++ b/compliance/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-compliance pom diff --git a/compliance/repository/pom.xml b/compliance/repository/pom.xml index 525603a283c..069ade53eec 100644 --- a/compliance/repository/pom.xml +++ b/compliance/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-compliance war diff --git a/compliance/rio/pom.xml b/compliance/rio/pom.xml index 4cf1450eb7d..850062bf2f4 100644 --- a/compliance/rio/pom.xml +++ b/compliance/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-compliance RDF4J: Rio compliance tests diff --git a/compliance/solr/pom.xml b/compliance/solr/pom.xml index 2736ad66afa..454f5295ca6 100644 --- a/compliance/solr/pom.xml +++ b/compliance/solr/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-solr-compliance RDF4J: Solr Sail Tests diff --git a/compliance/sparql/pom.xml b/compliance/sparql/pom.xml index 87a3e7f8686..3340661c18d 100644 --- a/compliance/sparql/pom.xml +++ b/compliance/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparql-compliance war diff --git a/core/client/pom.xml b/core/client/pom.xml index 9d3e54517a9..845360a26cc 100644 --- a/core/client/pom.xml +++ b/core/client/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-client RDF4J: Client Libraries diff --git a/core/collection-factory/api/pom.xml b/core/collection-factory/api/pom.xml index 93ae087d4b6..5ba6df646b7 100644 --- a/core/collection-factory/api/pom.xml +++ b/core/collection-factory/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-api RDF4J: Collection Factory - API diff --git a/core/collection-factory/mapdb/pom.xml b/core/collection-factory/mapdb/pom.xml index 96ba88ed1c0..c8e843b984e 100644 --- a/core/collection-factory/mapdb/pom.xml +++ b/core/collection-factory/mapdb/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-mapdb RDF4J: Collection Factory - Map DB backed diff --git a/core/collection-factory/mapdb3/pom.xml b/core/collection-factory/mapdb3/pom.xml index 2dcbf044f4c..a00d4c8e811 100644 --- a/core/collection-factory/mapdb3/pom.xml +++ b/core/collection-factory/mapdb3/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-mapdb3 RDF4J: Collection Factory - Map DB v3 backed diff --git a/core/collection-factory/pom.xml b/core/collection-factory/pom.xml index 3730b5791d8..bb1df7bb226 100644 --- a/core/collection-factory/pom.xml +++ b/core/collection-factory/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory pom diff --git a/core/common/annotation/pom.xml b/core/common/annotation/pom.xml index 1616b7e2b23..f5f53a65e21 100644 --- a/core/common/annotation/pom.xml +++ b/core/common/annotation/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-annotation RDF4J: common annotation diff --git a/core/common/exception/pom.xml b/core/common/exception/pom.xml index 79971b5c70a..bbd533f1adf 100644 --- a/core/common/exception/pom.xml +++ b/core/common/exception/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-exception RDF4J: common exception diff --git a/core/common/io/pom.xml b/core/common/io/pom.xml index c3a68f8fc47..f99f7f2af4e 100644 --- a/core/common/io/pom.xml +++ b/core/common/io/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-io RDF4J: common IO diff --git a/core/common/iterator/pom.xml b/core/common/iterator/pom.xml index 749c0d5ace7..c8b62012574 100644 --- a/core/common/iterator/pom.xml +++ b/core/common/iterator/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-iterator RDF4J: common iterators diff --git a/core/common/order/pom.xml b/core/common/order/pom.xml index 9376a68cb33..5228081fc0e 100644 --- a/core/common/order/pom.xml +++ b/core/common/order/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-order RDF4J: common order diff --git a/core/common/pom.xml b/core/common/pom.xml index 74ae2eb748b..fc821867500 100644 --- a/core/common/pom.xml +++ b/core/common/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common pom diff --git a/core/common/text/pom.xml b/core/common/text/pom.xml index 2a0a0a6fa7a..f26391a3d11 100644 --- a/core/common/text/pom.xml +++ b/core/common/text/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-text RDF4J: common text diff --git a/core/common/transaction/pom.xml b/core/common/transaction/pom.xml index cfe56234357..2a327eba54c 100644 --- a/core/common/transaction/pom.xml +++ b/core/common/transaction/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-transaction RDF4J: common transaction diff --git a/core/common/xml/pom.xml b/core/common/xml/pom.xml index aa84ad9800a..76184d6e127 100644 --- a/core/common/xml/pom.xml +++ b/core/common/xml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-xml RDF4J: common XML diff --git a/core/http/client/pom.xml b/core/http/client/pom.xml index 6250286308f..8e26e6c8d3f 100644 --- a/core/http/client/pom.xml +++ b/core/http/client/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-http - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-client RDF4J: HTTP client diff --git a/core/http/pom.xml b/core/http/pom.xml index ba2671b8c56..ee3e0261ad0 100644 --- a/core/http/pom.xml +++ b/core/http/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http pom diff --git a/core/http/protocol/pom.xml b/core/http/protocol/pom.xml index f07e229f6fb..a5d66a693e0 100644 --- a/core/http/protocol/pom.xml +++ b/core/http/protocol/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-http - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-protocol RDF4J: HTTP protocol diff --git a/core/model-api/pom.xml b/core/model-api/pom.xml index a433b0f869d..cf07e38798c 100644 --- a/core/model-api/pom.xml +++ b/core/model-api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-api RDF4J: Model API diff --git a/core/model-vocabulary/pom.xml b/core/model-vocabulary/pom.xml index 94e0e9c04df..bd27791c1d8 100644 --- a/core/model-vocabulary/pom.xml +++ b/core/model-vocabulary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-vocabulary RDF4J: RDF Vocabularies diff --git a/core/model/pom.xml b/core/model/pom.xml index f0bbba2b40e..17a4deb868b 100644 --- a/core/model/pom.xml +++ b/core/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model RDF4J: Model diff --git a/core/pom.xml b/core/pom.xml index fb6836e0586..ed94faedcde 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-core pom diff --git a/core/query/pom.xml b/core/query/pom.xml index 87b8f283869..6600bd51a4e 100644 --- a/core/query/pom.xml +++ b/core/query/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-query RDF4J: Query diff --git a/core/queryalgebra/evaluation/pom.xml b/core/queryalgebra/evaluation/pom.xml index c853c8ea202..a29c5f1ab29 100644 --- a/core/queryalgebra/evaluation/pom.xml +++ b/core/queryalgebra/evaluation/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-evaluation RDF4J: Query algebra - evaluation diff --git a/core/queryalgebra/geosparql/pom.xml b/core/queryalgebra/geosparql/pom.xml index 33c264d3a73..10fcbc837ce 100644 --- a/core/queryalgebra/geosparql/pom.xml +++ b/core/queryalgebra/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-geosparql RDF4J: Query algebra - GeoSPARQL diff --git a/core/queryalgebra/model/pom.xml b/core/queryalgebra/model/pom.xml index e46bcf2ad7e..c561441a066 100644 --- a/core/queryalgebra/model/pom.xml +++ b/core/queryalgebra/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-model RDF4J: Query algebra - model diff --git a/core/queryalgebra/pom.xml b/core/queryalgebra/pom.xml index fbfcbfc522a..fb04d8339cd 100644 --- a/core/queryalgebra/pom.xml +++ b/core/queryalgebra/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra pom diff --git a/core/queryparser/api/pom.xml b/core/queryparser/api/pom.xml index 6f177737906..6450c375f42 100644 --- a/core/queryparser/api/pom.xml +++ b/core/queryparser/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryparser - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser-api RDF4J: Query parser - API diff --git a/core/queryparser/pom.xml b/core/queryparser/pom.xml index 1486d5609aa..0dfcb8573d3 100644 --- a/core/queryparser/pom.xml +++ b/core/queryparser/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser pom diff --git a/core/queryparser/sparql/pom.xml b/core/queryparser/sparql/pom.xml index 4052cabda98..d21ecf2b1b2 100644 --- a/core/queryparser/sparql/pom.xml +++ b/core/queryparser/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryparser - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser-sparql RDF4J: Query parser - SPARQL diff --git a/core/queryrender/pom.xml b/core/queryrender/pom.xml index fc75bf6fa40..8db12169a80 100644 --- a/core/queryrender/pom.xml +++ b/core/queryrender/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryrender RDF4J: Query Rendering diff --git a/core/queryresultio/api/pom.xml b/core/queryresultio/api/pom.xml index e602c5b8e0c..273fe3b9801 100644 --- a/core/queryresultio/api/pom.xml +++ b/core/queryresultio/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-api RDF4J: Query result IO - API diff --git a/core/queryresultio/binary/pom.xml b/core/queryresultio/binary/pom.xml index 1bfd2bb14bf..34df6c0b64f 100644 --- a/core/queryresultio/binary/pom.xml +++ b/core/queryresultio/binary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-binary RDF4J: Query result IO - binary diff --git a/core/queryresultio/pom.xml b/core/queryresultio/pom.xml index 5ec5901ed6e..c1ba95a8c53 100644 --- a/core/queryresultio/pom.xml +++ b/core/queryresultio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio pom diff --git a/core/queryresultio/sparqljson/pom.xml b/core/queryresultio/sparqljson/pom.xml index 4047271c712..c5f589b6457 100644 --- a/core/queryresultio/sparqljson/pom.xml +++ b/core/queryresultio/sparqljson/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-sparqljson RDF4J: Query result IO - SPARQL/JSON diff --git a/core/queryresultio/sparqlxml/pom.xml b/core/queryresultio/sparqlxml/pom.xml index ee379720a6d..8d7bee2745b 100644 --- a/core/queryresultio/sparqlxml/pom.xml +++ b/core/queryresultio/sparqlxml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-sparqlxml RDF4J: Query result IO - SPARQL/XML diff --git a/core/queryresultio/text/pom.xml b/core/queryresultio/text/pom.xml index 714bd692e4a..e5f67e0b32e 100644 --- a/core/queryresultio/text/pom.xml +++ b/core/queryresultio/text/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-text RDF4J: Query result IO - plain text booleans diff --git a/core/repository/api/pom.xml b/core/repository/api/pom.xml index 57520d68471..48fdeecfad7 100644 --- a/core/repository/api/pom.xml +++ b/core/repository/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-api RDF4J: Repository - API diff --git a/core/repository/contextaware/pom.xml b/core/repository/contextaware/pom.xml index e0e003fe5ab..39bcbf0668c 100644 --- a/core/repository/contextaware/pom.xml +++ b/core/repository/contextaware/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-contextaware RDF4J: Repository - context aware (wrapper) diff --git a/core/repository/dataset/pom.xml b/core/repository/dataset/pom.xml index 1394368b656..c843dccda42 100644 --- a/core/repository/dataset/pom.xml +++ b/core/repository/dataset/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-dataset RDF4J: DatasetRepository (wrapper) diff --git a/core/repository/event/pom.xml b/core/repository/event/pom.xml index 1ab7a58786d..3a3109cf967 100644 --- a/core/repository/event/pom.xml +++ b/core/repository/event/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-event RDF4J: Repository - event (wrapper) diff --git a/core/repository/http/pom.xml b/core/repository/http/pom.xml index 25555a9948b..4fc07dc06e9 100644 --- a/core/repository/http/pom.xml +++ b/core/repository/http/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-http RDF4J: HTTPRepository diff --git a/core/repository/manager/pom.xml b/core/repository/manager/pom.xml index e2ff925750b..bad53d7101e 100644 --- a/core/repository/manager/pom.xml +++ b/core/repository/manager/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-manager RDF4J: Repository manager diff --git a/core/repository/pom.xml b/core/repository/pom.xml index c8583a15cdd..873757a9580 100644 --- a/core/repository/pom.xml +++ b/core/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository pom diff --git a/core/repository/sail/pom.xml b/core/repository/sail/pom.xml index 0bbe153720d..fb8fc483b2b 100644 --- a/core/repository/sail/pom.xml +++ b/core/repository/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-sail RDF4J: SailRepository diff --git a/core/repository/sparql/pom.xml b/core/repository/sparql/pom.xml index c9b2c8a7a47..66d5ee263ce 100644 --- a/core/repository/sparql/pom.xml +++ b/core/repository/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-sparql RDF4J: SPARQL Repository diff --git a/core/rio/api/pom.xml b/core/rio/api/pom.xml index 3506a515537..3733b074423 100644 --- a/core/rio/api/pom.xml +++ b/core/rio/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-api RDF4J: Rio - API diff --git a/core/rio/binary/pom.xml b/core/rio/binary/pom.xml index 8b797041f50..4ff5469eddf 100644 --- a/core/rio/binary/pom.xml +++ b/core/rio/binary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-binary RDF4J: Rio - Binary diff --git a/core/rio/datatypes/pom.xml b/core/rio/datatypes/pom.xml index 9b939440457..b9d9dcadcfb 100644 --- a/core/rio/datatypes/pom.xml +++ b/core/rio/datatypes/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-datatypes RDF4J: Rio - Datatypes diff --git a/core/rio/hdt/pom.xml b/core/rio/hdt/pom.xml index 713d2a4225f..f3647663a65 100644 --- a/core/rio/hdt/pom.xml +++ b/core/rio/hdt/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-hdt jar diff --git a/core/rio/jsonld-legacy/pom.xml b/core/rio/jsonld-legacy/pom.xml index 6fbf036e252..7509ef4b834 100644 --- a/core/rio/jsonld-legacy/pom.xml +++ b/core/rio/jsonld-legacy/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-jsonld-legacy RDF4J: Rio - JSON-LD 1.0 (legacy) diff --git a/core/rio/jsonld/pom.xml b/core/rio/jsonld/pom.xml index a2e006866f5..c817e4db0af 100644 --- a/core/rio/jsonld/pom.xml +++ b/core/rio/jsonld/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-jsonld RDF4J: Rio - JSON-LD diff --git a/core/rio/languages/pom.xml b/core/rio/languages/pom.xml index 08c7ab9a41c..b35931702bb 100644 --- a/core/rio/languages/pom.xml +++ b/core/rio/languages/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-languages RDF4J: Rio - Languages diff --git a/core/rio/n3/pom.xml b/core/rio/n3/pom.xml index 6693a832fdc..54ae5fe573c 100644 --- a/core/rio/n3/pom.xml +++ b/core/rio/n3/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-n3 RDF4J: Rio - N3 (writer-only) diff --git a/core/rio/nquads/pom.xml b/core/rio/nquads/pom.xml index f1c45b8944c..044216524fb 100644 --- a/core/rio/nquads/pom.xml +++ b/core/rio/nquads/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-nquads RDF4J: Rio - N-Quads diff --git a/core/rio/ntriples/pom.xml b/core/rio/ntriples/pom.xml index bbab8155930..37553504ed5 100644 --- a/core/rio/ntriples/pom.xml +++ b/core/rio/ntriples/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-ntriples RDF4J: Rio - N-Triples diff --git a/core/rio/pom.xml b/core/rio/pom.xml index ecb8cfe2183..77fc1cee292 100644 --- a/core/rio/pom.xml +++ b/core/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio pom diff --git a/core/rio/rdfjson/pom.xml b/core/rio/rdfjson/pom.xml index 47a83b76f63..55a3e3ad374 100644 --- a/core/rio/rdfjson/pom.xml +++ b/core/rio/rdfjson/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-rdfjson RDF4J: Rio - RDF/JSON diff --git a/core/rio/rdfxml/pom.xml b/core/rio/rdfxml/pom.xml index a8510bd13cc..08a1e0f6eb3 100644 --- a/core/rio/rdfxml/pom.xml +++ b/core/rio/rdfxml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-rdfxml RDF4J: Rio - RDF/XML diff --git a/core/rio/trig/pom.xml b/core/rio/trig/pom.xml index 7b287b0f31e..2736b12a916 100644 --- a/core/rio/trig/pom.xml +++ b/core/rio/trig/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-trig RDF4J: Rio - TriG diff --git a/core/rio/trix/pom.xml b/core/rio/trix/pom.xml index 86efd5c0620..a8b99df7634 100644 --- a/core/rio/trix/pom.xml +++ b/core/rio/trix/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-trix RDF4J: Rio - TriX diff --git a/core/rio/turtle/pom.xml b/core/rio/turtle/pom.xml index 0c535ecc576..087f4221601 100644 --- a/core/rio/turtle/pom.xml +++ b/core/rio/turtle/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-turtle RDF4J: Rio - Turtle diff --git a/core/sail/api/pom.xml b/core/sail/api/pom.xml index 0d532ecff0b..94ba13deba0 100644 --- a/core/sail/api/pom.xml +++ b/core/sail/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-api RDF4J: Sail API diff --git a/core/sail/base/pom.xml b/core/sail/base/pom.xml index 3e042760186..37f440d24a5 100644 --- a/core/sail/base/pom.xml +++ b/core/sail/base/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-base RDF4J: Sail base implementations diff --git a/core/sail/elasticsearch-store/pom.xml b/core/sail/elasticsearch-store/pom.xml index e0793c5dc79..8542f4a1aaa 100644 --- a/core/sail/elasticsearch-store/pom.xml +++ b/core/sail/elasticsearch-store/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-elasticsearch-store RDF4J: Elasticsearch Store diff --git a/core/sail/elasticsearch/pom.xml b/core/sail/elasticsearch/pom.xml index e60d6407dbd..022319c4697 100644 --- a/core/sail/elasticsearch/pom.xml +++ b/core/sail/elasticsearch/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-elasticsearch RDF4J: Elastic Search Sail Index diff --git a/core/sail/extensible-store/pom.xml b/core/sail/extensible-store/pom.xml index c418c852ead..e8d6a1af491 100644 --- a/core/sail/extensible-store/pom.xml +++ b/core/sail/extensible-store/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-extensible-store RDF4J: Extensible Store diff --git a/core/sail/inferencer/pom.xml b/core/sail/inferencer/pom.xml index 57b740d83b2..45babf4a3e2 100644 --- a/core/sail/inferencer/pom.xml +++ b/core/sail/inferencer/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-inferencer RDF4J: Inferencer Sails diff --git a/core/sail/lmdb/pom.xml b/core/sail/lmdb/pom.xml index 0342cfa5918..787fb19c4d4 100644 --- a/core/sail/lmdb/pom.xml +++ b/core/sail/lmdb/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lmdb RDF4J: LmdbStore diff --git a/core/sail/lucene-api/pom.xml b/core/sail/lucene-api/pom.xml index 8907ee221a4..a52d440fc00 100644 --- a/core/sail/lucene-api/pom.xml +++ b/core/sail/lucene-api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lucene-api RDF4J: Lucene Sail API diff --git a/core/sail/lucene/pom.xml b/core/sail/lucene/pom.xml index 001cbbc1fc7..530c6ac3c5d 100644 --- a/core/sail/lucene/pom.xml +++ b/core/sail/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lucene RDF4J: Lucene Sail Index diff --git a/core/sail/memory/pom.xml b/core/sail/memory/pom.xml index d5004a77af3..ae81a8c5531 100644 --- a/core/sail/memory/pom.xml +++ b/core/sail/memory/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-memory RDF4J: MemoryStore diff --git a/core/sail/model/pom.xml b/core/sail/model/pom.xml index 0efbfaf0156..531c473d10a 100644 --- a/core/sail/model/pom.xml +++ b/core/sail/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-model RDF4J: Sail Model diff --git a/core/sail/nativerdf/pom.xml b/core/sail/nativerdf/pom.xml index 9afab0943f6..17dbcc1c962 100644 --- a/core/sail/nativerdf/pom.xml +++ b/core/sail/nativerdf/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-nativerdf RDF4J: NativeStore diff --git a/core/sail/pom.xml b/core/sail/pom.xml index 9bdcb85cafb..cbce9f38bc7 100644 --- a/core/sail/pom.xml +++ b/core/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail pom diff --git a/core/sail/shacl/pom.xml b/core/sail/shacl/pom.xml index 99663a9e7e7..9cae94fe046 100644 --- a/core/sail/shacl/pom.xml +++ b/core/sail/shacl/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-shacl RDF4J: SHACL diff --git a/core/sail/solr/pom.xml b/core/sail/solr/pom.xml index 8207a496e40..c79bfa393e7 100644 --- a/core/sail/solr/pom.xml +++ b/core/sail/solr/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-solr RDF4J: Solr Sail Index diff --git a/core/sparqlbuilder/pom.xml b/core/sparqlbuilder/pom.xml index 8cec6f286b0..2e589196e67 100644 --- a/core/sparqlbuilder/pom.xml +++ b/core/sparqlbuilder/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparqlbuilder RDF4J: SparqlBuilder diff --git a/core/spin/pom.xml b/core/spin/pom.xml index 1d75efdb2c4..b80e9d40e04 100644 --- a/core/spin/pom.xml +++ b/core/spin/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spin RDF4J: SPIN diff --git a/core/storage/pom.xml b/core/storage/pom.xml index fc096bfc896..6775e4bf837 100644 --- a/core/storage/pom.xml +++ b/core/storage/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-storage RDF4J: Storage Libraries diff --git a/examples/pom.xml b/examples/pom.xml index b214df315ab..5610e589454 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -7,7 +7,7 @@ org.eclipse.rdf4j rdf4j - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/pom.xml b/pom.xml index 86e2a757380..7a1279635cd 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.eclipse.rdf4j rdf4j - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT pom Eclipse RDF4J An extensible Java framework for RDF and SPARQL diff --git a/spring-components/pom.xml b/spring-components/pom.xml index 32929ebb7f6..571eebb5bce 100644 --- a/spring-components/pom.xml +++ b/spring-components/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT pom diff --git a/spring-components/rdf4j-spring-demo/pom.xml b/spring-components/rdf4j-spring-demo/pom.xml index 390e98e5193..e3376a0dd51 100644 --- a/spring-components/rdf4j-spring-demo/pom.xml +++ b/spring-components/rdf4j-spring-demo/pom.xml @@ -7,7 +7,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/spring-components/rdf4j-spring/pom.xml b/spring-components/rdf4j-spring/pom.xml index 45ce60912ea..b3616e04951 100644 --- a/spring-components/rdf4j-spring/pom.xml +++ b/spring-components/rdf4j-spring/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spring RDF4J: Spring diff --git a/spring-components/spring-boot-sparql-web/pom.xml b/spring-components/spring-boot-sparql-web/pom.xml index db184eac0ba..222e9020799 100644 --- a/spring-components/spring-boot-sparql-web/pom.xml +++ b/spring-components/spring-boot-sparql-web/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spring-boot-sparql-web RDF4J: Spring boot component for a HTTP sparql server diff --git a/testsuites/benchmark/pom.xml b/testsuites/benchmark/pom.xml index e22ec0e3be8..96c07db5f28 100644 --- a/testsuites/benchmark/pom.xml +++ b/testsuites/benchmark/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-benchmark RDF4J: benchmarks diff --git a/testsuites/geosparql/pom.xml b/testsuites/geosparql/pom.xml index 67fb0c84ad3..3ebb0735c3e 100644 --- a/testsuites/geosparql/pom.xml +++ b/testsuites/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-geosparql-testsuite RDF4J: GeoSPARQL compliance test suite diff --git a/testsuites/lucene/pom.xml b/testsuites/lucene/pom.xml index 665c3e212fb..66488c8e819 100644 --- a/testsuites/lucene/pom.xml +++ b/testsuites/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-lucene-testsuite RDF4J: Lucene Sail Tests diff --git a/testsuites/model/pom.xml b/testsuites/model/pom.xml index 96e3c745157..cc4dc7ab767 100644 --- a/testsuites/model/pom.xml +++ b/testsuites/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-testsuite RDF4J: Model API testsuite diff --git a/testsuites/pom.xml b/testsuites/pom.xml index e3074c28050..42088f21a4d 100644 --- a/testsuites/pom.xml +++ b/testsuites/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-testsuites pom diff --git a/testsuites/queryresultio/pom.xml b/testsuites/queryresultio/pom.xml index fbe56070646..feaf586b953 100644 --- a/testsuites/queryresultio/pom.xml +++ b/testsuites/queryresultio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-testsuite RDF4J: QueryResultIO testsuite diff --git a/testsuites/repository/pom.xml b/testsuites/repository/pom.xml index 3f93579f787..d58184a0a20 100644 --- a/testsuites/repository/pom.xml +++ b/testsuites/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-testsuite RDF4J: Repository API testsuite diff --git a/testsuites/rio/pom.xml b/testsuites/rio/pom.xml index 601326afe65..8bab2f967d5 100644 --- a/testsuites/rio/pom.xml +++ b/testsuites/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-testsuite RDF4J: Rio compliance test suite diff --git a/testsuites/sail/pom.xml b/testsuites/sail/pom.xml index 18465a8b092..4b9b7d6c2d1 100644 --- a/testsuites/sail/pom.xml +++ b/testsuites/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-testsuite RDF4J: Sail API testsuite diff --git a/testsuites/sparql/pom.xml b/testsuites/sparql/pom.xml index f50f5fd058d..6d81c721943 100644 --- a/testsuites/sparql/pom.xml +++ b/testsuites/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparql-testsuite RDF4J: SPARQL compliance test suite diff --git a/tools/config/pom.xml b/tools/config/pom.xml index 2fd8c05905b..c6e819ebcc0 100644 --- a/tools/config/pom.xml +++ b/tools/config/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-config RDF4J: application configuration diff --git a/tools/console/pom.xml b/tools/console/pom.xml index 159a65fd711..a342f03ad96 100644 --- a/tools/console/pom.xml +++ b/tools/console/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-console RDF4J: Console diff --git a/tools/federation/pom.xml b/tools/federation/pom.xml index dec822a6560..5430f89d375 100644 --- a/tools/federation/pom.xml +++ b/tools/federation/pom.xml @@ -8,7 +8,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/tools/pom.xml b/tools/pom.xml index 6842c44b1f9..08778066c03 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-tools pom diff --git a/tools/runtime-osgi/pom.xml b/tools/runtime-osgi/pom.xml index 88cfc766f79..642959844af 100644 --- a/tools/runtime-osgi/pom.xml +++ b/tools/runtime-osgi/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-runtime-osgi bundle diff --git a/tools/runtime/pom.xml b/tools/runtime/pom.xml index bcc4e090bd3..ccb07aa2992 100644 --- a/tools/runtime/pom.xml +++ b/tools/runtime/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-runtime RDF4J: Runtime diff --git a/tools/server-spring/pom.xml b/tools/server-spring/pom.xml index 3c447239d45..a3cba434f29 100644 --- a/tools/server-spring/pom.xml +++ b/tools/server-spring/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-server-spring RDF4J: HTTP server - core diff --git a/tools/server/pom.xml b/tools/server/pom.xml index 72d526f0480..5e6fc517570 100644 --- a/tools/server/pom.xml +++ b/tools/server/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-server war diff --git a/tools/workbench/pom.xml b/tools/workbench/pom.xml index e5768ba9f1d..cabca3a9a48 100644 --- a/tools/workbench/pom.xml +++ b/tools/workbench/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.3-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-workbench war From 2bcf6739ed992b49890112e081935da2f171a181 Mon Sep 17 00:00:00 2001 From: Chengxu Bian Date: Fri, 7 Mar 2025 14:26:20 -0500 Subject: [PATCH 05/46] GH-5266 Fix the CI by upgrading actions/cache@v2 --- .github/workflows/develop-status.yml | 2 +- .github/workflows/main-status.yml | 2 +- .github/workflows/pr-verify.yml | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/develop-status.yml b/.github/workflows/develop-status.yml index 76301ef8fbe..193d1754297 100644 --- a/.github/workflows/develop-status.yml +++ b/.github/workflows/develop-status.yml @@ -20,7 +20,7 @@ jobs: with: java-version: ${{ matrix.jdk }} - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-jdk${{ matrix.jdk }}-maven-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/main-status.yml b/.github/workflows/main-status.yml index fdf918fb68e..aa50c4590c0 100644 --- a/.github/workflows/main-status.yml +++ b/.github/workflows/main-status.yml @@ -20,7 +20,7 @@ jobs: with: java-version: ${{ matrix.jdk }} - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-jdk${{ matrix.jdk }}-maven-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/pr-verify.yml b/.github/workflows/pr-verify.yml index c89ed189e98..16443b76af1 100644 --- a/.github/workflows/pr-verify.yml +++ b/.github/workflows/pr-verify.yml @@ -15,7 +15,7 @@ jobs: with: java-version: ${{ matrix.jdk }} - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-jdk${{ matrix.jdk }}-maven-${{ hashFiles('**/pom.xml') }} @@ -48,7 +48,7 @@ jobs: with: java-version: 11 - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-jdk11-maven-${{ hashFiles('**/pom.xml') }} @@ -81,7 +81,7 @@ jobs: with: java-version: 11 - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-jdk11-maven-${{ hashFiles('**/pom.xml') }} @@ -107,7 +107,7 @@ jobs: with: java-version: 11 - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-jdk11-maven-${{ hashFiles('**/pom.xml') }} @@ -133,7 +133,7 @@ jobs: with: java-version: 11 - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-jdk11-maven-${{ hashFiles('**/pom.xml') }} From 19364289d63f377c0a809e9c2d451ea8ec0f0d59 Mon Sep 17 00:00:00 2001 From: Chengxu Bian Date: Wed, 26 Feb 2025 23:31:33 -0500 Subject: [PATCH 06/46] GH-5260 LMDB Store: Add valueEvictionInterval config --- .../eclipse/rdf4j/sail/lmdb/ValueStore.java | 20 +++- .../sail/lmdb/config/LmdbStoreConfig.java | 28 ++++- .../sail/lmdb/config/LmdbStoreSchema.java | 6 + .../rdf4j/sail/lmdb/ValueStoreTest.java | 48 ++++++++ .../sail/lmdb/config/LmdbStoreConfigTest.java | 103 ++++++++++++++++++ 5 files changed, 198 insertions(+), 7 deletions(-) create mode 100644 core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreConfigTest.java diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java index a0ef94ce123..8d8f393c88d 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java @@ -94,8 +94,6 @@ class ValueStore extends AbstractValueFactory { private final static Logger logger = LoggerFactory.getLogger(ValueStore.class); - private static final long VALUE_EVICTION_INTERVAL = 60000; // 60 seconds - private static final byte URI_VALUE = 0x0; // 00 private static final byte LITERAL_VALUE = 0x1; // 01 @@ -186,18 +184,20 @@ class ValueStore extends AbstractValueFactory { private final ConcurrentCleaner cleaner = new ConcurrentCleaner(); + private final long valueEvictionInterval; + ValueStore(File dir, LmdbStoreConfig config) throws IOException { this.dir = dir; this.forceSync = config.getForceSync(); this.autoGrow = config.getAutoGrow(); this.mapSize = config.getValueDBSize(); + this.valueEvictionInterval = config.getValueEvictionInterval(); open(); valueCache = new LmdbValue[config.getValueCacheSize()]; valueIDCache = new ConcurrentCache<>(config.getValueIDCacheSize()); namespaceCache = new ConcurrentCache<>(config.getNamespaceCacheSize()); namespaceIDCache = new ConcurrentCache<>(config.getNamespaceIDCacheSize()); - setNewRevision(); // read maximum id from store @@ -935,6 +935,10 @@ private static boolean isCommonVocabulary(IRI nv) { } public void gcIds(Collection ids, Collection nextIds) throws IOException { + if (!enableGC()) { + return; + } + if (!ids.isEmpty()) { // wrap into read txn as resizeMap expects an active surrounding read txn readTransaction(env, (stack1, txn1) -> { @@ -967,9 +971,9 @@ public void gcIds(Collection ids, Collection nextIds) throws IOExcep deleteValueToIdMappings(stack, writeTxn, finalIds, finalNextIds); - invalidateRevisionOnCommit = true; + invalidateRevisionOnCommit = enableGC(); if (nextValueEvictionTime < 0) { - nextValueEvictionTime = System.currentTimeMillis() + VALUE_EVICTION_INTERVAL; + nextValueEvictionTime = System.currentTimeMillis() + this.valueEvictionInterval; } return null; }); @@ -1180,7 +1184,7 @@ void endTransaction(boolean commit) throws IOException { unusedRevisionIds.add(revisionId); } if (nextValueEvictionTime < 0) { - nextValueEvictionTime = System.currentTimeMillis() + VALUE_EVICTION_INTERVAL; + nextValueEvictionTime = System.currentTimeMillis() + this.valueEvictionInterval; } }); setNewRevision(); @@ -1614,6 +1618,10 @@ public LmdbLiteral getLmdbLiteral(Literal l) { } } + private boolean enableGC() { + return this.valueEvictionInterval > 0; + } + public void forceEvictionOfValues() { nextValueEvictionTime = 0L; } diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreConfig.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreConfig.java index 572008314e5..4c072e91317 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreConfig.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreConfig.java @@ -10,6 +10,8 @@ *******************************************************************************/ package org.eclipse.rdf4j.sail.lmdb.config; +import java.time.Duration; + import org.eclipse.rdf4j.model.Model; import org.eclipse.rdf4j.model.Resource; import org.eclipse.rdf4j.model.ValueFactory; @@ -71,6 +73,8 @@ public class LmdbStoreConfig extends BaseSailConfig { private boolean autoGrow = true; + private long valueEvictionInterval = Duration.ofSeconds(60).toMillis(); + /*--------------* * Constructors * *--------------*/ @@ -92,7 +96,6 @@ public LmdbStoreConfig(String tripleIndexes, boolean forceSync) { /*---------* * Methods * *---------*/ - public String getTripleIndexes() { return tripleIndexes; } @@ -178,6 +181,15 @@ public LmdbStoreConfig setAutoGrow(boolean autoGrow) { return this; } + public long getValueEvictionInterval() { + return valueEvictionInterval; + } + + public LmdbStoreConfig setValueEvictionInterval(long valueEvictionInterval) { + this.valueEvictionInterval = valueEvictionInterval; + return this; + } + @Override public Resource export(Model m) { Resource implNode = super.export(m); @@ -211,6 +223,9 @@ public Resource export(Model m) { if (!autoGrow) { m.add(implNode, LmdbStoreSchema.AUTO_GROW, vf.createLiteral(false)); } + if (valueEvictionInterval != Duration.ofSeconds(60).toMillis()) { + m.add(implNode, LmdbStoreSchema.VALUE_EVICTION_INTERVAL, vf.createLiteral(valueEvictionInterval)); + } return implNode; } @@ -304,6 +319,17 @@ public void parse(Model m, Resource implNode) throws SailConfigException { "Boolean value required for " + LmdbStoreSchema.AUTO_GROW + " property, found " + lit); } }); + + Models.objectLiteral(m.getStatements(implNode, LmdbStoreSchema.VALUE_EVICTION_INTERVAL, null)) + .ifPresent(lit -> { + try { + setValueEvictionInterval(lit.longValue()); + } catch (NumberFormatException e) { + throw new SailConfigException( + "Long value required for " + LmdbStoreSchema.VALUE_EVICTION_INTERVAL + + " property, found " + lit); + } + }); } catch (ModelException e) { throw new SailConfigException(e.getMessage(), e); } diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreSchema.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreSchema.java index 63c28cfa018..8a9c5acca8d 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreSchema.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreSchema.java @@ -71,6 +71,11 @@ public class LmdbStoreSchema { */ public final static IRI AUTO_GROW; + /** + * http://rdf4j.org/config/sail/lmdb#valueEvictionInterval + */ + public final static IRI VALUE_EVICTION_INTERVAL; + static { ValueFactory factory = SimpleValueFactory.getInstance(); TRIPLE_INDEXES = factory.createIRI(NAMESPACE, "tripleIndexes"); @@ -82,5 +87,6 @@ public class LmdbStoreSchema { NAMESPACE_CACHE_SIZE = factory.createIRI(NAMESPACE, "namespaceCacheSize"); NAMESPACE_ID_CACHE_SIZE = factory.createIRI(NAMESPACE, "namespaceIDCacheSize"); AUTO_GROW = factory.createIRI(NAMESPACE, "autoGrow"); + VALUE_EVICTION_INTERVAL = factory.createIRI(NAMESPACE, "valueEvictionInterval"); } } diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/ValueStoreTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/ValueStoreTest.java index fff2bf17b11..8850b889c5b 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/ValueStoreTest.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/ValueStoreTest.java @@ -221,6 +221,54 @@ public void testGcURIs() throws Exception { } } + @Test + public void testDisableGc() throws Exception { + final Random random = new Random(1337); + final LmdbValue values[] = new LmdbValue[1000]; + + final ValueStore valueStore = new ValueStore( + new File(dataDir, "values"), new LmdbStoreConfig().setValueEvictionInterval(-1)); + + valueStore.startTransaction(true); + for (int i = 0; i < values.length; i++) { + values[i] = valueStore.createLiteral("This is a random literal:" + random.nextLong()); + valueStore.storeValue(values[i]); + } + valueStore.commit(); + + final ValueStoreRevision revBefore = valueStore.getRevision(); + + valueStore.startTransaction(true); + Set ids = new HashSet<>(); + for (int i = 0; i < 30; i++) { + ids.add(values[i].getInternalID()); + } + valueStore.gcIds(ids, new HashSet<>()); + valueStore.commit(); + + final ValueStoreRevision revAfter = valueStore.getRevision(); + + assertEquals("revisions must NOT change since GC is disabled", revBefore, revAfter); + + Arrays.fill(values, null); + valueStore.unusedRevisionIds.add(revBefore.getRevisionId()); + + valueStore.forceEvictionOfValues(); + valueStore.startTransaction(true); + valueStore.commit(); + + valueStore.startTransaction(true); + for (int i = 0; i < 30; i++) { + LmdbValue value = valueStore.createLiteral("This is a random literal:" + random.nextLong()); + values[i] = value; + valueStore.storeValue(value); + ids.remove(value.getInternalID()); + } + valueStore.commit(); + + assertNotEquals("IDs should NOT have been reused since GC is disabled", Collections.emptySet(), ids); + } + @AfterEach public void after() throws Exception { valueStore.close(); diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreConfigTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreConfigTest.java new file mode 100644 index 00000000000..6bffee78436 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreConfigTest.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb.config; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.rdf4j.model.util.Values.bnode; +import static org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig.VALUE_CACHE_SIZE; + +import org.eclipse.rdf4j.model.BNode; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Literal; +import org.eclipse.rdf4j.model.Model; +import org.eclipse.rdf4j.model.Resource; +import org.eclipse.rdf4j.model.impl.LinkedHashModel; +import org.eclipse.rdf4j.model.util.ModelBuilder; +import org.eclipse.rdf4j.model.util.Values; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class LmdbStoreConfigTest { + + @ParameterizedTest + @ValueSource(longs = { 1, 205454, 0, -1231 }) + void testThatLmdbStoreConfigParseAndExportValueEvictionInterval(final long valueEvictionInterval) { + testParseAndExport( + LmdbStoreSchema.VALUE_EVICTION_INTERVAL, + Values.literal(valueEvictionInterval), + LmdbStoreConfig::getValueEvictionInterval, + valueEvictionInterval, + true + ); + } + + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testThatLmdbStoreConfigParseAndExportAutoGrow(final boolean autoGrow) { + testParseAndExport( + LmdbStoreSchema.AUTO_GROW, + Values.literal(autoGrow), + LmdbStoreConfig::getAutoGrow, + autoGrow, + !autoGrow + ); + } + + @ParameterizedTest + @ValueSource(ints = { 1, 205454, 0, -1231 }) + void testThatLmdbStoreConfigParseAndExportValueCacheSize(final int valueCacheSize) { + testParseAndExport( + LmdbStoreSchema.VALUE_CACHE_SIZE, + Values.literal(valueCacheSize >= 0 ? valueCacheSize : VALUE_CACHE_SIZE), + LmdbStoreConfig::getValueCacheSize, + valueCacheSize >= 0 ? valueCacheSize : VALUE_CACHE_SIZE, + true + ); + } + + // TODO: Add more tests for other properties + + /** + * Generic method to test parsing and exporting of config properties. + * + * @param property The schema property to test + * @param value The literal value to use in the test + * @param getter Function to get the value from the config object + * @param expectedValue The expected value after parsing + * @param expectedContains The expected result of the contains check + * @param The type of the value being tested + */ + private void testParseAndExport( + IRI property, + Literal value, + java.util.function.Function getter, + T expectedValue, + boolean expectedContains + ) { + final BNode implNode = bnode(); + final LmdbStoreConfig lmdbStoreConfig = new LmdbStoreConfig(); + final Model configModel = new ModelBuilder() + .add(implNode, property, value) + .build(); + + // Parse the config + lmdbStoreConfig.parse(configModel, implNode); + assertThat(getter.apply(lmdbStoreConfig)).isEqualTo(expectedValue); + + // Export the config + final Model exportedModel = new LinkedHashModel(); + final Resource exportImplNode = lmdbStoreConfig.export(exportedModel); + + // Verify the export + assertThat(exportedModel.contains(exportImplNode, property, value)) + .isEqualTo(expectedContains); + } +} \ No newline at end of file From 04cc815ba05a48912bba27c07afdc4ec960d530d Mon Sep 17 00:00:00 2001 From: qaate47 Date: Wed, 26 Mar 2025 13:47:53 +0100 Subject: [PATCH 07/46] GH-5277 Add check for geospatial lucene answers --- .../function/geosparql/SpatialSupport.java | 8 +- core/sail/lucene-api/pom.xml | 5 ++ .../sail/lucene/AbstractSearchIndex.java | 77 +++++++++++++++- .../rdf4j/sail/lucene/impl/LuceneGeoTest.java | 89 +++++++++++++++++++ 4 files changed, 172 insertions(+), 7 deletions(-) create mode 100644 core/sail/lucene/src/test/java/org/eclipse/rdf4j/sail/lucene/impl/LuceneGeoTest.java diff --git a/core/queryalgebra/geosparql/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/geosparql/SpatialSupport.java b/core/queryalgebra/geosparql/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/geosparql/SpatialSupport.java index f3862b59a52..5d707476944 100644 --- a/core/queryalgebra/geosparql/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/geosparql/SpatialSupport.java +++ b/core/queryalgebra/geosparql/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/geosparql/SpatialSupport.java @@ -27,7 +27,7 @@ *
  • a WktWriter that only supports points
  • . * */ -abstract class SpatialSupport { +public abstract class SpatialSupport { private static final SpatialContext spatialContext; @@ -50,15 +50,15 @@ abstract class SpatialSupport { wktWriter = support.createWktWriter(); } - static SpatialContext getSpatialContext() { + public static SpatialContext getSpatialContext() { return spatialContext; } - static SpatialAlgebra getSpatialAlgebra() { + public static SpatialAlgebra getSpatialAlgebra() { return spatialAlgebra; } - static WktWriter getWktWriter() { + public static WktWriter getWktWriter() { return wktWriter; } diff --git a/core/sail/lucene-api/pom.xml b/core/sail/lucene-api/pom.xml index a52d440fc00..3fb152fa5f1 100644 --- a/core/sail/lucene-api/pom.xml +++ b/core/sail/lucene-api/pom.xml @@ -20,6 +20,11 @@ rdf4j-queryalgebra-evaluation ${project.version}
    + + ${project.groupId} + rdf4j-queryalgebra-geosparql + ${project.version} + ${project.groupId} rdf4j-repository-sail diff --git a/core/sail/lucene-api/src/main/java/org/eclipse/rdf4j/sail/lucene/AbstractSearchIndex.java b/core/sail/lucene-api/src/main/java/org/eclipse/rdf4j/sail/lucene/AbstractSearchIndex.java index ba3a7f3d35c..17e16730f89 100644 --- a/core/sail/lucene-api/src/main/java/org/eclipse/rdf4j/sail/lucene/AbstractSearchIndex.java +++ b/core/sail/lucene-api/src/main/java/org/eclipse/rdf4j/sail/lucene/AbstractSearchIndex.java @@ -32,6 +32,7 @@ import org.eclipse.rdf4j.model.Resource; import org.eclipse.rdf4j.model.Statement; import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.model.impl.BooleanLiteral; import org.eclipse.rdf4j.model.impl.SimpleValueFactory; import org.eclipse.rdf4j.model.vocabulary.GEO; @@ -41,10 +42,13 @@ import org.eclipse.rdf4j.query.MalformedQueryException; import org.eclipse.rdf4j.query.algebra.Var; import org.eclipse.rdf4j.query.algebra.evaluation.QueryBindingSet; +import org.eclipse.rdf4j.query.algebra.evaluation.function.geosparql.SpatialAlgebra; +import org.eclipse.rdf4j.query.algebra.evaluation.function.geosparql.SpatialSupport; import org.eclipse.rdf4j.sail.Sail; import org.eclipse.rdf4j.sail.SailException; import org.eclipse.rdf4j.sail.lucene.util.MapOfListMaps; import org.locationtech.spatial4j.context.SpatialContext; +import org.locationtech.spatial4j.io.ShapeReader; import org.locationtech.spatial4j.shape.Point; import org.locationtech.spatial4j.shape.Shape; import org.slf4j.Logger; @@ -855,6 +859,59 @@ private Iterable evaluateQuery(GeoRelationQuerySpec qu return hits; } + private IRI toSpatialOp(String relation) { + if (GEOF.SF_INTERSECTS.stringValue().equals(relation)) { + return GEOF.SF_INTERSECTS; + } else if (GEOF.SF_DISJOINT.stringValue().equals(relation)) { + return GEOF.SF_DISJOINT; + } else if (GEOF.SF_EQUALS.stringValue().equals(relation)) { + return GEOF.SF_EQUALS; + } else if (GEOF.SF_OVERLAPS.stringValue().equals(relation)) { + return GEOF.SF_OVERLAPS; + } else if (GEOF.EH_COVERED_BY.stringValue().equals(relation)) { + return GEOF.SF_WITHIN; + } else if (GEOF.EH_COVERS.stringValue().equals(relation)) { + return GEOF.EH_CONTAINS; + } else if (GEOF.SF_WITHIN.stringValue().equals(relation)) { + return GEOF.SF_WITHIN; + } else if (GEOF.EH_CONTAINS.stringValue().equals(relation)) { + return GEOF.EH_CONTAINS; + } + return null; + } + + private Shape readShape(String geo) { + ShapeReader reader = SpatialSupport.getSpatialContext().getFormats().getWktReader(); + try { + return reader.read(geo); + } catch (IOException | ParseException e) { + throw new SailException("Can't read geo wkt shape", e); + } + } + + private boolean checkSpatialOp(IRI op, Shape arg1, Shape arg2) { + SpatialAlgebra algebra = SpatialSupport.getSpatialAlgebra(); + if (op == GEOF.SF_INTERSECTS) { + return algebra.sfIntersects(arg1, arg2); + } + if (op == GEOF.SF_DISJOINT) { + return algebra.sfDisjoint(arg1, arg2); + } + if (op == GEOF.SF_EQUALS) { + return algebra.sfEquals(arg1, arg2); + } + if (op == GEOF.SF_OVERLAPS) { + return algebra.sfOverlaps(arg1, arg2); + } + if (op == GEOF.SF_WITHIN) { + return algebra.sfWithin(arg1, arg2); + } + if (op == GEOF.EH_CONTAINS) { + return algebra.ehContains(arg1, arg2); + } else + throw new SailException(new IllegalArgumentException("bad spatial op : " + op)); + } + private BindingSetCollection generateBindingSets(GeoRelationQuerySpec query, Iterable hits) throws SailException { // Since one resource can be returned many times, it can lead now to @@ -893,8 +950,25 @@ private BindingSetCollection generateBindingSets(GeoRelationQuerySpec query, } List geometries = doc.getProperty(SearchFields.getPropertyField(query.getGeoProperty())); + boolean needValidation = geometries.size() != 1; + IRI spatialOp = null; + Shape funcGeo = null; + if (needValidation) { + spatialOp = toSpatialOp(query.getRelation()); + funcGeo = readShape(query.getQueryGeometry().getLabel()); + } for (String geometry : geometries) { QueryBindingSet derivedBindings = new QueryBindingSet(); + if (geoVar != null) { + if (needValidation && spatialOp != null) { + Shape geo = readShape(geometry); + + if (!checkSpatialOp(spatialOp, funcGeo, geo)) { + continue; // not inside the asked shape + } + } + derivedBindings.addBinding(geoVar, SearchFields.wktToLiteral(geometry)); + } if (subjVar != null) { Resource resource = getResource(doc); derivedBindings.addBinding(subjVar, resource); @@ -905,9 +979,6 @@ private BindingSetCollection generateBindingSets(GeoRelationQuerySpec query, derivedBindings.addBinding(contextVar.getName(), ctx); } } - if (geoVar != null) { - derivedBindings.addBinding(geoVar, SearchFields.wktToLiteral(geometry)); - } if (fVar != null) { derivedBindings.addBinding(fVar, BooleanLiteral.TRUE); } diff --git a/core/sail/lucene/src/test/java/org/eclipse/rdf4j/sail/lucene/impl/LuceneGeoTest.java b/core/sail/lucene/src/test/java/org/eclipse/rdf4j/sail/lucene/impl/LuceneGeoTest.java new file mode 100644 index 00000000000..cc7f38c227e --- /dev/null +++ b/core/sail/lucene/src/test/java/org/eclipse/rdf4j/sail/lucene/impl/LuceneGeoTest.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lucene.impl; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.io.StringReader; +import java.util.stream.Collectors; + +import org.apache.lucene.document.Document; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.IndexableField; +import org.eclipse.rdf4j.model.base.CoreDatatype; +import org.eclipse.rdf4j.model.util.Values; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.TupleQueryResult; +import org.eclipse.rdf4j.repository.sail.SailRepository; +import org.eclipse.rdf4j.repository.util.Repositories; +import org.eclipse.rdf4j.rio.RDFFormat; +import org.eclipse.rdf4j.sail.lucene.LuceneSail; +import org.eclipse.rdf4j.sail.memory.MemoryStore; +import org.junit.jupiter.api.Test; + +public class LuceneGeoTest { + @Test + public void geoFailTest() { + MemoryStore store = new MemoryStore(); + LuceneSail lucene = new LuceneSail(); + lucene.setParameter(LuceneSail.LUCENE_RAMDIR_KEY, "true"); + lucene.setParameter(LuceneSail.WKT_FIELDS, "https://example.org/#location"); + lucene.setBaseSail(store); + SailRepository repo = new SailRepository(lucene); + try { + repo.init(); + + Repositories.consume(repo, conn -> { + try { + conn.add(new StringReader( + "@prefix ex: ." + // point in + + "ex:s ex:location \"POINT(9.6929555 45.6762274)\"^^ ." + // point out + + "ex:s ex:location \"POINT(9.18457 45.466873)\"^^ ." + ), "https://example.org/#", RDFFormat.TURTLE); + } catch (IOException e) { + throw new AssertionError(e); + } + }); + + lucene.reindex(); + + // a random polygon of Milan + // POLYGON((9.000892639160158 45.3796432523812,9.381294250488283 45.3796432523812,9.381294250488283 + // 45.55420812072298,9.000892639160158 45.55420812072298,9.000892639160158 45.3796432523812)) + Repositories.consumeNoTransaction(repo, conn -> { + try (TupleQueryResult res = conn.prepareTupleQuery( + "PREFIX ex: " + + "SELECT * { " + + " ?s ex:location ?loc " + + " FILTER ((\"POLYGON((9.000892639160158 45.3796432523812,9.381294250488283 45.3796432523812,9.381294250488283 45.55420812072298,9.000892639160158 45.55420812072298,9.000892639160158 45.3796432523812))\"^^, ?loc)) " + + "} " + ).evaluate()) { + assertTrue(res.hasNext(), "missing good value"); + BindingSet next = res.next(); + assertEquals(Values.iri("https://example.org/#s"), next.getValue("s")); + assertEquals(Values.literal("POINT(9.18457 45.466873)", CoreDatatype.GEO.WKT_LITERAL), + next.getValue("loc")); + assertFalse(res.hasNext(), "more value(s) :" + + res.stream().map(Object::toString).collect(Collectors.joining("\n", "\n", ""))); + } + }); + + } finally { + repo.shutDown(); + } + + } +} From 6f49cf6f64436d386b1079c33cb22f1040094704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Tue, 15 Apr 2025 15:12:28 +0200 Subject: [PATCH 08/46] set correct version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Håvard Ottestad --- assembly-descriptors/pom.xml | 2 +- assembly/pom.xml | 2 +- bom/pom.xml | 2 +- compliance/elasticsearch/pom.xml | 2 +- compliance/geosparql/pom.xml | 2 +- compliance/lucene/pom.xml | 2 +- compliance/model/pom.xml | 2 +- compliance/pom.xml | 2 +- compliance/repository/pom.xml | 2 +- compliance/rio/pom.xml | 2 +- compliance/solr/pom.xml | 2 +- compliance/sparql/pom.xml | 2 +- core/client/pom.xml | 2 +- core/collection-factory/api/pom.xml | 2 +- core/collection-factory/mapdb/pom.xml | 2 +- core/collection-factory/mapdb3/pom.xml | 2 +- core/collection-factory/pom.xml | 2 +- core/common/annotation/pom.xml | 2 +- core/common/exception/pom.xml | 2 +- core/common/io/pom.xml | 2 +- core/common/iterator/pom.xml | 2 +- core/common/order/pom.xml | 2 +- core/common/pom.xml | 2 +- core/common/text/pom.xml | 2 +- core/common/transaction/pom.xml | 2 +- core/common/xml/pom.xml | 2 +- core/http/client/pom.xml | 2 +- core/http/pom.xml | 2 +- core/http/protocol/pom.xml | 2 +- core/model-api/pom.xml | 2 +- core/model-vocabulary/pom.xml | 2 +- core/model/pom.xml | 2 +- core/pom.xml | 2 +- core/query/pom.xml | 2 +- core/queryalgebra/evaluation/pom.xml | 2 +- core/queryalgebra/geosparql/pom.xml | 2 +- core/queryalgebra/model/pom.xml | 2 +- core/queryalgebra/pom.xml | 2 +- core/queryparser/api/pom.xml | 2 +- core/queryparser/pom.xml | 2 +- core/queryparser/sparql/pom.xml | 2 +- core/queryrender/pom.xml | 2 +- core/queryresultio/api/pom.xml | 2 +- core/queryresultio/binary/pom.xml | 2 +- core/queryresultio/pom.xml | 2 +- core/queryresultio/sparqljson/pom.xml | 2 +- core/queryresultio/sparqlxml/pom.xml | 2 +- core/queryresultio/text/pom.xml | 2 +- core/repository/api/pom.xml | 2 +- core/repository/contextaware/pom.xml | 2 +- core/repository/dataset/pom.xml | 2 +- core/repository/event/pom.xml | 2 +- core/repository/http/pom.xml | 2 +- core/repository/manager/pom.xml | 2 +- core/repository/pom.xml | 2 +- core/repository/sail/pom.xml | 2 +- core/repository/sparql/pom.xml | 2 +- core/rio/api/pom.xml | 2 +- core/rio/binary/pom.xml | 2 +- core/rio/datatypes/pom.xml | 2 +- core/rio/hdt/pom.xml | 2 +- core/rio/jsonld-legacy/pom.xml | 2 +- core/rio/jsonld/pom.xml | 2 +- core/rio/languages/pom.xml | 2 +- core/rio/n3/pom.xml | 2 +- core/rio/nquads/pom.xml | 2 +- core/rio/ntriples/pom.xml | 2 +- core/rio/pom.xml | 2 +- core/rio/rdfjson/pom.xml | 2 +- core/rio/rdfxml/pom.xml | 2 +- core/rio/trig/pom.xml | 2 +- core/rio/trix/pom.xml | 2 +- core/rio/turtle/pom.xml | 2 +- core/sail/api/pom.xml | 2 +- core/sail/base/pom.xml | 2 +- core/sail/elasticsearch-store/pom.xml | 2 +- core/sail/elasticsearch/pom.xml | 2 +- core/sail/extensible-store/pom.xml | 2 +- core/sail/inferencer/pom.xml | 2 +- core/sail/lmdb/pom.xml | 2 +- core/sail/lucene-api/pom.xml | 2 +- core/sail/lucene/pom.xml | 2 +- core/sail/memory/pom.xml | 2 +- core/sail/model/pom.xml | 2 +- core/sail/nativerdf/pom.xml | 2 +- core/sail/pom.xml | 2 +- core/sail/shacl/pom.xml | 2 +- core/sail/solr/pom.xml | 2 +- core/sparqlbuilder/pom.xml | 2 +- core/spin/pom.xml | 2 +- core/storage/pom.xml | 2 +- examples/pom.xml | 2 +- pom.xml | 2 +- spring-components/pom.xml | 2 +- spring-components/rdf4j-spring-demo/pom.xml | 2 +- spring-components/rdf4j-spring/pom.xml | 2 +- spring-components/spring-boot-sparql-web/pom.xml | 2 +- testsuites/benchmark/pom.xml | 2 +- testsuites/geosparql/pom.xml | 2 +- testsuites/lucene/pom.xml | 2 +- testsuites/model/pom.xml | 2 +- testsuites/pom.xml | 2 +- testsuites/queryresultio/pom.xml | 2 +- testsuites/repository/pom.xml | 2 +- testsuites/rio/pom.xml | 2 +- testsuites/sail/pom.xml | 2 +- testsuites/sparql/pom.xml | 2 +- tools/config/pom.xml | 2 +- tools/console/pom.xml | 2 +- tools/federation/pom.xml | 2 +- tools/pom.xml | 2 +- tools/runtime-osgi/pom.xml | 2 +- tools/runtime/pom.xml | 2 +- tools/server-spring/pom.xml | 2 +- tools/server/pom.xml | 2 +- tools/workbench/pom.xml | 2 +- 116 files changed, 116 insertions(+), 116 deletions(-) diff --git a/assembly-descriptors/pom.xml b/assembly-descriptors/pom.xml index fbad324d2c2..adb16157dc8 100644 --- a/assembly-descriptors/pom.xml +++ b/assembly-descriptors/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-assembly-descriptors RDF4J: Assembly Descriptors diff --git a/assembly/pom.xml b/assembly/pom.xml index 3309c013327..a88d567f0e5 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-assembly pom diff --git a/bom/pom.xml b/bom/pom.xml index bc891c506f8..8056082ccc1 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-bom pom diff --git a/compliance/elasticsearch/pom.xml b/compliance/elasticsearch/pom.xml index a404fc5412d..794dacb7bd7 100644 --- a/compliance/elasticsearch/pom.xml +++ b/compliance/elasticsearch/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-elasticsearch-compliance RDF4J: Elasticsearch Sail Tests diff --git a/compliance/geosparql/pom.xml b/compliance/geosparql/pom.xml index b7a5b7f0427..41e8785595b 100644 --- a/compliance/geosparql/pom.xml +++ b/compliance/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-geosparql-compliance RDF4J: GeoSPARQL compliance tests diff --git a/compliance/lucene/pom.xml b/compliance/lucene/pom.xml index c01f297a7a7..dcf84054c6d 100644 --- a/compliance/lucene/pom.xml +++ b/compliance/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-lucene-compliance RDF4J: Lucene Sail Tests diff --git a/compliance/model/pom.xml b/compliance/model/pom.xml index 0bf24514625..3d3ac105fa9 100644 --- a/compliance/model/pom.xml +++ b/compliance/model/pom.xml @@ -3,7 +3,7 @@ rdf4j-compliance org.eclipse.rdf4j - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT 4.0.0 rdf4j-model-compliance diff --git a/compliance/pom.xml b/compliance/pom.xml index 7b2f5c5e79c..7a3f7a3e7ff 100644 --- a/compliance/pom.xml +++ b/compliance/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-compliance pom diff --git a/compliance/repository/pom.xml b/compliance/repository/pom.xml index b175b531c54..069ade53eec 100644 --- a/compliance/repository/pom.xml +++ b/compliance/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-compliance war diff --git a/compliance/rio/pom.xml b/compliance/rio/pom.xml index d2a9489a45a..850062bf2f4 100644 --- a/compliance/rio/pom.xml +++ b/compliance/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-compliance RDF4J: Rio compliance tests diff --git a/compliance/solr/pom.xml b/compliance/solr/pom.xml index 0184b88e858..454f5295ca6 100644 --- a/compliance/solr/pom.xml +++ b/compliance/solr/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-solr-compliance RDF4J: Solr Sail Tests diff --git a/compliance/sparql/pom.xml b/compliance/sparql/pom.xml index 9ede962f426..3340661c18d 100644 --- a/compliance/sparql/pom.xml +++ b/compliance/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparql-compliance war diff --git a/core/client/pom.xml b/core/client/pom.xml index aec29ad1591..845360a26cc 100644 --- a/core/client/pom.xml +++ b/core/client/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-client RDF4J: Client Libraries diff --git a/core/collection-factory/api/pom.xml b/core/collection-factory/api/pom.xml index 2fcca4969ba..5ba6df646b7 100644 --- a/core/collection-factory/api/pom.xml +++ b/core/collection-factory/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-api RDF4J: Collection Factory - API diff --git a/core/collection-factory/mapdb/pom.xml b/core/collection-factory/mapdb/pom.xml index c59a18b7bd9..c8e843b984e 100644 --- a/core/collection-factory/mapdb/pom.xml +++ b/core/collection-factory/mapdb/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-mapdb RDF4J: Collection Factory - Map DB backed diff --git a/core/collection-factory/mapdb3/pom.xml b/core/collection-factory/mapdb3/pom.xml index 56dd51967c5..a00d4c8e811 100644 --- a/core/collection-factory/mapdb3/pom.xml +++ b/core/collection-factory/mapdb3/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-mapdb3 RDF4J: Collection Factory - Map DB v3 backed diff --git a/core/collection-factory/pom.xml b/core/collection-factory/pom.xml index 75ef4066211..bb1df7bb226 100644 --- a/core/collection-factory/pom.xml +++ b/core/collection-factory/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory pom diff --git a/core/common/annotation/pom.xml b/core/common/annotation/pom.xml index 53eabf03406..f5f53a65e21 100644 --- a/core/common/annotation/pom.xml +++ b/core/common/annotation/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-annotation RDF4J: common annotation diff --git a/core/common/exception/pom.xml b/core/common/exception/pom.xml index 457f5f2ee60..bbd533f1adf 100644 --- a/core/common/exception/pom.xml +++ b/core/common/exception/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-exception RDF4J: common exception diff --git a/core/common/io/pom.xml b/core/common/io/pom.xml index e796429c4a0..f99f7f2af4e 100644 --- a/core/common/io/pom.xml +++ b/core/common/io/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-io RDF4J: common IO diff --git a/core/common/iterator/pom.xml b/core/common/iterator/pom.xml index 849c25209c2..c8b62012574 100644 --- a/core/common/iterator/pom.xml +++ b/core/common/iterator/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-iterator RDF4J: common iterators diff --git a/core/common/order/pom.xml b/core/common/order/pom.xml index df46ede44ff..5228081fc0e 100644 --- a/core/common/order/pom.xml +++ b/core/common/order/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-order RDF4J: common order diff --git a/core/common/pom.xml b/core/common/pom.xml index 212788ac72d..fc821867500 100644 --- a/core/common/pom.xml +++ b/core/common/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common pom diff --git a/core/common/text/pom.xml b/core/common/text/pom.xml index 026ab778c27..f26391a3d11 100644 --- a/core/common/text/pom.xml +++ b/core/common/text/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-text RDF4J: common text diff --git a/core/common/transaction/pom.xml b/core/common/transaction/pom.xml index 9d654132f03..2a327eba54c 100644 --- a/core/common/transaction/pom.xml +++ b/core/common/transaction/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-transaction RDF4J: common transaction diff --git a/core/common/xml/pom.xml b/core/common/xml/pom.xml index f7ce64d82d3..76184d6e127 100644 --- a/core/common/xml/pom.xml +++ b/core/common/xml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-xml RDF4J: common XML diff --git a/core/http/client/pom.xml b/core/http/client/pom.xml index fae12d08c2d..8e26e6c8d3f 100644 --- a/core/http/client/pom.xml +++ b/core/http/client/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-http - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-client RDF4J: HTTP client diff --git a/core/http/pom.xml b/core/http/pom.xml index d44289c40d8..ee3e0261ad0 100644 --- a/core/http/pom.xml +++ b/core/http/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http pom diff --git a/core/http/protocol/pom.xml b/core/http/protocol/pom.xml index eaf4b204c39..a5d66a693e0 100644 --- a/core/http/protocol/pom.xml +++ b/core/http/protocol/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-http - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-protocol RDF4J: HTTP protocol diff --git a/core/model-api/pom.xml b/core/model-api/pom.xml index 70610307aa7..cf07e38798c 100644 --- a/core/model-api/pom.xml +++ b/core/model-api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-api RDF4J: Model API diff --git a/core/model-vocabulary/pom.xml b/core/model-vocabulary/pom.xml index f2c5f5cce5d..bd27791c1d8 100644 --- a/core/model-vocabulary/pom.xml +++ b/core/model-vocabulary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-vocabulary RDF4J: RDF Vocabularies diff --git a/core/model/pom.xml b/core/model/pom.xml index 412b4dd1dd8..17a4deb868b 100644 --- a/core/model/pom.xml +++ b/core/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model RDF4J: Model diff --git a/core/pom.xml b/core/pom.xml index 16b6b9890e6..ed94faedcde 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-core pom diff --git a/core/query/pom.xml b/core/query/pom.xml index c5821745f30..6600bd51a4e 100644 --- a/core/query/pom.xml +++ b/core/query/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-query RDF4J: Query diff --git a/core/queryalgebra/evaluation/pom.xml b/core/queryalgebra/evaluation/pom.xml index ed53f3ab95f..a29c5f1ab29 100644 --- a/core/queryalgebra/evaluation/pom.xml +++ b/core/queryalgebra/evaluation/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-evaluation RDF4J: Query algebra - evaluation diff --git a/core/queryalgebra/geosparql/pom.xml b/core/queryalgebra/geosparql/pom.xml index 37428834344..10fcbc837ce 100644 --- a/core/queryalgebra/geosparql/pom.xml +++ b/core/queryalgebra/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-geosparql RDF4J: Query algebra - GeoSPARQL diff --git a/core/queryalgebra/model/pom.xml b/core/queryalgebra/model/pom.xml index 3f967631390..c561441a066 100644 --- a/core/queryalgebra/model/pom.xml +++ b/core/queryalgebra/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-model RDF4J: Query algebra - model diff --git a/core/queryalgebra/pom.xml b/core/queryalgebra/pom.xml index 737869fd3a2..fb04d8339cd 100644 --- a/core/queryalgebra/pom.xml +++ b/core/queryalgebra/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra pom diff --git a/core/queryparser/api/pom.xml b/core/queryparser/api/pom.xml index 1f45963a645..6450c375f42 100644 --- a/core/queryparser/api/pom.xml +++ b/core/queryparser/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryparser - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser-api RDF4J: Query parser - API diff --git a/core/queryparser/pom.xml b/core/queryparser/pom.xml index ea41b358a15..0dfcb8573d3 100644 --- a/core/queryparser/pom.xml +++ b/core/queryparser/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser pom diff --git a/core/queryparser/sparql/pom.xml b/core/queryparser/sparql/pom.xml index da516ea6353..d21ecf2b1b2 100644 --- a/core/queryparser/sparql/pom.xml +++ b/core/queryparser/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryparser - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser-sparql RDF4J: Query parser - SPARQL diff --git a/core/queryrender/pom.xml b/core/queryrender/pom.xml index 220eb715ebd..8db12169a80 100644 --- a/core/queryrender/pom.xml +++ b/core/queryrender/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryrender RDF4J: Query Rendering diff --git a/core/queryresultio/api/pom.xml b/core/queryresultio/api/pom.xml index ec83dcaeafd..273fe3b9801 100644 --- a/core/queryresultio/api/pom.xml +++ b/core/queryresultio/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-api RDF4J: Query result IO - API diff --git a/core/queryresultio/binary/pom.xml b/core/queryresultio/binary/pom.xml index 5987f21e2bc..34df6c0b64f 100644 --- a/core/queryresultio/binary/pom.xml +++ b/core/queryresultio/binary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-binary RDF4J: Query result IO - binary diff --git a/core/queryresultio/pom.xml b/core/queryresultio/pom.xml index 60cb4f8fb12..c1ba95a8c53 100644 --- a/core/queryresultio/pom.xml +++ b/core/queryresultio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio pom diff --git a/core/queryresultio/sparqljson/pom.xml b/core/queryresultio/sparqljson/pom.xml index 4ca57a7e04b..c5f589b6457 100644 --- a/core/queryresultio/sparqljson/pom.xml +++ b/core/queryresultio/sparqljson/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-sparqljson RDF4J: Query result IO - SPARQL/JSON diff --git a/core/queryresultio/sparqlxml/pom.xml b/core/queryresultio/sparqlxml/pom.xml index 0674fb9c7be..8d7bee2745b 100644 --- a/core/queryresultio/sparqlxml/pom.xml +++ b/core/queryresultio/sparqlxml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-sparqlxml RDF4J: Query result IO - SPARQL/XML diff --git a/core/queryresultio/text/pom.xml b/core/queryresultio/text/pom.xml index a3582407d92..e5f67e0b32e 100644 --- a/core/queryresultio/text/pom.xml +++ b/core/queryresultio/text/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-text RDF4J: Query result IO - plain text booleans diff --git a/core/repository/api/pom.xml b/core/repository/api/pom.xml index 1262c6a2284..48fdeecfad7 100644 --- a/core/repository/api/pom.xml +++ b/core/repository/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-api RDF4J: Repository - API diff --git a/core/repository/contextaware/pom.xml b/core/repository/contextaware/pom.xml index 96105e126e0..39bcbf0668c 100644 --- a/core/repository/contextaware/pom.xml +++ b/core/repository/contextaware/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-contextaware RDF4J: Repository - context aware (wrapper) diff --git a/core/repository/dataset/pom.xml b/core/repository/dataset/pom.xml index b7d156dfef5..c843dccda42 100644 --- a/core/repository/dataset/pom.xml +++ b/core/repository/dataset/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-dataset RDF4J: DatasetRepository (wrapper) diff --git a/core/repository/event/pom.xml b/core/repository/event/pom.xml index fee7e4c2cfc..3a3109cf967 100644 --- a/core/repository/event/pom.xml +++ b/core/repository/event/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-event RDF4J: Repository - event (wrapper) diff --git a/core/repository/http/pom.xml b/core/repository/http/pom.xml index bab15a900e6..4fc07dc06e9 100644 --- a/core/repository/http/pom.xml +++ b/core/repository/http/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-http RDF4J: HTTPRepository diff --git a/core/repository/manager/pom.xml b/core/repository/manager/pom.xml index 25592296d8a..bad53d7101e 100644 --- a/core/repository/manager/pom.xml +++ b/core/repository/manager/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-manager RDF4J: Repository manager diff --git a/core/repository/pom.xml b/core/repository/pom.xml index 6ca6734d2c2..873757a9580 100644 --- a/core/repository/pom.xml +++ b/core/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository pom diff --git a/core/repository/sail/pom.xml b/core/repository/sail/pom.xml index 9a803693c51..fb8fc483b2b 100644 --- a/core/repository/sail/pom.xml +++ b/core/repository/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-sail RDF4J: SailRepository diff --git a/core/repository/sparql/pom.xml b/core/repository/sparql/pom.xml index 389da87d72d..66d5ee263ce 100644 --- a/core/repository/sparql/pom.xml +++ b/core/repository/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-sparql RDF4J: SPARQL Repository diff --git a/core/rio/api/pom.xml b/core/rio/api/pom.xml index 24b22ea5f96..3733b074423 100644 --- a/core/rio/api/pom.xml +++ b/core/rio/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-api RDF4J: Rio - API diff --git a/core/rio/binary/pom.xml b/core/rio/binary/pom.xml index 603934788af..4ff5469eddf 100644 --- a/core/rio/binary/pom.xml +++ b/core/rio/binary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-binary RDF4J: Rio - Binary diff --git a/core/rio/datatypes/pom.xml b/core/rio/datatypes/pom.xml index f0a7b1c2777..b9d9dcadcfb 100644 --- a/core/rio/datatypes/pom.xml +++ b/core/rio/datatypes/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-datatypes RDF4J: Rio - Datatypes diff --git a/core/rio/hdt/pom.xml b/core/rio/hdt/pom.xml index ddaeea5f544..f3647663a65 100644 --- a/core/rio/hdt/pom.xml +++ b/core/rio/hdt/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-hdt jar diff --git a/core/rio/jsonld-legacy/pom.xml b/core/rio/jsonld-legacy/pom.xml index 6860994b3a1..7509ef4b834 100644 --- a/core/rio/jsonld-legacy/pom.xml +++ b/core/rio/jsonld-legacy/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-jsonld-legacy RDF4J: Rio - JSON-LD 1.0 (legacy) diff --git a/core/rio/jsonld/pom.xml b/core/rio/jsonld/pom.xml index e36390a9f63..998013a10ec 100644 --- a/core/rio/jsonld/pom.xml +++ b/core/rio/jsonld/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-jsonld RDF4J: Rio - JSON-LD diff --git a/core/rio/languages/pom.xml b/core/rio/languages/pom.xml index 10e20769775..b35931702bb 100644 --- a/core/rio/languages/pom.xml +++ b/core/rio/languages/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-languages RDF4J: Rio - Languages diff --git a/core/rio/n3/pom.xml b/core/rio/n3/pom.xml index 6fc0b1db1c2..54ae5fe573c 100644 --- a/core/rio/n3/pom.xml +++ b/core/rio/n3/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-n3 RDF4J: Rio - N3 (writer-only) diff --git a/core/rio/nquads/pom.xml b/core/rio/nquads/pom.xml index 7f0541f148b..044216524fb 100644 --- a/core/rio/nquads/pom.xml +++ b/core/rio/nquads/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-nquads RDF4J: Rio - N-Quads diff --git a/core/rio/ntriples/pom.xml b/core/rio/ntriples/pom.xml index ed7212964e0..37553504ed5 100644 --- a/core/rio/ntriples/pom.xml +++ b/core/rio/ntriples/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-ntriples RDF4J: Rio - N-Triples diff --git a/core/rio/pom.xml b/core/rio/pom.xml index 7fd83942f47..77fc1cee292 100644 --- a/core/rio/pom.xml +++ b/core/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio pom diff --git a/core/rio/rdfjson/pom.xml b/core/rio/rdfjson/pom.xml index d32d799da2c..55a3e3ad374 100644 --- a/core/rio/rdfjson/pom.xml +++ b/core/rio/rdfjson/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-rdfjson RDF4J: Rio - RDF/JSON diff --git a/core/rio/rdfxml/pom.xml b/core/rio/rdfxml/pom.xml index 469db996dfd..08a1e0f6eb3 100644 --- a/core/rio/rdfxml/pom.xml +++ b/core/rio/rdfxml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-rdfxml RDF4J: Rio - RDF/XML diff --git a/core/rio/trig/pom.xml b/core/rio/trig/pom.xml index dd9c8e9edc2..2736b12a916 100644 --- a/core/rio/trig/pom.xml +++ b/core/rio/trig/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-trig RDF4J: Rio - TriG diff --git a/core/rio/trix/pom.xml b/core/rio/trix/pom.xml index cd11066001c..a8b99df7634 100644 --- a/core/rio/trix/pom.xml +++ b/core/rio/trix/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-trix RDF4J: Rio - TriX diff --git a/core/rio/turtle/pom.xml b/core/rio/turtle/pom.xml index 8588653c240..087f4221601 100644 --- a/core/rio/turtle/pom.xml +++ b/core/rio/turtle/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-turtle RDF4J: Rio - Turtle diff --git a/core/sail/api/pom.xml b/core/sail/api/pom.xml index 2fa0c1f7d6f..94ba13deba0 100644 --- a/core/sail/api/pom.xml +++ b/core/sail/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-api RDF4J: Sail API diff --git a/core/sail/base/pom.xml b/core/sail/base/pom.xml index 8c7825f6c32..37f440d24a5 100644 --- a/core/sail/base/pom.xml +++ b/core/sail/base/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-base RDF4J: Sail base implementations diff --git a/core/sail/elasticsearch-store/pom.xml b/core/sail/elasticsearch-store/pom.xml index dd6ad9d7210..8542f4a1aaa 100644 --- a/core/sail/elasticsearch-store/pom.xml +++ b/core/sail/elasticsearch-store/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-elasticsearch-store RDF4J: Elasticsearch Store diff --git a/core/sail/elasticsearch/pom.xml b/core/sail/elasticsearch/pom.xml index 4a832bf580e..022319c4697 100644 --- a/core/sail/elasticsearch/pom.xml +++ b/core/sail/elasticsearch/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-elasticsearch RDF4J: Elastic Search Sail Index diff --git a/core/sail/extensible-store/pom.xml b/core/sail/extensible-store/pom.xml index d382048565d..e8d6a1af491 100644 --- a/core/sail/extensible-store/pom.xml +++ b/core/sail/extensible-store/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-extensible-store RDF4J: Extensible Store diff --git a/core/sail/inferencer/pom.xml b/core/sail/inferencer/pom.xml index 35b629ee437..45babf4a3e2 100644 --- a/core/sail/inferencer/pom.xml +++ b/core/sail/inferencer/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-inferencer RDF4J: Inferencer Sails diff --git a/core/sail/lmdb/pom.xml b/core/sail/lmdb/pom.xml index d3afd4f7492..787fb19c4d4 100644 --- a/core/sail/lmdb/pom.xml +++ b/core/sail/lmdb/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lmdb RDF4J: LmdbStore diff --git a/core/sail/lucene-api/pom.xml b/core/sail/lucene-api/pom.xml index b14ccf4b027..a52d440fc00 100644 --- a/core/sail/lucene-api/pom.xml +++ b/core/sail/lucene-api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lucene-api RDF4J: Lucene Sail API diff --git a/core/sail/lucene/pom.xml b/core/sail/lucene/pom.xml index 95c8e110215..530c6ac3c5d 100644 --- a/core/sail/lucene/pom.xml +++ b/core/sail/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lucene RDF4J: Lucene Sail Index diff --git a/core/sail/memory/pom.xml b/core/sail/memory/pom.xml index 37eea9e43ab..ae81a8c5531 100644 --- a/core/sail/memory/pom.xml +++ b/core/sail/memory/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-memory RDF4J: MemoryStore diff --git a/core/sail/model/pom.xml b/core/sail/model/pom.xml index 872113f840f..531c473d10a 100644 --- a/core/sail/model/pom.xml +++ b/core/sail/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-model RDF4J: Sail Model diff --git a/core/sail/nativerdf/pom.xml b/core/sail/nativerdf/pom.xml index 03d6b5d455e..17dbcc1c962 100644 --- a/core/sail/nativerdf/pom.xml +++ b/core/sail/nativerdf/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-nativerdf RDF4J: NativeStore diff --git a/core/sail/pom.xml b/core/sail/pom.xml index 565c55dc83a..cbce9f38bc7 100644 --- a/core/sail/pom.xml +++ b/core/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail pom diff --git a/core/sail/shacl/pom.xml b/core/sail/shacl/pom.xml index 1a2627e09b2..9cae94fe046 100644 --- a/core/sail/shacl/pom.xml +++ b/core/sail/shacl/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-shacl RDF4J: SHACL diff --git a/core/sail/solr/pom.xml b/core/sail/solr/pom.xml index 23fc129f467..c79bfa393e7 100644 --- a/core/sail/solr/pom.xml +++ b/core/sail/solr/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-solr RDF4J: Solr Sail Index diff --git a/core/sparqlbuilder/pom.xml b/core/sparqlbuilder/pom.xml index aa63a9e70fd..2e589196e67 100644 --- a/core/sparqlbuilder/pom.xml +++ b/core/sparqlbuilder/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparqlbuilder RDF4J: SparqlBuilder diff --git a/core/spin/pom.xml b/core/spin/pom.xml index 6c85ed3b9ff..b80e9d40e04 100644 --- a/core/spin/pom.xml +++ b/core/spin/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spin RDF4J: SPIN diff --git a/core/storage/pom.xml b/core/storage/pom.xml index 745692f4cb3..6775e4bf837 100644 --- a/core/storage/pom.xml +++ b/core/storage/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-storage RDF4J: Storage Libraries diff --git a/examples/pom.xml b/examples/pom.xml index fa58b79ea64..5610e589454 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -7,7 +7,7 @@ org.eclipse.rdf4j rdf4j - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/pom.xml b/pom.xml index 4ed6c113b2f..7a1279635cd 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.eclipse.rdf4j rdf4j - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT pom Eclipse RDF4J An extensible Java framework for RDF and SPARQL diff --git a/spring-components/pom.xml b/spring-components/pom.xml index a374d168538..571eebb5bce 100644 --- a/spring-components/pom.xml +++ b/spring-components/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT pom diff --git a/spring-components/rdf4j-spring-demo/pom.xml b/spring-components/rdf4j-spring-demo/pom.xml index 4788af7cd6f..e3376a0dd51 100644 --- a/spring-components/rdf4j-spring-demo/pom.xml +++ b/spring-components/rdf4j-spring-demo/pom.xml @@ -7,7 +7,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/spring-components/rdf4j-spring/pom.xml b/spring-components/rdf4j-spring/pom.xml index 854485a4270..b3616e04951 100644 --- a/spring-components/rdf4j-spring/pom.xml +++ b/spring-components/rdf4j-spring/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spring RDF4J: Spring diff --git a/spring-components/spring-boot-sparql-web/pom.xml b/spring-components/spring-boot-sparql-web/pom.xml index f8d446d3b70..222e9020799 100644 --- a/spring-components/spring-boot-sparql-web/pom.xml +++ b/spring-components/spring-boot-sparql-web/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spring-boot-sparql-web RDF4J: Spring boot component for a HTTP sparql server diff --git a/testsuites/benchmark/pom.xml b/testsuites/benchmark/pom.xml index c7f83115e2c..96c07db5f28 100644 --- a/testsuites/benchmark/pom.xml +++ b/testsuites/benchmark/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-benchmark RDF4J: benchmarks diff --git a/testsuites/geosparql/pom.xml b/testsuites/geosparql/pom.xml index 27c4b7549a2..3ebb0735c3e 100644 --- a/testsuites/geosparql/pom.xml +++ b/testsuites/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-geosparql-testsuite RDF4J: GeoSPARQL compliance test suite diff --git a/testsuites/lucene/pom.xml b/testsuites/lucene/pom.xml index 0ce70da5639..66488c8e819 100644 --- a/testsuites/lucene/pom.xml +++ b/testsuites/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-lucene-testsuite RDF4J: Lucene Sail Tests diff --git a/testsuites/model/pom.xml b/testsuites/model/pom.xml index 74767922ab5..cc4dc7ab767 100644 --- a/testsuites/model/pom.xml +++ b/testsuites/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-testsuite RDF4J: Model API testsuite diff --git a/testsuites/pom.xml b/testsuites/pom.xml index b03b84d760e..42088f21a4d 100644 --- a/testsuites/pom.xml +++ b/testsuites/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-testsuites pom diff --git a/testsuites/queryresultio/pom.xml b/testsuites/queryresultio/pom.xml index dd97c1b216e..feaf586b953 100644 --- a/testsuites/queryresultio/pom.xml +++ b/testsuites/queryresultio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-testsuite RDF4J: QueryResultIO testsuite diff --git a/testsuites/repository/pom.xml b/testsuites/repository/pom.xml index 81a8dcf291a..d58184a0a20 100644 --- a/testsuites/repository/pom.xml +++ b/testsuites/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-testsuite RDF4J: Repository API testsuite diff --git a/testsuites/rio/pom.xml b/testsuites/rio/pom.xml index 412ec5d54cb..8bab2f967d5 100644 --- a/testsuites/rio/pom.xml +++ b/testsuites/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-testsuite RDF4J: Rio compliance test suite diff --git a/testsuites/sail/pom.xml b/testsuites/sail/pom.xml index 5cc8d7800ca..4b9b7d6c2d1 100644 --- a/testsuites/sail/pom.xml +++ b/testsuites/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-testsuite RDF4J: Sail API testsuite diff --git a/testsuites/sparql/pom.xml b/testsuites/sparql/pom.xml index d531db27201..6d81c721943 100644 --- a/testsuites/sparql/pom.xml +++ b/testsuites/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparql-testsuite RDF4J: SPARQL compliance test suite diff --git a/tools/config/pom.xml b/tools/config/pom.xml index 0a667845970..c6e819ebcc0 100644 --- a/tools/config/pom.xml +++ b/tools/config/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-config RDF4J: application configuration diff --git a/tools/console/pom.xml b/tools/console/pom.xml index 0fade44b76a..a342f03ad96 100644 --- a/tools/console/pom.xml +++ b/tools/console/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-console RDF4J: Console diff --git a/tools/federation/pom.xml b/tools/federation/pom.xml index e24e35c191b..5430f89d375 100644 --- a/tools/federation/pom.xml +++ b/tools/federation/pom.xml @@ -8,7 +8,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/tools/pom.xml b/tools/pom.xml index eb4d6ecad5d..08778066c03 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-tools pom diff --git a/tools/runtime-osgi/pom.xml b/tools/runtime-osgi/pom.xml index 6f4c95fa88f..642959844af 100644 --- a/tools/runtime-osgi/pom.xml +++ b/tools/runtime-osgi/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-runtime-osgi bundle diff --git a/tools/runtime/pom.xml b/tools/runtime/pom.xml index ed91424ba74..ccb07aa2992 100644 --- a/tools/runtime/pom.xml +++ b/tools/runtime/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-runtime RDF4J: Runtime diff --git a/tools/server-spring/pom.xml b/tools/server-spring/pom.xml index 9406c020ed4..a3cba434f29 100644 --- a/tools/server-spring/pom.xml +++ b/tools/server-spring/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-server-spring RDF4J: HTTP server - core diff --git a/tools/server/pom.xml b/tools/server/pom.xml index 2eb37dc8027..5e6fc517570 100644 --- a/tools/server/pom.xml +++ b/tools/server/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-server war diff --git a/tools/workbench/pom.xml b/tools/workbench/pom.xml index 060b993e49f..cabca3a9a48 100644 --- a/tools/workbench/pom.xml +++ b/tools/workbench/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.4-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-workbench war From 99d6bfac3de6c7d54910da41fa8fb7650114896c Mon Sep 17 00:00:00 2001 From: Chengxu Bian Date: Fri, 18 Apr 2025 19:42:42 -0400 Subject: [PATCH 09/46] GH-5302 Add Update SPARQL Query Debug Log --- .../server/repository/statements/StatementsController.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/statements/StatementsController.java b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/statements/StatementsController.java index 85ac839c2cd..9c4424952fc 100644 --- a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/statements/StatementsController.java +++ b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/statements/StatementsController.java @@ -156,6 +156,11 @@ private ModelAndView getSparqlUpdateResult(Repository repository, HttpServletReq throw new ClientHTTPException("Updates must be non-empty"); } + if (logger.isDebugEnabled()) { + final int queryHashCode = sparqlUpdateString.hashCode(); + logger.debug("update query {} = {}", queryHashCode, sparqlUpdateString); + } + // default query language is SPARQL QueryLanguage queryLn = QueryLanguage.SPARQL; From e2bf139200fb82d8e123f6b17ea0314c3a2f7cb7 Mon Sep 17 00:00:00 2001 From: Linn Aung Date: Fri, 25 Apr 2025 17:44:40 +0200 Subject: [PATCH 10/46] GH-5314: Add property paths validation check for construct query --- .../query/parser/sparql/TupleExprBuilder.java | 57 ++++++- .../query/parser/sparql/SPARQLParserTest.java | 157 +++++++++++++++++- 2 files changed, 204 insertions(+), 10 deletions(-) diff --git a/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilder.java b/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilder.java index 2fa952ee627..65c70adf0ee 100644 --- a/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilder.java +++ b/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilder.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015 Eclipse RDF4J contributors, Aduna, and others. + * Copyright (c) 2025 Eclipse RDF4J contributors. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Distribution License v1.0 @@ -180,6 +180,7 @@ import org.eclipse.rdf4j.query.parser.sparql.ast.ASTPathAlternative; import org.eclipse.rdf4j.query.parser.sparql.ast.ASTPathElt; import org.eclipse.rdf4j.query.parser.sparql.ast.ASTPathMod; +import org.eclipse.rdf4j.query.parser.sparql.ast.ASTPathNegatedPropertySet; import org.eclipse.rdf4j.query.parser.sparql.ast.ASTPathOneInPropertySet; import org.eclipse.rdf4j.query.parser.sparql.ast.ASTPathSequence; import org.eclipse.rdf4j.query.parser.sparql.ast.ASTProjectionElem; @@ -251,9 +252,6 @@ public class TupleExprBuilder extends AbstractASTVisitor { GraphPattern graphPattern = new GraphPattern(); - // private Map mappedValueConstants = new - // HashMap(); - /*--------------* * Constructors * *--------------*/ @@ -820,6 +818,12 @@ public TupleExpr visit(ASTConstructQuery node, Object data) throws VisitorExcept @Override public TupleExpr visit(ASTConstruct node, Object data) throws VisitorException { + + // check if the construct template contains any invalid nodes + if (isInvalidConstructQueryTemplate(node, true)) { + throw new MalformedQueryException("Invalid construct caluse."); + } + TupleExpr result = (TupleExpr) data; // Collect construct triples @@ -872,7 +876,7 @@ public TupleExpr visit(ASTConstruct node, Object data) throws VisitorException { // assign non-anonymous vars not present in where clause as // extension elements. This is necessary to make external // binding - // assingnment possible (see SES-996) + // assignment possible (see SES-996) extElemMap.put(var, new ExtensionElem(var.clone(), var.getName())); } } @@ -910,6 +914,41 @@ public TupleExpr visit(ASTConstruct node, Object data) throws VisitorException { return new Reduced(result); } + /** + * Checks if the given node is invalid in a CONSTRUCT template. + * + * @param node The node to check. + * @param isInConstructTemplate Indicates if the check is being performed within a CONSTRUCT clause. + * @return true if the node is invalid, false otherwise. + */ + private static boolean isInvalidConstructQueryTemplate(Node node, boolean isInConstructTemplate) { + if (isInConstructTemplate && isInvalidConstructNode(node)) { + return true; + } + + // recursively check child nodes + for (int i = 0; i < node.jjtGetNumChildren(); i++) { + if (isInvalidConstructQueryTemplate(node.jjtGetChild(i), isInConstructTemplate)) { + return true; + } + } + + return false; + } + + /** + * Checks if the given node is an invalid construct node. + * + * @param node The node to check. + * @return true if the node is invalid, false otherwise. + */ + private static boolean isInvalidConstructNode(Node node) { + return node instanceof ASTPathMod + || node instanceof ASTPathNegatedPropertySet + || node instanceof ASTPathOneInPropertySet + || (node instanceof ASTPathAlternative && node.jjtGetNumChildren() > 1); + } + /** * Gets the set of variables that are relevant for the constructor. This method accumulates all subject, predicate * and object variables from the supplied statement patterns, but ignores any context variables. @@ -1359,7 +1398,7 @@ public TupleExpr visit(ASTPathAlternative pathAltNode, Object data) throws Visit } } - // when using union to execute path expressions, the scope does not not change + // when using union to execute path expressions, the scope does not change union.setVariableScopeChange(false); return union; } @@ -1548,7 +1587,7 @@ private TupleExpr createTupleExprForNegatedPropertySets(List np TupleExpr patternMatchInverse = null; - // build a inverse statement pattern if needed + // build an inverse statement pattern if needed if (filterConditionInverse != null) { patternMatchInverse = new StatementPattern(pathSequenceContext.scope, endVar.clone(), predVar.clone(), subjVar.clone(), @@ -2362,7 +2401,7 @@ public Object visit(ASTBind node, Object data) throws VisitorException { ValueExpr ve = child0 instanceof ValueExpr ? (ValueExpr) child0 : (child0 instanceof TripleRef) ? ((TripleRef) child0).getExprVar() : null; if (ve == null) { - throw new IllegalArgumentException("Unexpected expressin on bind"); + throw new IllegalArgumentException("Unexpected expression on bind"); } // name to bind the expression outcome to @@ -2381,7 +2420,7 @@ public Object visit(ASTBind node, Object data) throws VisitorException { // check if alias is not previously used in the BGP if (arg.getBindingNames().contains(alias)) { - // SES-2314 we need to doublecheck that the reused varname is not just + // SES-2314 we need to double-check that the reused varname is not just // for an anonymous var or a constant. VarCollector collector = new VarCollector(); arg.visit(collector); diff --git a/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/SPARQLParserTest.java b/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/SPARQLParserTest.java index fa963633783..a0efe9c13b1 100644 --- a/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/SPARQLParserTest.java +++ b/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/SPARQLParserTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015 Eclipse RDF4J contributors, Aduna, and others. + * Copyright (c) 2025 Eclipse RDF4J contributors. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Distribution License v1.0 @@ -43,6 +43,7 @@ import org.eclipse.rdf4j.model.util.Values; import org.eclipse.rdf4j.query.BindingSet; import org.eclipse.rdf4j.query.MalformedQueryException; +import org.eclipse.rdf4j.query.QueryLanguage; import org.eclipse.rdf4j.query.algebra.AggregateFunctionCall; import org.eclipse.rdf4j.query.algebra.ArbitraryLengthPath; import org.eclipse.rdf4j.query.algebra.DeleteData; @@ -70,6 +71,8 @@ import org.eclipse.rdf4j.query.parser.ParsedQuery; import org.eclipse.rdf4j.query.parser.ParsedTupleQuery; import org.eclipse.rdf4j.query.parser.ParsedUpdate; +import org.eclipse.rdf4j.query.parser.QueryParserUtil; +import org.eclipse.rdf4j.query.parser.sparql.SPARQLParser; import org.eclipse.rdf4j.query.parser.sparql.aggregate.AggregateCollector; import org.eclipse.rdf4j.query.parser.sparql.aggregate.AggregateFunction; import org.eclipse.rdf4j.query.parser.sparql.aggregate.AggregateFunctionFactory; @@ -1002,6 +1005,158 @@ public void testApostropheInsertData() { parser.parseUpdate(query, null); } + @Test + public void testInvalidConstructQueryWithPropertyPathInConstructClause() { + String invalidSparqlQuery = " CONSTRUCT {\n" + + " ?s (!a)* ?p .\n" + + " }\n" + + " WHERE {\n" + + " ?s (!a)* ?p .\n" + + " }"; + + assertThrows(MalformedQueryException.class, () -> { + parser.parseQuery(invalidSparqlQuery, null); + }); + + } + + @Test + public void testValidConstructQueryWithPropertyPathInWhereClause() { + String validSparqlQuery = "PREFIX foaf: " + + "CONSTRUCT {\n" + + " ?person foaf:knows ?friend .\n" + + "}\n" + + "WHERE {\n" + + " ?person foaf:knows+ ?friend .\n" + + "}"; + + ParsedQuery parsedQuery = parser.parseQuery(validSparqlQuery, null); + assertThat(parsedQuery.getSourceString()).isEqualTo(validSparqlQuery); + + } + + @Test + public void testValidConstructQueryWithBlankNodeAsSubject() { + String validSparqlQuery = "PREFIX foaf: \n" + + "PREFIX site: \n" + + "\n" + + "CONSTRUCT { [] foaf:name ?name }\n" + + "WHERE\n" + + "{ [] foaf:name ?name ;\n" + + " site:hits ?hits .\n" + + "}\n" + + "ORDER BY desc(?hits)\n" + + "LIMIT 2"; + + ParsedQuery parsedQuery = parser.parseQuery(validSparqlQuery, null); + assertThat(parsedQuery.getSourceString()).isEqualTo(validSparqlQuery); + + } + + @Test + public void testInvalidConstructQueryWithPropertyPathInPredicatePosition() { + String invalidSparqlQuery = "PREFIX foaf: " + + "CONSTRUCT {\n" + + " ?person foaf:knows+ ?friend . " + + "}\n" + + "WHERE {\n" + + " ?person foaf:knows+ ?friend .\n" + + "}"; + assertThrows(MalformedQueryException.class, () -> { + parser.parseQuery(invalidSparqlQuery, null); + }); + + } + + @Test + public void testInvalidConstructQueryWithPropertyPathAlternationInPredicate() { + String invalidSparqlQuery = "PREFIX foaf: " + + "CONSTRUCT {\n" + + " ?x (foaf:knows|foaf:friendOf) ?y . " + + "}\n" + + "WHERE {\n" + + " ?x (foaf:knows|foaf:friendOf) ?y .\n" + + "} "; + assertThrows(MalformedQueryException.class, () -> { + parser.parseQuery(invalidSparqlQuery, null); + }); + + } + + @Test + public void testInvalidConstructQueryWithPathSequenceInPredicate() { + String invalidSparqlQuery = "PREFIX foaf: " + + "CONSTRUCT {\n" + + " ?a foaf:knows/foaf:knows ?c . " + + "}\n" + + "WHERE {\n" + + " ?a foaf:knows/foaf:knows ?c . .\n" + + "} "; + assertThrows(MalformedQueryException.class, () -> { + parser.parseQuery(invalidSparqlQuery, null); + }); + + } + + @Test + public void testInvalidConstructQueryWithNegatedPropertySet() { + String invalidSparqlQuery = "PREFIX foaf: " + + "CONSTRUCT {\n" + + " ?s !foaf:knows ?o . " + + "}\n" + + "WHERE {\n" + + " ?s !foaf:knows ?o .\n" + + "} "; + assertThrows(MalformedQueryException.class, () -> { + parser.parseQuery(invalidSparqlQuery, null); + }); + + } + + @Test + public void testInvalidConstructQueryWithZeroOrMorePathInPredicate() { + String invalidSparqlQuery = "PREFIX foaf: " + + "CONSTRUCT {\n" + + " ?s foaf:knows* ?o . " + + "}\n" + + "WHERE {\n" + + " ?s foaf:knows* ?o .\n" + + "} "; + assertThrows(MalformedQueryException.class, () -> { + parser.parseQuery(invalidSparqlQuery, null); + }); + } + + @Test + public void testInvalidConstructQueryWithZeroOrMorePathInPredicates() { + String invalidSparqlQuery = "PREFIX foaf: " + + "CONSTRUCT {\n" + + " ?s foaf:knows ?o . " + + " ?s foaf:name+ ?o . " + + "}\n" + + "WHERE {\n" + + " ?s foaf:knows* ?o .\n" + + " ?s foaf:name ?o .\n" + + "} "; + assertThrows(MalformedQueryException.class, () -> { + parser.parseQuery(invalidSparqlQuery, null); + }); + } + + @Test + public void testInvalidConstructQueryWithMalformedTriple() { + String invalidSparqlQuery = "PREFIX foaf: " + + "CONSTRUCT {\n" + + " ?person foaf:name \n" + + "}\n" + + "WHERE {\n" + + " ?person foaf:name ?name .\n" + + "} "; + assertThrows(MalformedQueryException.class, () -> { + parser.parseQuery(invalidSparqlQuery, null); + }); + } + private AggregateFunctionFactory buildDummyFactory() { return new AggregateFunctionFactory() { @Override From 53fda356839e620676bae5f6be52e0449b060506 Mon Sep 17 00:00:00 2001 From: Linn Aung Date: Sat, 26 Apr 2025 10:25:17 +0200 Subject: [PATCH 11/46] GH-5314: rephrase exception message --- .../org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilder.java b/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilder.java index 65c70adf0ee..bb9c97ccbaa 100644 --- a/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilder.java +++ b/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilder.java @@ -821,7 +821,7 @@ public TupleExpr visit(ASTConstruct node, Object data) throws VisitorException { // check if the construct template contains any invalid nodes if (isInvalidConstructQueryTemplate(node, true)) { - throw new MalformedQueryException("Invalid construct caluse."); + throw new MalformedQueryException("Invalid construct clause."); } TupleExpr result = (TupleExpr) data; From cad419c1e8cb80d640e5eeb955d79c95b1ed9cec Mon Sep 17 00:00:00 2001 From: Linn Aung Date: Sun, 4 May 2025 08:51:54 +0200 Subject: [PATCH 12/46] GH-5314: revert license header --- .../org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilder.java | 2 +- .../org/eclipse/rdf4j/query/parser/sparql/SPARQLParserTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilder.java b/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilder.java index bb9c97ccbaa..a52d72d8871 100644 --- a/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilder.java +++ b/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilder.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2025 Eclipse RDF4J contributors. + * Copyright (c) 2015 Eclipse RDF4J contributors, Aduna, and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Distribution License v1.0 diff --git a/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/SPARQLParserTest.java b/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/SPARQLParserTest.java index a0efe9c13b1..e52e1016776 100644 --- a/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/SPARQLParserTest.java +++ b/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/SPARQLParserTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2025 Eclipse RDF4J contributors. + * Copyright (c) 2015 Eclipse RDF4J contributors, Aduna, and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Distribution License v1.0 From c03814d62f26313f7739ee1a89c2b95b7ca885fe Mon Sep 17 00:00:00 2001 From: Chengxu Bian Date: Mon, 12 May 2025 23:32:25 -0400 Subject: [PATCH 13/46] GH-2979 Allow query explanation via HTTPRepository / REST API --- .../eclipse/rdf4j/http/protocol/Protocol.java | 2 + .../reference/rest-api/rdf4j-openapi.yaml | 65 ++++++++++++------- .../repository/ExplainQueryResultView.java | 64 ++++++++++++++++++ .../server/repository/QueryResultView.java | 5 ++ .../handler/AbstractQueryRequestHandler.java | 46 ++++++++++--- .../handler/DefaultQueryRequestHandler.java | 25 ++++++- 6 files changed, 172 insertions(+), 35 deletions(-) create mode 100644 tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/ExplainQueryResultView.java diff --git a/core/http/protocol/src/main/java/org/eclipse/rdf4j/http/protocol/Protocol.java b/core/http/protocol/src/main/java/org/eclipse/rdf4j/http/protocol/Protocol.java index 0eb50ddb466..7b1e391f3fe 100644 --- a/core/http/protocol/src/main/java/org/eclipse/rdf4j/http/protocol/Protocol.java +++ b/core/http/protocol/src/main/java/org/eclipse/rdf4j/http/protocol/Protocol.java @@ -166,6 +166,8 @@ public enum TIMEOUT { public static final String OFFSET_PARAM_NAME = "offset"; + public static final String EXPLAIN_PARAM_NAME = "explain"; + /** * Parameter name for the query language parameter. */ diff --git a/site/static/documentation/reference/rest-api/rdf4j-openapi.yaml b/site/static/documentation/reference/rest-api/rdf4j-openapi.yaml index 3bdc164998c..3493b4a3188 100644 --- a/site/static/documentation/reference/rest-api/rdf4j-openapi.yaml +++ b/site/static/documentation/reference/rest-api/rdf4j-openapi.yaml @@ -187,6 +187,7 @@ components: schema: type: string format: binary + examples: SparqlXmlBindings: value: | @@ -381,6 +382,22 @@ paths: description: Specifies the number of query solutions to skip. The value should be a positive integer. This parameter is cumulative with any OFFSET modifier in the supplied SPARQL query itself. schema: type: integer + - name: explain + in: query + required: false + description: | + The level of explanation to return. If specified, the server will return an explanation of the query execution plan. The value should be one of the following: + - `Unoptimized`: Simple parsed plan + - `Optimized`: Parsed and optimized plan, including cost estimation + - `Executed`: Plan as it was executed, including actual result size + - `Timed`: Executed plan with timing details for each node + schema: + type: string + enum: + - Unoptimized + - Optimized + - Executed + - Timed responses: '200': $ref: "#/components/responses/200SparqlResult" @@ -449,7 +466,7 @@ paths: required: true $ref: "#/components/requestBodies/RdfData" responses: - '204': + '204': description: created /repositories/{repositoryID}/statements: @@ -829,8 +846,8 @@ paths: type: string example: http://www.example.com responses: - 204: - description: The defined namespace was successfully set to the given prefix. + 204: + description: The defined namespace was successfully set to the given prefix. delete: tags: - Namespaces @@ -1126,24 +1143,24 @@ paths: description: No content delete: - tags: - - Transactions - summary: Abort a transaction - description: | - An active transaction can be aborted by means of a HTTP DELETE request on the transaction resource. This will execute a transaction rollback on the repository and will close the transaction. After executing a DELETE, further operations on the same transaction will result in an error. - parameters: - - name: repositoryID - in: path - required: true - description: The repository ID - schema: - type: string - - name: transactionID - in: path - required: true - schema: - type: string - description: The transaction ID - responses: - 204: - description: Successfully aborted the defined transaction. + tags: + - Transactions + summary: Abort a transaction + description: | + An active transaction can be aborted by means of a HTTP DELETE request on the transaction resource. This will execute a transaction rollback on the repository and will close the transaction. After executing a DELETE, further operations on the same transaction will result in an error. + parameters: + - name: repositoryID + in: path + required: true + description: The repository ID + schema: + type: string + - name: transactionID + in: path + required: true + schema: + type: string + description: The transaction ID + responses: + 204: + description: Successfully aborted the defined transaction. diff --git a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/ExplainQueryResultView.java b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/ExplainQueryResultView.java new file mode 100644 index 00000000000..56a6da3b341 --- /dev/null +++ b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/ExplainQueryResultView.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.http.server.repository; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.http.HttpStatus; +import org.eclipse.rdf4j.http.protocol.Protocol; +import org.eclipse.rdf4j.query.explanation.Explanation; + +public class ExplainQueryResultView extends QueryResultView { + + private static final String MIME_PLAIN = "text/plain"; + private static final String MIME_JSON = "application/json"; + + @Override + protected void renderInternal( + final Map model, final HttpServletRequest request, final HttpServletResponse response) throws IOException { + + String mimeType = getRequestedMimeType(request); + Explanation explanation = (Explanation) model.get(QUERY_EXPLAIN_RESULT_KEY); + + if (explanation == null) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "No explanation result found."); + return; + } + + response.setCharacterEncoding("UTF-8"); + response.setStatus(HttpStatus.SC_OK); + + try (PrintWriter writer = response.getWriter()) { + if (MIME_JSON.equals(mimeType)) { + response.setContentType(MIME_JSON); + writer.write(explanation.toJson()); + } else if (MIME_PLAIN.equals(mimeType) || mimeType == null || mimeType.isEmpty()) { + response.setContentType(MIME_PLAIN); + writer.write(explanation.toString()); + } else { + response.sendError( + HttpServletResponse.SC_BAD_REQUEST, + "Unsupported MIME type: " + mimeType + ". Must be either text/plain or application/json." + ); + } + } + } + + private String getRequestedMimeType(HttpServletRequest request) { + String mimeType = request.getParameter(Protocol.ACCEPT_PARAM_NAME); + return (mimeType != null) ? mimeType : request.getHeader("Accept"); + } +} diff --git a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/QueryResultView.java b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/QueryResultView.java index 7a09198fb11..1040bcf8745 100644 --- a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/QueryResultView.java +++ b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/QueryResultView.java @@ -40,6 +40,11 @@ public abstract class QueryResultView implements View { */ public static final String QUERY_RESULT_KEY = "queryResult"; + /** + * Key by which the query result explanation is stored in the model. + */ + public static final String QUERY_EXPLAIN_RESULT_KEY = "explainResult"; + /** * Key by which the query result writer factory is stored in the model. */ diff --git a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/handler/AbstractQueryRequestHandler.java b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/handler/AbstractQueryRequestHandler.java index c9274bb83f6..1baa73bf29c 100644 --- a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/handler/AbstractQueryRequestHandler.java +++ b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/handler/AbstractQueryRequestHandler.java @@ -16,6 +16,7 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -32,6 +33,7 @@ import org.eclipse.rdf4j.query.Query; import org.eclipse.rdf4j.query.QueryEvaluationException; import org.eclipse.rdf4j.query.QueryInterruptedException; +import org.eclipse.rdf4j.query.explanation.Explanation; import org.eclipse.rdf4j.repository.Repository; import org.eclipse.rdf4j.repository.RepositoryConnection; import org.slf4j.Logger; @@ -54,8 +56,10 @@ public AbstractQueryRequestHandler(RepositoryResolver repositoryResolver) { } @Override - public ModelAndView handleQueryRequest(HttpServletRequest request, RequestMethod requestMethod, - HttpServletResponse response) throws HTTPException, IOException { + public ModelAndView handleQueryRequest( + HttpServletRequest request, RequestMethod requestMethod, + HttpServletResponse response + ) throws HTTPException, IOException { RepositoryConnection repositoryCon = null; Object queryResponse = null; @@ -74,11 +78,14 @@ public ModelAndView handleQueryRequest(HttpServletRequest request, RequestMethod long limit = getLimit(request); long offset = getOffset(request); boolean distinct = isDistinct(request); - + final Optional explainLevel = getExplain(request); try { - if (headersOnly) { - queryResponse = null; - } else { + if (!headersOnly) { + // explain param is present, return the query explanation + if (explainLevel.isPresent()) { + final Explanation explanation = explainQuery(query, explainLevel.get()); + return getExplainQueryResponse(request, response, explanation); + } queryResponse = evaluateQuery(query, limit, offset, distinct); } @@ -134,6 +141,14 @@ public ModelAndView handleQueryRequest(HttpServletRequest request, RequestMethod } + protected Explanation explainQuery(final Query query, final Explanation.Level explainLevel) + throws ServerHTTPException { + throw new ServerHTTPException("unimplemented explainQuery feature"); + } + + protected abstract ModelAndView getExplainQueryResponse( + final HttpServletRequest request, final HttpServletResponse response, final Explanation explanation); + abstract protected Object evaluateQuery(Query query, long limit, long offset, boolean distinct) throws ClientHTTPException; @@ -147,9 +162,11 @@ abstract protected String getQueryString(HttpServletRequest request, RequestMeth abstract protected Query getQuery(HttpServletRequest request, RepositoryConnection repositoryCon, String queryString) throws IOException, HTTPException; - protected ModelAndView getModelAndView(HttpServletRequest request, HttpServletResponse response, + protected ModelAndView getModelAndView( + HttpServletRequest request, HttpServletResponse response, boolean headersOnly, RepositoryConnection repositoryCon, View view, Object queryResult, - FileFormatServiceRegistry registry) throws ClientHTTPException { + FileFormatServiceRegistry registry + ) throws ClientHTTPException { Map model = new HashMap<>(); model.put(QueryResultView.FILENAME_HINT_KEY, "query-result"); model.put(QueryResultView.QUERY_RESULT_KEY, queryResult); @@ -172,6 +189,19 @@ protected long getLimit(HttpServletRequest request) throws ClientHTTPException { return getParam(request, Protocol.LIMIT_PARAM_NAME, 0L, Long.TYPE); } + protected Optional getExplain(HttpServletRequest request) throws ClientHTTPException { + final String explainString = request.getParameter(Protocol.EXPLAIN_PARAM_NAME); + if (explainString == null) { + return Optional.empty(); + } + try { + final Explanation.Level level = Explanation.Level.valueOf(explainString); + return Optional.of(level); + } catch (final IllegalArgumentException e) { + throw new ClientHTTPException("Invalid explanation level: " + explainString, e); + } + } + T getParam(HttpServletRequest request, String distinctParamName, T defaultValue, Class clazz) throws ClientHTTPException { if (clazz == Boolean.TYPE) { diff --git a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/handler/DefaultQueryRequestHandler.java b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/handler/DefaultQueryRequestHandler.java index 934260d1ddb..b7d969b1dac 100644 --- a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/handler/DefaultQueryRequestHandler.java +++ b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/handler/DefaultQueryRequestHandler.java @@ -24,8 +24,11 @@ import java.io.IOException; import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import org.apache.http.HttpStatus; @@ -38,9 +41,7 @@ import org.eclipse.rdf4j.http.server.ClientHTTPException; import org.eclipse.rdf4j.http.server.HTTPException; import org.eclipse.rdf4j.http.server.ProtocolUtil; -import org.eclipse.rdf4j.http.server.repository.BooleanQueryResultView; -import org.eclipse.rdf4j.http.server.repository.GraphQueryResultView; -import org.eclipse.rdf4j.http.server.repository.TupleQueryResultView; +import org.eclipse.rdf4j.http.server.repository.*; import org.eclipse.rdf4j.http.server.repository.resolver.RepositoryResolver; import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Value; @@ -56,6 +57,7 @@ import org.eclipse.rdf4j.query.TupleQuery; import org.eclipse.rdf4j.query.TupleQueryResult; import org.eclipse.rdf4j.query.UnsupportedQueryLanguageException; +import org.eclipse.rdf4j.query.explanation.Explanation; import org.eclipse.rdf4j.query.impl.SimpleDataset; import org.eclipse.rdf4j.query.resultio.BooleanQueryResultWriterRegistry; import org.eclipse.rdf4j.query.resultio.TupleQueryResultWriterRegistry; @@ -65,6 +67,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.View; public class DefaultQueryRequestHandler extends AbstractQueryRequestHandler { @@ -75,6 +78,22 @@ public DefaultQueryRequestHandler(RepositoryResolver repositoryResolver) { super(repositoryResolver); } + @Override + protected Explanation explainQuery(final Query query, final Explanation.Level level) { + return query.explain(level); + } + + @Override + protected ModelAndView getExplainQueryResponse( + final HttpServletRequest request, final HttpServletResponse response, + final Explanation explanation + ) { + Map model = new HashMap<>(); + model.put(QueryResultView.FILENAME_HINT_KEY, "query-result"); + model.put(QueryResultView.QUERY_EXPLAIN_RESULT_KEY, explanation); + return new ModelAndView(new ExplainQueryResultView(), model); + } + @Override protected Object evaluateQuery(Query query, long limit, long offset, boolean distinct) throws ClientHTTPException { if (query instanceof TupleQuery) { From 7493711a8a1630a6200dcddde64e5973d3c2e45b Mon Sep 17 00:00:00 2001 From: Chengxu Bian Date: Tue, 20 May 2025 23:24:45 -0400 Subject: [PATCH 14/46] GH-5330 throw an exception for unknown repositories in getRepositoryConnection --- .../rdf4j/http/server/repository/RepositoryInterceptor.java | 5 ++++- .../server/repository/namespaces/NamespaceController.java | 2 +- .../server/repository/namespaces/NamespacesController.java | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/RepositoryInterceptor.java b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/RepositoryInterceptor.java index 98d50d996dc..ef3fc181389 100644 --- a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/RepositoryInterceptor.java +++ b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/RepositoryInterceptor.java @@ -133,8 +133,11 @@ public static Repository getRepository(HttpServletRequest request) { * @param request the {@link HttpServletRequest} for which a {@link RepositoryConnection} is to be returned * @return a configured {@link RepositoryConnection} */ - public static RepositoryConnection getRepositoryConnection(HttpServletRequest request) { + public static RepositoryConnection getRepositoryConnection(HttpServletRequest request) throws ClientHTTPException { Repository repo = getRepository(request); + if (repo == null) { + throw new ClientHTTPException(SC_NOT_FOUND, "Unknown repository: " + getRepositoryID(request)); + } RepositoryConnection conn = repo.getConnection(); conn.getParserConfig().addNonFatalError(BasicParserSettings.VERIFY_DATATYPE_VALUES); conn.getParserConfig().addNonFatalError(BasicParserSettings.VERIFY_LANGUAGE_TAGS); diff --git a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/namespaces/NamespaceController.java b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/namespaces/NamespaceController.java index 070dfd5b55d..f7a2951714f 100644 --- a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/namespaces/NamespaceController.java +++ b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/namespaces/NamespaceController.java @@ -115,7 +115,7 @@ private ModelAndView getUpdateNamespaceResult(HttpServletRequest request, String } private ModelAndView getRemoveNamespaceResult(HttpServletRequest request, String prefix) - throws ServerHTTPException { + throws ServerHTTPException, ClientHTTPException { try (RepositoryConnection repositoryCon = RepositoryInterceptor.getRepositoryConnection(request)) { repositoryCon.removeNamespace(prefix); } catch (RepositoryException e) { diff --git a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/namespaces/NamespacesController.java b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/namespaces/NamespacesController.java index 7b772e956a8..178d414b65d 100644 --- a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/namespaces/NamespacesController.java +++ b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/namespaces/NamespacesController.java @@ -117,7 +117,7 @@ private ModelAndView getExportNamespacesResult(HttpServletRequest request, HttpS } private ModelAndView getClearNamespacesResult(HttpServletRequest request, HttpServletResponse response) - throws ServerHTTPException { + throws ServerHTTPException, ClientHTTPException { try (RepositoryConnection repositoryCon = RepositoryInterceptor.getRepositoryConnection(request)) { try { repositoryCon.clearNamespaces(); From 541a96700e49e0d4e5045f268610d24ed968de08 Mon Sep 17 00:00:00 2001 From: Ken Wenzel Date: Fri, 20 Jun 2025 17:16:59 +0200 Subject: [PATCH 15/46] GH-5353: Make write benchmarks more realistic. Reworks write benchmarks for NativeStore and LmdbStore to use varying subjects, properties as well as literals with different datatypes. --- .../lmdb/benchmark/BenchmarkBaseFoaf.java | 2 +- .../benchmark/RandomLiteralGenerator.java | 119 ++++++++++++++++ .../TransactionsPerSecondBenchmark.java | 53 +++++-- .../TransactionsPerSecondBenchmarkFoaf.java | 8 +- ...ansactionsPerSecondForceSyncBenchmark.java | 97 ++----------- .../benchmark/BenchmarkBaseFoaf.java | 108 +++++++++++++++ .../benchmark/RandomLiteralGenerator.java | 119 ++++++++++++++++ .../TransactionsPerSecondBenchmark.java | 56 +++++--- .../TransactionsPerSecondBenchmarkFoaf.java | 130 ++++++++++++++++++ ...ansactionsPerSecondForceSyncBenchmark.java | 116 ++-------------- 10 files changed, 578 insertions(+), 230 deletions(-) create mode 100644 core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/RandomLiteralGenerator.java create mode 100644 core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/BenchmarkBaseFoaf.java create mode 100644 core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/RandomLiteralGenerator.java create mode 100644 core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/TransactionsPerSecondBenchmarkFoaf.java diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/BenchmarkBaseFoaf.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/BenchmarkBaseFoaf.java index f9223ff12ec..349413d7248 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/BenchmarkBaseFoaf.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/BenchmarkBaseFoaf.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 Eclipse RDF4J contributors. + * Copyright (c) 2025 Eclipse RDF4J contributors. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Distribution License v1.0 diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/RandomLiteralGenerator.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/RandomLiteralGenerator.java new file mode 100644 index 00000000000..80dfe5bb478 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/RandomLiteralGenerator.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb.benchmark; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; +import java.util.Random; +import java.util.UUID; +import java.util.function.Supplier; + +import org.eclipse.rdf4j.model.Literal; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.model.vocabulary.XSD; + +/** + * A utility class for generating random RDF literals using a variety of data types, including numeric types (integer, + * float, double, etc.), boolean, string, and date/time types. + *

    + * This class is primarily useful for testing and demonstration purposes where randomized literal values are needed. + */ +public class RandomLiteralGenerator { + + /** + * The {@link ValueFactory} used to create RDF literals. + */ + private final ValueFactory vf; + + /** + * The {@link Random} instance used to generate random values. + */ + private final Random random; + + /** + * A list of suppliers, each of which produces a different type of RDF literal. + */ + private List> literalSuppliers; + + /** + * Constructs a new {@code RandomLiteralGenerator} with the specified {@link ValueFactory} and {@link Random} + * instances. + * + * @param vf the value factory used to create RDF literals + * @param random the random generator used to generate random values + */ + public RandomLiteralGenerator(ValueFactory vf, Random random) { + this.vf = vf; + this.random = random; + init(); + } + + /** + * Initializes the list of literal suppliers with a variety of data types. Includes decimals, doubles, floats, + * integers, booleans, strings, unsigned values, and date/time literals. + */ + private void init() { + literalSuppliers = Arrays.asList( + // Decimal + () -> vf.createLiteral(BigDecimal.valueOf(random.nextDouble() * 100000 - 50000)), + // Double + () -> vf.createLiteral(random.nextBoolean() ? Double.NaN : random.nextDouble() * 1000), + () -> vf.createLiteral(random.nextBoolean() ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY), + () -> vf.createLiteral((double) random.nextInt(1000)), + // Float + () -> vf.createLiteral(random.nextBoolean() ? Float.NaN : random.nextFloat() * 100), + () -> vf.createLiteral(random.nextBoolean() ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY), + // Integer + () -> vf.createLiteral(BigInteger.valueOf(random.nextInt(1_000_000) - 500_000)), + () -> vf.createLiteral(random.nextInt(1_000_000) - 500_000), + // Long + () -> vf.createLiteral(random.nextLong()), + // Short + () -> vf.createLiteral((short) (random.nextInt(Short.MAX_VALUE - Short.MIN_VALUE) + Short.MIN_VALUE)), + // Byte + () -> vf.createLiteral((byte) (random.nextInt(Byte.MAX_VALUE - Byte.MIN_VALUE) + Byte.MIN_VALUE)), + // Unsigned Int types + () -> vf.createLiteral(String.valueOf(random.nextInt(1 << 16)), XSD.UNSIGNED_SHORT), + () -> vf.createLiteral(String.valueOf(random.nextInt(1 << 8)), XSD.UNSIGNED_BYTE), + () -> vf.createLiteral(String.valueOf(random.nextInt(100000)), XSD.UNSIGNED_INT), + // Positive/Negative Integer + () -> vf.createLiteral(String.valueOf(1 + random.nextInt(1_000_000)), XSD.POSITIVE_INTEGER), + () -> vf.createLiteral("-" + (1 + random.nextInt(1_000_000)), XSD.NEGATIVE_INTEGER), + // Non-negative/Non-positive + () -> vf.createLiteral(String.valueOf(random.nextInt(1_000_000)), XSD.NON_NEGATIVE_INTEGER), + () -> vf.createLiteral("-" + random.nextInt(1_000_000), XSD.NON_POSITIVE_INTEGER), + // String + () -> vf.createLiteral(UUID.randomUUID().toString().substring(0, 8), XSD.STRING), + () -> vf.createLiteral("testString" + random.nextInt(100), XSD.STRING), + // Boolean + () -> vf.createLiteral(random.nextBoolean()), + // Date and DateTime + () -> vf.createLiteral( + LocalDate.of(1970 + random.nextInt(100), 1 + random.nextInt(12), 1 + random.nextInt(28))), + () -> vf.createLiteral( + LocalDateTime.of(1970 + random.nextInt(100), 1 + random.nextInt(12), 1 + random.nextInt(28), + random.nextInt(24), random.nextInt(60), random.nextInt(60))) + ); + } + + /** + * Generates a random RDF literal. + * + * @return a randomly selected RDF literal + */ + public Literal createRandomLiteral() { + return literalSuppliers.get(random.nextInt(literalSuppliers.size())).get(); + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/TransactionsPerSecondBenchmark.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/TransactionsPerSecondBenchmark.java index e6f6ac5d911..dd20a7ceac4 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/TransactionsPerSecondBenchmark.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/TransactionsPerSecondBenchmark.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021 Eclipse RDF4J contributors. + * Copyright (c) 2025 Eclipse RDF4J contributors. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Distribution License v1.0 @@ -13,12 +13,15 @@ import java.io.File; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; import java.util.concurrent.TimeUnit; import org.apache.commons.io.FileUtils; import org.assertj.core.util.Files; import org.eclipse.rdf4j.common.transaction.IsolationLevels; -import org.eclipse.rdf4j.model.vocabulary.RDFS; +import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.repository.sail.SailRepository; import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection; import org.eclipse.rdf4j.sail.lmdb.LmdbStore; @@ -46,20 +49,23 @@ @Warmup(iterations = 2) @BenchmarkMode({ Mode.Throughput }) @Fork(value = 1, jvmArgs = { "-Xms2G", "-Xmx2G", "-XX:+UseG1GC" }) -//@Fork(value = 1, jvmArgs = {"-Xms8G", "-Xmx8G", "-XX:+UseG1GC", "-XX:+UnlockCommercialFeatures", "-XX:StartFlightRecording=delay=60s,duration=120s,filename=recording.jfr,settings=profile", "-XX:FlightRecorderOptions=samplethreads=true,stackdepth=1024", "-XX:+UnlockDiagnosticVMOptions", "-XX:+DebugNonSafepoints"}) -@Measurement(iterations = 5) +@Measurement(iterations = 3) @OutputTimeUnit(TimeUnit.SECONDS) public class TransactionsPerSecondBenchmark { - private SailRepository repository; - private File file; - SailRepositoryConnection connection; + RandomLiteralGenerator literalGenerator; + Random random; int i; + List resources; + List predicates; + protected SailRepository repository; + protected File file; + protected boolean forceSync = false; public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() - .include("TransactionsPerSecondBenchmark") // adapt to control which benchmark tests to run + .include("TransactionsPerSecondBenchmark\\.") // adapt to control which benchmarks to run .forks(1) .build(); @@ -75,12 +81,29 @@ public void beforeClass() { i = 0; file = Files.newTemporaryFolder(); - LmdbStore sail = new LmdbStore(file, ConfigUtil.createConfig()); + LmdbStore sail = new LmdbStore(file, ConfigUtil.createConfig().setForceSync(forceSync)); repository = new SailRepository(sail); connection = repository.getConnection(); + random = new Random(1337); + literalGenerator = new RandomLiteralGenerator(connection.getValueFactory(), random); + resources = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + resources.add(connection.getValueFactory().createIRI("some:resource-" + i)); + } + predicates = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + predicates.add(connection.getValueFactory().createIRI("some:predicate-" + i)); + } System.gc(); + } + + IRI randomResource() { + return resources.get(random.nextInt(resources.size())); + } + IRI randomPredicate() { + return predicates.get(random.nextInt(predicates.size())); } @TearDown(Level.Iteration) @@ -97,14 +120,14 @@ public void afterClass() throws IOException { @Benchmark public void transactions() { connection.begin(); - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++)); + connection.add(randomResource(), randomPredicate(), literalGenerator.createRandomLiteral()); connection.commit(); } @Benchmark public void transactionsLevelNone() { connection.begin(IsolationLevels.NONE); - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++)); + connection.add(randomResource(), randomPredicate(), literalGenerator.createRandomLiteral()); connection.commit(); } @@ -112,7 +135,7 @@ public void transactionsLevelNone() { public void mediumTransactionsLevelNone() { connection.begin(IsolationLevels.NONE); for (int k = 0; k < 10; k++) { - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++ + "_" + k)); + connection.add(randomResource(), randomPredicate(), literalGenerator.createRandomLiteral()); } connection.commit(); } @@ -121,7 +144,7 @@ public void mediumTransactionsLevelNone() { public void largerTransaction() { connection.begin(); for (int k = 0; k < 10000; k++) { - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++ + "_" + k)); + connection.add(randomResource(), randomPredicate(), literalGenerator.createRandomLiteral()); } connection.commit(); } @@ -130,7 +153,7 @@ public void largerTransaction() { public void largerTransactionLevelNone() { connection.begin(IsolationLevels.NONE); for (int k = 0; k < 10000; k++) { - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++ + "_" + k)); + connection.add(randomResource(), randomPredicate(), literalGenerator.createRandomLiteral()); } connection.commit(); } @@ -139,7 +162,7 @@ public void largerTransactionLevelNone() { public void veryLargerTransactionLevelNone() { connection.begin(IsolationLevels.NONE); for (int k = 0; k < 1000000; k++) { - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++ + "_" + k)); + connection.add(randomResource(), randomPredicate(), literalGenerator.createRandomLiteral()); } connection.commit(); } diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/TransactionsPerSecondBenchmarkFoaf.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/TransactionsPerSecondBenchmarkFoaf.java index 8472095cf2f..a452e3c7202 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/TransactionsPerSecondBenchmarkFoaf.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/TransactionsPerSecondBenchmarkFoaf.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 Eclipse RDF4J contributors. + * Copyright (c) 2025 Eclipse RDF4J contributors. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Distribution License v1.0 @@ -33,19 +33,19 @@ import org.openjdk.jmh.runner.options.OptionsBuilder; /** - * Benchmarks insertion performance with extended FOAF data. + * Benchmarks insertion performance with synthetic FOAF data. */ @State(Scope.Benchmark) @Warmup(iterations = 2) @BenchmarkMode({ Mode.Throughput }) @Fork(value = 1, jvmArgs = { "-Xms2G", "-Xmx2G", "-XX:+UseG1GC" }) -@Measurement(iterations = 5) +@Measurement(iterations = 3) @OutputTimeUnit(TimeUnit.SECONDS) public class TransactionsPerSecondBenchmarkFoaf extends BenchmarkBaseFoaf { public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() - .include("TransactionsPerSecondBenchmarkFoaf") // adapt to control which benchmark tests to run + .include("TransactionsPerSecondBenchmarkFoaf\\.") // adapt to control which benchmark tests to run .forks(1) .build(); diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/TransactionsPerSecondForceSyncBenchmark.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/TransactionsPerSecondForceSyncBenchmark.java index 1ff2dc72549..94b2c42e09e 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/TransactionsPerSecondForceSyncBenchmark.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/TransactionsPerSecondForceSyncBenchmark.java @@ -43,104 +43,23 @@ * Benchmarks insertion performance with synthetic data. */ @State(Scope.Benchmark) -@Warmup(iterations = 20) +@Warmup(iterations = 2) @BenchmarkMode({ Mode.Throughput }) @Fork(value = 1, jvmArgs = { "-Xms2G", "-Xmx2G", "-XX:+UseG1GC" }) -//@Fork(value = 1, jvmArgs = {"-Xms8G", "-Xmx8G", "-XX:+UseG1GC", "-XX:+UnlockCommercialFeatures", "-XX:StartFlightRecording=delay=60s,duration=120s,filename=recording.jfr,settings=profile", "-XX:FlightRecorderOptions=samplethreads=true,stackdepth=1024", "-XX:+UnlockDiagnosticVMOptions", "-XX:+DebugNonSafepoints"}) -@Measurement(iterations = 10) +@Measurement(iterations = 3) @OutputTimeUnit(TimeUnit.SECONDS) -public class TransactionsPerSecondForceSyncBenchmark { - - private SailRepository repository; - private File file; - - SailRepositoryConnection connection; - int i; +public class TransactionsPerSecondForceSyncBenchmark extends TransactionsPerSecondBenchmark { + { + // enforce syncing to disk + forceSync = true; + } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() - .include("TransactionsPerSecondForceSyncBenchmark") // adapt to control which benchmark tests to run + .include("TransactionsPerSecondForceSyncBenchmark\\.") // adapt to control which benchmarks to run .forks(1) .build(); new Runner(opt).run(); } - - @Setup(Level.Iteration) - public void beforeClass() { - if (connection != null) { - connection.close(); - connection = null; - } - i = 0; - file = Files.newTemporaryFolder(); - - LmdbStore sail = new LmdbStore(file, ConfigUtil.createConfig().setForceSync(true)); - repository = new SailRepository(sail); - connection = repository.getConnection(); - - System.gc(); - - } - - @TearDown(Level.Iteration) - public void afterClass() throws IOException { - if (connection != null) { - connection.close(); - connection = null; - } - repository.shutDown(); - FileUtils.deleteDirectory(file); - - } - - @Benchmark - public void transactions() { - connection.begin(); - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++)); - connection.commit(); - } - - @Benchmark - public void transactionsLevelNone() { - connection.begin(IsolationLevels.NONE); - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++)); - connection.commit(); - } - - @Benchmark - public void mediumTransactionsLevelNone() { - connection.begin(IsolationLevels.NONE); - for (int k = 0; k < 10; k++) { - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++ + "_" + k)); - } - connection.commit(); - } - - @Benchmark - public void largerTransaction() { - connection.begin(); - for (int k = 0; k < 10000; k++) { - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++ + "_" + k)); - } - connection.commit(); - } - - @Benchmark - public void largerTransactionLevelNone() { - connection.begin(IsolationLevels.NONE); - for (int k = 0; k < 10000; k++) { - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++ + "_" + k)); - } - connection.commit(); - } - - @Benchmark - public void veryLargerTransactionLevelNone() { - connection.begin(IsolationLevels.NONE); - for (int k = 0; k < 1000000; k++) { - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++ + "_" + k)); - } - connection.commit(); - } } diff --git a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/BenchmarkBaseFoaf.java b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/BenchmarkBaseFoaf.java new file mode 100644 index 00000000000..350dda002ad --- /dev/null +++ b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/BenchmarkBaseFoaf.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ + +package org.eclipse.rdf4j.sail.nativerdf.benchmark; + +import java.io.File; +import java.io.IOException; +import java.util.Locale; +import java.util.Random; + +import org.apache.commons.io.FileUtils; +import org.assertj.core.util.Files; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.repository.sail.SailRepository; +import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection; +import org.eclipse.rdf4j.sail.nativerdf.NativeStore; + +public class BenchmarkBaseFoaf { + + protected File file; + + protected SailRepository repository; + protected SailRepositoryConnection connection; + + protected Random random = new Random(12345); + + private int i = 1; + private final String[] countries = Locale.getISOCountries(); + private final String[] languages = Locale.getISOLanguages(); + + public void setup() throws IOException { + i = 1; + if (connection != null) { + connection.close(); + connection = null; + } + file = Files.newTemporaryFolder(); + + NativeStore sail = new NativeStore(file); + repository = new SailRepository(sail); + connection = repository.getConnection(); + + System.gc(); + } + + public void tearDown() throws IOException { + if (connection != null) { + connection.close(); + connection = null; + } + repository.shutDown(); + FileUtils.deleteDirectory(file); + } + + void addPersonNameOnly() { + ValueFactory vf = connection.getValueFactory(); + + IRI person = vf.createIRI("http://www.example.org/persons/person_" + i); + // English label + connection.add(person, vf.createIRI("http://www.w3.org/2000/01/rdf-schema#label"), + vf.createLiteral("Name @en" + i, "en")); + + i++; + } + + void addPerson() { + ValueFactory vf = connection.getValueFactory(); + + IRI person = vf.createIRI("http://www.example.org/persons/person_" + i); + connection.add(person, vf.createIRI("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), + vf.createIRI("http://xmlns.com/foaf/0.1/Person")); + + // English label + connection.add(person, vf.createIRI("http://www.w3.org/2000/01/rdf-schema#label"), + vf.createLiteral("Name @en" + i, "en")); + + // 3 other languages + random.ints(3, 0, languages.length).distinct().forEach(langIndex -> { + connection.add(person, vf.createIRI("http://www.w3.org/2000/01/rdf-schema#label"), + vf.createLiteral("Name " + i, languages[langIndex])); + }); + + int countryIndex = random.nextInt(countries.length); + int organizationNr = countryIndex + 1; + connection.add(vf.createIRI("http://www.example.org/organizations/org_" + organizationNr), + vf.createIRI("http://xmlns.com/foaf/0.1/member"), person); + + connection.add(person, vf.createIRI("http://www.example.org/vocab/countryCode"), + vf.createLiteral(countries[countryIndex])); + + int knowsMemberNr = random.nextInt(i) + 1; + for (int nr = 0; nr < 3; nr++) { + connection.add(person, vf.createIRI("http://xmlns.com/foaf/0.1/knows"), + vf.createIRI("http://www.example.org/persons/person_" + (knowsMemberNr + nr))); + } + + i++; + } +} diff --git a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/RandomLiteralGenerator.java b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/RandomLiteralGenerator.java new file mode 100644 index 00000000000..091ede74766 --- /dev/null +++ b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/RandomLiteralGenerator.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.nativerdf.benchmark; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; +import java.util.Random; +import java.util.UUID; +import java.util.function.Supplier; + +import org.eclipse.rdf4j.model.Literal; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.model.vocabulary.XSD; + +/** + * A utility class for generating random RDF literals using a variety of data types, including numeric types (integer, + * float, double, etc.), boolean, string, and date/time types. + *

    + * This class is primarily useful for testing and demonstration purposes where randomized literal values are needed. + */ +public class RandomLiteralGenerator { + + /** + * The {@link ValueFactory} used to create RDF literals. + */ + private final ValueFactory vf; + + /** + * The {@link Random} instance used to generate random values. + */ + private final Random random; + + /** + * A list of suppliers, each of which produces a different type of RDF literal. + */ + private List> literalSuppliers; + + /** + * Constructs a new {@code RandomLiteralGenerator} with the specified {@link ValueFactory} and {@link Random} + * instances. + * + * @param vf the value factory used to create RDF literals + * @param random the random generator used to generate random values + */ + public RandomLiteralGenerator(ValueFactory vf, Random random) { + this.vf = vf; + this.random = random; + init(); + } + + /** + * Initializes the list of literal suppliers with a variety of data types. Includes decimals, doubles, floats, + * integers, booleans, strings, unsigned values, and date/time literals. + */ + private void init() { + literalSuppliers = Arrays.asList( + // Decimal + () -> vf.createLiteral(BigDecimal.valueOf(random.nextDouble() * 100000 - 50000)), + // Double + () -> vf.createLiteral(random.nextBoolean() ? Double.NaN : random.nextDouble() * 1000), + () -> vf.createLiteral(random.nextBoolean() ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY), + () -> vf.createLiteral((double) random.nextInt(1000)), + // Float + () -> vf.createLiteral(random.nextBoolean() ? Float.NaN : random.nextFloat() * 100), + () -> vf.createLiteral(random.nextBoolean() ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY), + // Integer + () -> vf.createLiteral(BigInteger.valueOf(random.nextInt(1_000_000) - 500_000)), + () -> vf.createLiteral(random.nextInt(1_000_000) - 500_000), + // Long + () -> vf.createLiteral(random.nextLong()), + // Short + () -> vf.createLiteral((short) (random.nextInt(Short.MAX_VALUE - Short.MIN_VALUE) + Short.MIN_VALUE)), + // Byte + () -> vf.createLiteral((byte) (random.nextInt(Byte.MAX_VALUE - Byte.MIN_VALUE) + Byte.MIN_VALUE)), + // Unsigned Int types + () -> vf.createLiteral(String.valueOf(random.nextInt(1 << 16)), XSD.UNSIGNED_SHORT), + () -> vf.createLiteral(String.valueOf(random.nextInt(1 << 8)), XSD.UNSIGNED_BYTE), + () -> vf.createLiteral(String.valueOf(random.nextInt(100000)), XSD.UNSIGNED_INT), + // Positive/Negative Integer + () -> vf.createLiteral(String.valueOf(1 + random.nextInt(1_000_000)), XSD.POSITIVE_INTEGER), + () -> vf.createLiteral("-" + (1 + random.nextInt(1_000_000)), XSD.NEGATIVE_INTEGER), + // Non-negative/Non-positive + () -> vf.createLiteral(String.valueOf(random.nextInt(1_000_000)), XSD.NON_NEGATIVE_INTEGER), + () -> vf.createLiteral("-" + random.nextInt(1_000_000), XSD.NON_POSITIVE_INTEGER), + // String + () -> vf.createLiteral(UUID.randomUUID().toString().substring(0, 8), XSD.STRING), + () -> vf.createLiteral("testString" + random.nextInt(100), XSD.STRING), + // Boolean + () -> vf.createLiteral(random.nextBoolean()), + // Date and DateTime + () -> vf.createLiteral( + LocalDate.of(1970 + random.nextInt(100), 1 + random.nextInt(12), 1 + random.nextInt(28))), + () -> vf.createLiteral( + LocalDateTime.of(1970 + random.nextInt(100), 1 + random.nextInt(12), 1 + random.nextInt(28), + random.nextInt(24), random.nextInt(60), random.nextInt(60))) + ); + } + + /** + * Generates a random RDF literal. + * + * @return a randomly selected RDF literal + */ + public Literal createRandomLiteral() { + return literalSuppliers.get(random.nextInt(literalSuppliers.size())).get(); + } +} diff --git a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/TransactionsPerSecondBenchmark.java b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/TransactionsPerSecondBenchmark.java index 206426643b9..635e456b819 100644 --- a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/TransactionsPerSecondBenchmark.java +++ b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/TransactionsPerSecondBenchmark.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2020 Eclipse RDF4J contributors. + * Copyright (c) 2025 Eclipse RDF4J contributors. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Distribution License v1.0 @@ -13,12 +13,15 @@ import java.io.File; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; import java.util.concurrent.TimeUnit; import org.apache.commons.io.FileUtils; import org.assertj.core.util.Files; import org.eclipse.rdf4j.common.transaction.IsolationLevels; -import org.eclipse.rdf4j.model.vocabulary.RDFS; +import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.repository.sail.SailRepository; import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection; import org.eclipse.rdf4j.sail.nativerdf.NativeStore; @@ -40,26 +43,30 @@ import org.openjdk.jmh.runner.options.OptionsBuilder; /** - * @author Håvard Ottestad + * Benchmarks insertion performance with synthetic data. */ @State(Scope.Benchmark) -@Warmup(iterations = 20) +@Warmup(iterations = 2) @BenchmarkMode({ Mode.Throughput }) @Fork(value = 1, jvmArgs = { "-Xms8G", "-Xmx8G", "-XX:+UseG1GC" }) //@Fork(value = 1, jvmArgs = {"-Xms8G", "-Xmx8G", "-XX:+UseG1GC", "-XX:+UnlockCommercialFeatures", "-XX:StartFlightRecording=delay=60s,duration=120s,filename=recording.jfr,settings=profile", "-XX:FlightRecorderOptions=samplethreads=true,stackdepth=1024", "-XX:+UnlockDiagnosticVMOptions", "-XX:+DebugNonSafepoints"}) -@Measurement(iterations = 10) +@Measurement(iterations = 3) @OutputTimeUnit(TimeUnit.SECONDS) public class TransactionsPerSecondBenchmark { - private SailRepository repository; - private File file; - SailRepositoryConnection connection; + RandomLiteralGenerator literalGenerator; + Random random; int i; + List resources; + List predicates; + protected SailRepository repository; + protected File file; + protected boolean forceSync = false; public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() - .include("TransactionsPerSecondBenchmark") // adapt to control which benchmark tests to run + .include("TransactionsPerSecondBenchmark\\.") // adapt to control which benchmarks to run .forks(1) .build(); @@ -76,12 +83,29 @@ public void beforeClass() { file = Files.newTemporaryFolder(); NativeStore sail = new NativeStore(file, "spoc,ospc,psoc"); - sail.setForceSync(false); + sail.setForceSync(forceSync); repository = new SailRepository(sail); connection = repository.getConnection(); + random = new Random(1337); + literalGenerator = new RandomLiteralGenerator(connection.getValueFactory(), random); + resources = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + resources.add(connection.getValueFactory().createIRI("some:resource-" + i)); + } + predicates = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + predicates.add(connection.getValueFactory().createIRI("some:predicate-" + i)); + } System.gc(); + } + + IRI randomResource() { + return resources.get(random.nextInt(resources.size())); + } + IRI randomPredicate() { + return predicates.get(random.nextInt(predicates.size())); } @TearDown(Level.Iteration) @@ -98,14 +122,14 @@ public void afterClass() throws IOException { @Benchmark public void transactions() { connection.begin(); - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++)); + connection.add(randomResource(), randomPredicate(), literalGenerator.createRandomLiteral()); connection.commit(); } @Benchmark public void transactionsLevelNone() { connection.begin(IsolationLevels.NONE); - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++)); + connection.add(randomResource(), randomPredicate(), literalGenerator.createRandomLiteral()); connection.commit(); } @@ -113,7 +137,7 @@ public void transactionsLevelNone() { public void mediumTransactionsLevelNone() { connection.begin(IsolationLevels.NONE); for (int k = 0; k < 10; k++) { - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++ + "_" + k)); + connection.add(randomResource(), randomPredicate(), literalGenerator.createRandomLiteral()); } connection.commit(); } @@ -122,7 +146,7 @@ public void mediumTransactionsLevelNone() { public void largerTransaction() { connection.begin(); for (int k = 0; k < 10000; k++) { - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++ + "_" + k)); + connection.add(randomResource(), randomPredicate(), literalGenerator.createRandomLiteral()); } connection.commit(); } @@ -131,7 +155,7 @@ public void largerTransaction() { public void largerTransactionLevelNone() { connection.begin(IsolationLevels.NONE); for (int k = 0; k < 10000; k++) { - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++ + "_" + k)); + connection.add(randomResource(), randomPredicate(), literalGenerator.createRandomLiteral()); } connection.commit(); } @@ -140,7 +164,7 @@ public void largerTransactionLevelNone() { public void veryLargerTransactionLevelNone() { connection.begin(IsolationLevels.NONE); for (int k = 0; k < 1000000; k++) { - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++ + "_" + k)); + connection.add(randomResource(), randomPredicate(), literalGenerator.createRandomLiteral()); } connection.commit(); } diff --git a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/TransactionsPerSecondBenchmarkFoaf.java b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/TransactionsPerSecondBenchmarkFoaf.java new file mode 100644 index 00000000000..d25928a2ee0 --- /dev/null +++ b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/TransactionsPerSecondBenchmarkFoaf.java @@ -0,0 +1,130 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ + +package org.eclipse.rdf4j.sail.nativerdf.benchmark; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import org.eclipse.rdf4j.common.transaction.IsolationLevels; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/** + * Benchmarks insertion performance with synthetic FOAF data. + */ +@State(Scope.Benchmark) +@Warmup(iterations = 2) +@BenchmarkMode({ Mode.Throughput }) +@Fork(value = 1, jvmArgs = { "-Xms8G", "-Xmx8G", "-XX:+UseG1GC" }) +//@Fork(value = 1, jvmArgs = {"-Xms8G", "-Xmx8G", "-XX:+UseG1GC", "-XX:+UnlockCommercialFeatures", "-XX:StartFlightRecording=delay=60s,duration=120s,filename=recording.jfr,settings=profile", "-XX:FlightRecorderOptions=samplethreads=true,stackdepth=1024", "-XX:+UnlockDiagnosticVMOptions", "-XX:+DebugNonSafepoints"}) +@Measurement(iterations = 2, time = 60) +@OutputTimeUnit(TimeUnit.SECONDS) +public class TransactionsPerSecondBenchmarkFoaf extends BenchmarkBaseFoaf { + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include("TransactionsPerSecondBenchmarkFoaf\\.") // adapt to control which + // benchmark tests to run + .forks(1) + .build(); + + new Runner(opt).run(); + } + + @Setup(Level.Iteration) + public void setup() throws IOException { + super.setup(); + } + + @TearDown(Level.Iteration) + public void tearDown() throws IOException { + super.tearDown(); + } + + @Benchmark + public void transaction1x() { + connection.begin(); + addPersonNameOnly(); + connection.commit(); + } + + @Benchmark + public void transaction1xLevelNone() { + connection.begin(IsolationLevels.NONE); + addPersonNameOnly(); + connection.commit(); + } + + @Benchmark + public void transaction10x() { + connection.begin(); + addPerson(); + connection.commit(); + } + + @Benchmark + public void transaction10xLevelNone() { + connection.begin(IsolationLevels.NONE); + addPerson(); + connection.commit(); + } + + @Benchmark + public void transaction10kx() { + connection.begin(); + for (int k = 0; k < 1000; k++) { + addPerson(); + } + connection.commit(); + } + + @Benchmark + public void transaction10kxLevelNone() { + connection.begin(IsolationLevels.NONE); + for (int k = 0; k < 1000; k++) { + addPerson(); + } + connection.commit(); + } + + @Benchmark + public void transaction100kx() { + connection.begin(); + for (int k = 0; k < 10000; k++) { + addPerson(); + } + connection.commit(); + } + + @Benchmark + public void transaction100kxLevelNone() { + connection.begin(IsolationLevels.NONE); + for (int k = 0; k < 10000; k++) { + addPerson(); + } + connection.commit(); + } +} diff --git a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/TransactionsPerSecondForceSyncBenchmark.java b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/TransactionsPerSecondForceSyncBenchmark.java index d3d6c6f6dd8..150f1fa132e 100644 --- a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/TransactionsPerSecondForceSyncBenchmark.java +++ b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/TransactionsPerSecondForceSyncBenchmark.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2020 Eclipse RDF4J contributors. + * Copyright (c) 2021 Eclipse RDF4J contributors. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Distribution License v1.0 @@ -11,28 +11,15 @@ package org.eclipse.rdf4j.sail.nativerdf.benchmark; -import java.io.File; -import java.io.IOException; import java.util.concurrent.TimeUnit; -import org.apache.commons.io.FileUtils; -import org.assertj.core.util.Files; -import org.eclipse.rdf4j.common.transaction.IsolationLevels; -import org.eclipse.rdf4j.model.vocabulary.RDFS; -import org.eclipse.rdf4j.repository.sail.SailRepository; -import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection; -import org.eclipse.rdf4j.sail.nativerdf.NativeStore; -import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Level; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; @@ -40,108 +27,27 @@ import org.openjdk.jmh.runner.options.OptionsBuilder; /** - * @author Håvard Ottestad + * Benchmarks insertion performance with synthetic data. */ @State(Scope.Benchmark) -@Warmup(iterations = 20) +@Warmup(iterations = 2) @BenchmarkMode({ Mode.Throughput }) @Fork(value = 1, jvmArgs = { "-Xms8G", "-Xmx8G", "-XX:+UseG1GC" }) -//@Fork(value = 1, jvmArgs = {"-Xms8G", "-Xmx8G", "-XX:+UseG1GC", "-XX:+UnlockCommercialFeatures", "-XX:StartFlightRecording=delay=60s,duration=120s,filename=recording.jfr,settings=profile", "-XX:FlightRecorderOptions=samplethreads=true,stackdepth=1024", "-XX:+UnlockDiagnosticVMOptions", "-XX:+DebugNonSafepoints"}) -@Measurement(iterations = 10) +// @Fork(value = 1, jvmArgs = {"-Xms8G", "-Xmx8G", "-XX:+UseG1GC", "-XX:+UnlockCommercialFeatures", "-XX:StartFlightRecording=delay=60s,duration=120s,filename=recording.jfr,settings=profile", "-XX:FlightRecorderOptions=samplethreads=true,stackdepth=1024", "-XX:+UnlockDiagnosticVMOptions", "-XX:+DebugNonSafepoints"}) +@Measurement(iterations = 5) @OutputTimeUnit(TimeUnit.SECONDS) -public class TransactionsPerSecondForceSyncBenchmark { - - private SailRepository repository; - private File file; - - SailRepositoryConnection connection; - int i; +public class TransactionsPerSecondForceSyncBenchmark extends TransactionsPerSecondBenchmark { + { + // enforce syncing to disk + forceSync = true; + } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() - .include("TransactionsPerSecondForceSyncBenchmark") // adapt to control which benchmark tests to run + .include("TransactionsPerSecondForceSyncBenchmark\\.") // adapt to control which benchmarks to run .forks(1) .build(); new Runner(opt).run(); } - - @Setup(Level.Iteration) - public void beforeClass() { - if (connection != null) { - connection.close(); - connection = null; - } - i = 0; - file = Files.newTemporaryFolder(); - - NativeStore sail = new NativeStore(file, "spoc,ospc,psoc"); - sail.setForceSync(true); - repository = new SailRepository(sail); - connection = repository.getConnection(); - - System.gc(); - - } - - @TearDown(Level.Iteration) - public void afterClass() throws IOException { - if (connection != null) { - connection.close(); - connection = null; - } - repository.shutDown(); - FileUtils.deleteDirectory(file); - - } - - @Benchmark - public void transactions() { - connection.begin(); - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++)); - connection.commit(); - } - - @Benchmark - public void transactionsLevelNone() { - connection.begin(IsolationLevels.NONE); - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++)); - connection.commit(); - } - - @Benchmark - public void mediumTransactionsLevelNone() { - connection.begin(IsolationLevels.NONE); - for (int k = 0; k < 10; k++) { - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++ + "_" + k)); - } - connection.commit(); - } - - @Benchmark - public void largerTransaction() { - connection.begin(); - for (int k = 0; k < 10000; k++) { - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++ + "_" + k)); - } - connection.commit(); - } - - @Benchmark - public void largerTransactionLevelNone() { - connection.begin(IsolationLevels.NONE); - for (int k = 0; k < 10000; k++) { - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++ + "_" + k)); - } - connection.commit(); - } - - @Benchmark - public void veryLargerTransactionLevelNone() { - connection.begin(IsolationLevels.NONE); - for (int k = 0; k < 1000000; k++) { - connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++ + "_" + k)); - } - connection.commit(); - } } From 8ca368294489614ec8387aa8bf2a085a3abc7bb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Mon, 21 Jul 2025 18:01:40 +0200 Subject: [PATCH 16/46] set correct version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Håvard Ottestad --- assembly-descriptors/pom.xml | 2 +- assembly/pom.xml | 2 +- bom/pom.xml | 2 +- compliance/elasticsearch/pom.xml | 2 +- compliance/geosparql/pom.xml | 2 +- compliance/lucene/pom.xml | 2 +- compliance/model/pom.xml | 2 +- compliance/pom.xml | 2 +- compliance/repository/pom.xml | 2 +- compliance/rio/pom.xml | 2 +- compliance/solr/pom.xml | 2 +- compliance/sparql/pom.xml | 2 +- core/client/pom.xml | 2 +- core/collection-factory/api/pom.xml | 2 +- core/collection-factory/mapdb/pom.xml | 2 +- core/collection-factory/mapdb3/pom.xml | 2 +- core/collection-factory/pom.xml | 2 +- core/common/annotation/pom.xml | 2 +- core/common/exception/pom.xml | 2 +- core/common/io/pom.xml | 2 +- core/common/iterator/pom.xml | 2 +- core/common/order/pom.xml | 2 +- core/common/pom.xml | 2 +- core/common/text/pom.xml | 2 +- core/common/transaction/pom.xml | 2 +- core/common/xml/pom.xml | 2 +- core/http/client/pom.xml | 2 +- core/http/pom.xml | 2 +- core/http/protocol/pom.xml | 2 +- core/model-api/pom.xml | 2 +- core/model-vocabulary/pom.xml | 2 +- core/model/pom.xml | 2 +- core/pom.xml | 2 +- core/query/pom.xml | 2 +- core/queryalgebra/evaluation/pom.xml | 2 +- core/queryalgebra/geosparql/pom.xml | 2 +- core/queryalgebra/model/pom.xml | 2 +- core/queryalgebra/pom.xml | 2 +- core/queryparser/api/pom.xml | 2 +- core/queryparser/pom.xml | 2 +- core/queryparser/sparql/pom.xml | 2 +- core/queryrender/pom.xml | 2 +- core/queryresultio/api/pom.xml | 2 +- core/queryresultio/binary/pom.xml | 2 +- core/queryresultio/pom.xml | 2 +- core/queryresultio/sparqljson/pom.xml | 2 +- core/queryresultio/sparqlxml/pom.xml | 2 +- core/queryresultio/text/pom.xml | 2 +- core/repository/api/pom.xml | 2 +- core/repository/contextaware/pom.xml | 2 +- core/repository/dataset/pom.xml | 2 +- core/repository/event/pom.xml | 2 +- core/repository/http/pom.xml | 2 +- core/repository/manager/pom.xml | 2 +- core/repository/pom.xml | 2 +- core/repository/sail/pom.xml | 2 +- core/repository/sparql/pom.xml | 2 +- core/rio/api/pom.xml | 2 +- core/rio/binary/pom.xml | 2 +- core/rio/datatypes/pom.xml | 2 +- core/rio/hdt/pom.xml | 2 +- core/rio/jsonld-legacy/pom.xml | 2 +- core/rio/jsonld/pom.xml | 2 +- core/rio/languages/pom.xml | 2 +- core/rio/n3/pom.xml | 2 +- core/rio/nquads/pom.xml | 2 +- core/rio/ntriples/pom.xml | 2 +- core/rio/pom.xml | 2 +- core/rio/rdfjson/pom.xml | 2 +- core/rio/rdfxml/pom.xml | 2 +- core/rio/trig/pom.xml | 2 +- core/rio/trix/pom.xml | 2 +- core/rio/turtle/pom.xml | 2 +- core/sail/api/pom.xml | 2 +- core/sail/base/pom.xml | 2 +- core/sail/elasticsearch-store/pom.xml | 2 +- core/sail/elasticsearch/pom.xml | 2 +- core/sail/extensible-store/pom.xml | 2 +- core/sail/inferencer/pom.xml | 2 +- core/sail/lmdb/pom.xml | 2 +- core/sail/lucene-api/pom.xml | 2 +- core/sail/lucene/pom.xml | 2 +- core/sail/memory/pom.xml | 2 +- core/sail/model/pom.xml | 2 +- core/sail/nativerdf/pom.xml | 2 +- core/sail/pom.xml | 2 +- core/sail/shacl/pom.xml | 2 +- core/sail/solr/pom.xml | 2 +- core/sparqlbuilder/pom.xml | 2 +- core/spin/pom.xml | 2 +- core/storage/pom.xml | 2 +- examples/pom.xml | 2 +- pom.xml | 2 +- spring-components/pom.xml | 2 +- spring-components/rdf4j-spring-demo/pom.xml | 2 +- spring-components/rdf4j-spring/pom.xml | 2 +- spring-components/spring-boot-sparql-web/pom.xml | 2 +- testsuites/benchmark/pom.xml | 2 +- testsuites/geosparql/pom.xml | 2 +- testsuites/lucene/pom.xml | 2 +- testsuites/model/pom.xml | 2 +- testsuites/pom.xml | 2 +- testsuites/queryresultio/pom.xml | 2 +- testsuites/repository/pom.xml | 2 +- testsuites/rio/pom.xml | 2 +- testsuites/sail/pom.xml | 2 +- testsuites/sparql/pom.xml | 2 +- tools/config/pom.xml | 2 +- tools/console/pom.xml | 2 +- tools/federation/pom.xml | 2 +- tools/pom.xml | 2 +- tools/runtime-osgi/pom.xml | 2 +- tools/runtime/pom.xml | 2 +- tools/server-spring/pom.xml | 2 +- tools/server/pom.xml | 2 +- tools/workbench/pom.xml | 2 +- 116 files changed, 116 insertions(+), 116 deletions(-) diff --git a/assembly-descriptors/pom.xml b/assembly-descriptors/pom.xml index eb97442e1cb..25b0b093889 100644 --- a/assembly-descriptors/pom.xml +++ b/assembly-descriptors/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-assembly-descriptors RDF4J: Assembly Descriptors diff --git a/assembly/pom.xml b/assembly/pom.xml index 19f77377ea9..a88d567f0e5 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-assembly pom diff --git a/bom/pom.xml b/bom/pom.xml index 35a9d4e2d46..8056082ccc1 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-bom pom diff --git a/compliance/elasticsearch/pom.xml b/compliance/elasticsearch/pom.xml index 00fa4f1836a..794dacb7bd7 100644 --- a/compliance/elasticsearch/pom.xml +++ b/compliance/elasticsearch/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-elasticsearch-compliance RDF4J: Elasticsearch Sail Tests diff --git a/compliance/geosparql/pom.xml b/compliance/geosparql/pom.xml index 4ffc59e5cc4..41e8785595b 100644 --- a/compliance/geosparql/pom.xml +++ b/compliance/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-geosparql-compliance RDF4J: GeoSPARQL compliance tests diff --git a/compliance/lucene/pom.xml b/compliance/lucene/pom.xml index 62d4979157b..dcf84054c6d 100644 --- a/compliance/lucene/pom.xml +++ b/compliance/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-lucene-compliance RDF4J: Lucene Sail Tests diff --git a/compliance/model/pom.xml b/compliance/model/pom.xml index 2ab4e7f0b71..3d3ac105fa9 100644 --- a/compliance/model/pom.xml +++ b/compliance/model/pom.xml @@ -3,7 +3,7 @@ rdf4j-compliance org.eclipse.rdf4j - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT 4.0.0 rdf4j-model-compliance diff --git a/compliance/pom.xml b/compliance/pom.xml index 7578bd85172..7a3f7a3e7ff 100644 --- a/compliance/pom.xml +++ b/compliance/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-compliance pom diff --git a/compliance/repository/pom.xml b/compliance/repository/pom.xml index 22efe6cd537..069ade53eec 100644 --- a/compliance/repository/pom.xml +++ b/compliance/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-compliance war diff --git a/compliance/rio/pom.xml b/compliance/rio/pom.xml index 03f1b68cfd4..850062bf2f4 100644 --- a/compliance/rio/pom.xml +++ b/compliance/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-compliance RDF4J: Rio compliance tests diff --git a/compliance/solr/pom.xml b/compliance/solr/pom.xml index 2b51927b0b0..454f5295ca6 100644 --- a/compliance/solr/pom.xml +++ b/compliance/solr/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-solr-compliance RDF4J: Solr Sail Tests diff --git a/compliance/sparql/pom.xml b/compliance/sparql/pom.xml index 9fce2bea097..3340661c18d 100644 --- a/compliance/sparql/pom.xml +++ b/compliance/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparql-compliance war diff --git a/core/client/pom.xml b/core/client/pom.xml index e033886df52..845360a26cc 100644 --- a/core/client/pom.xml +++ b/core/client/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-client RDF4J: Client Libraries diff --git a/core/collection-factory/api/pom.xml b/core/collection-factory/api/pom.xml index dd40f98ab4c..5ba6df646b7 100644 --- a/core/collection-factory/api/pom.xml +++ b/core/collection-factory/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-api RDF4J: Collection Factory - API diff --git a/core/collection-factory/mapdb/pom.xml b/core/collection-factory/mapdb/pom.xml index 7f996ca053b..c8e843b984e 100644 --- a/core/collection-factory/mapdb/pom.xml +++ b/core/collection-factory/mapdb/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-mapdb RDF4J: Collection Factory - Map DB backed diff --git a/core/collection-factory/mapdb3/pom.xml b/core/collection-factory/mapdb3/pom.xml index bab1ef8edfd..a00d4c8e811 100644 --- a/core/collection-factory/mapdb3/pom.xml +++ b/core/collection-factory/mapdb3/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-mapdb3 RDF4J: Collection Factory - Map DB v3 backed diff --git a/core/collection-factory/pom.xml b/core/collection-factory/pom.xml index 582ba6ca17f..bb1df7bb226 100644 --- a/core/collection-factory/pom.xml +++ b/core/collection-factory/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory pom diff --git a/core/common/annotation/pom.xml b/core/common/annotation/pom.xml index 047fd43731c..f5f53a65e21 100644 --- a/core/common/annotation/pom.xml +++ b/core/common/annotation/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-annotation RDF4J: common annotation diff --git a/core/common/exception/pom.xml b/core/common/exception/pom.xml index 4b4d4f44736..bbd533f1adf 100644 --- a/core/common/exception/pom.xml +++ b/core/common/exception/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-exception RDF4J: common exception diff --git a/core/common/io/pom.xml b/core/common/io/pom.xml index a0dd4425087..f99f7f2af4e 100644 --- a/core/common/io/pom.xml +++ b/core/common/io/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-io RDF4J: common IO diff --git a/core/common/iterator/pom.xml b/core/common/iterator/pom.xml index d34338f41f9..c8b62012574 100644 --- a/core/common/iterator/pom.xml +++ b/core/common/iterator/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-iterator RDF4J: common iterators diff --git a/core/common/order/pom.xml b/core/common/order/pom.xml index f972a895b87..5228081fc0e 100644 --- a/core/common/order/pom.xml +++ b/core/common/order/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-order RDF4J: common order diff --git a/core/common/pom.xml b/core/common/pom.xml index 0d2373a5f20..fc821867500 100644 --- a/core/common/pom.xml +++ b/core/common/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common pom diff --git a/core/common/text/pom.xml b/core/common/text/pom.xml index aad04e4bfde..f26391a3d11 100644 --- a/core/common/text/pom.xml +++ b/core/common/text/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-text RDF4J: common text diff --git a/core/common/transaction/pom.xml b/core/common/transaction/pom.xml index 0545c27753b..2a327eba54c 100644 --- a/core/common/transaction/pom.xml +++ b/core/common/transaction/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-transaction RDF4J: common transaction diff --git a/core/common/xml/pom.xml b/core/common/xml/pom.xml index a4754d58bfe..76184d6e127 100644 --- a/core/common/xml/pom.xml +++ b/core/common/xml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-xml RDF4J: common XML diff --git a/core/http/client/pom.xml b/core/http/client/pom.xml index 5b4ea95a073..8e26e6c8d3f 100644 --- a/core/http/client/pom.xml +++ b/core/http/client/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-http - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-client RDF4J: HTTP client diff --git a/core/http/pom.xml b/core/http/pom.xml index 9d4e358ca2c..ee3e0261ad0 100644 --- a/core/http/pom.xml +++ b/core/http/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http pom diff --git a/core/http/protocol/pom.xml b/core/http/protocol/pom.xml index 5fbd6252d52..a5d66a693e0 100644 --- a/core/http/protocol/pom.xml +++ b/core/http/protocol/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-http - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-protocol RDF4J: HTTP protocol diff --git a/core/model-api/pom.xml b/core/model-api/pom.xml index 344bef6aeb5..cf07e38798c 100644 --- a/core/model-api/pom.xml +++ b/core/model-api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-api RDF4J: Model API diff --git a/core/model-vocabulary/pom.xml b/core/model-vocabulary/pom.xml index a58fa227bb9..bd27791c1d8 100644 --- a/core/model-vocabulary/pom.xml +++ b/core/model-vocabulary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-vocabulary RDF4J: RDF Vocabularies diff --git a/core/model/pom.xml b/core/model/pom.xml index 4e3a3ce0510..17a4deb868b 100644 --- a/core/model/pom.xml +++ b/core/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model RDF4J: Model diff --git a/core/pom.xml b/core/pom.xml index 82eda0e845a..ed94faedcde 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-core pom diff --git a/core/query/pom.xml b/core/query/pom.xml index 635c540d6d9..6600bd51a4e 100644 --- a/core/query/pom.xml +++ b/core/query/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-query RDF4J: Query diff --git a/core/queryalgebra/evaluation/pom.xml b/core/queryalgebra/evaluation/pom.xml index 52e209ae65f..a29c5f1ab29 100644 --- a/core/queryalgebra/evaluation/pom.xml +++ b/core/queryalgebra/evaluation/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-evaluation RDF4J: Query algebra - evaluation diff --git a/core/queryalgebra/geosparql/pom.xml b/core/queryalgebra/geosparql/pom.xml index 995c72b0c60..10fcbc837ce 100644 --- a/core/queryalgebra/geosparql/pom.xml +++ b/core/queryalgebra/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-geosparql RDF4J: Query algebra - GeoSPARQL diff --git a/core/queryalgebra/model/pom.xml b/core/queryalgebra/model/pom.xml index f140b57a3ce..c561441a066 100644 --- a/core/queryalgebra/model/pom.xml +++ b/core/queryalgebra/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-model RDF4J: Query algebra - model diff --git a/core/queryalgebra/pom.xml b/core/queryalgebra/pom.xml index 9f8ff2e0a61..fb04d8339cd 100644 --- a/core/queryalgebra/pom.xml +++ b/core/queryalgebra/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra pom diff --git a/core/queryparser/api/pom.xml b/core/queryparser/api/pom.xml index c72d94344e8..6450c375f42 100644 --- a/core/queryparser/api/pom.xml +++ b/core/queryparser/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryparser - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser-api RDF4J: Query parser - API diff --git a/core/queryparser/pom.xml b/core/queryparser/pom.xml index d282ae29a4e..0dfcb8573d3 100644 --- a/core/queryparser/pom.xml +++ b/core/queryparser/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser pom diff --git a/core/queryparser/sparql/pom.xml b/core/queryparser/sparql/pom.xml index e1f61a5e3bc..d21ecf2b1b2 100644 --- a/core/queryparser/sparql/pom.xml +++ b/core/queryparser/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryparser - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser-sparql RDF4J: Query parser - SPARQL diff --git a/core/queryrender/pom.xml b/core/queryrender/pom.xml index 22cbba0a139..8db12169a80 100644 --- a/core/queryrender/pom.xml +++ b/core/queryrender/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryrender RDF4J: Query Rendering diff --git a/core/queryresultio/api/pom.xml b/core/queryresultio/api/pom.xml index ceec500704d..273fe3b9801 100644 --- a/core/queryresultio/api/pom.xml +++ b/core/queryresultio/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-api RDF4J: Query result IO - API diff --git a/core/queryresultio/binary/pom.xml b/core/queryresultio/binary/pom.xml index 1a8df138035..34df6c0b64f 100644 --- a/core/queryresultio/binary/pom.xml +++ b/core/queryresultio/binary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-binary RDF4J: Query result IO - binary diff --git a/core/queryresultio/pom.xml b/core/queryresultio/pom.xml index 17a677d19fb..c1ba95a8c53 100644 --- a/core/queryresultio/pom.xml +++ b/core/queryresultio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio pom diff --git a/core/queryresultio/sparqljson/pom.xml b/core/queryresultio/sparqljson/pom.xml index c8968b70483..c5f589b6457 100644 --- a/core/queryresultio/sparqljson/pom.xml +++ b/core/queryresultio/sparqljson/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-sparqljson RDF4J: Query result IO - SPARQL/JSON diff --git a/core/queryresultio/sparqlxml/pom.xml b/core/queryresultio/sparqlxml/pom.xml index 79b3f6a2a92..8d7bee2745b 100644 --- a/core/queryresultio/sparqlxml/pom.xml +++ b/core/queryresultio/sparqlxml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-sparqlxml RDF4J: Query result IO - SPARQL/XML diff --git a/core/queryresultio/text/pom.xml b/core/queryresultio/text/pom.xml index dda8175896f..e5f67e0b32e 100644 --- a/core/queryresultio/text/pom.xml +++ b/core/queryresultio/text/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-text RDF4J: Query result IO - plain text booleans diff --git a/core/repository/api/pom.xml b/core/repository/api/pom.xml index e0baf93690c..48fdeecfad7 100644 --- a/core/repository/api/pom.xml +++ b/core/repository/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-api RDF4J: Repository - API diff --git a/core/repository/contextaware/pom.xml b/core/repository/contextaware/pom.xml index badd8f4602f..39bcbf0668c 100644 --- a/core/repository/contextaware/pom.xml +++ b/core/repository/contextaware/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-contextaware RDF4J: Repository - context aware (wrapper) diff --git a/core/repository/dataset/pom.xml b/core/repository/dataset/pom.xml index 0cc66066b70..c843dccda42 100644 --- a/core/repository/dataset/pom.xml +++ b/core/repository/dataset/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-dataset RDF4J: DatasetRepository (wrapper) diff --git a/core/repository/event/pom.xml b/core/repository/event/pom.xml index dfa470ea875..3a3109cf967 100644 --- a/core/repository/event/pom.xml +++ b/core/repository/event/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-event RDF4J: Repository - event (wrapper) diff --git a/core/repository/http/pom.xml b/core/repository/http/pom.xml index 3088f5ce702..4fc07dc06e9 100644 --- a/core/repository/http/pom.xml +++ b/core/repository/http/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-http RDF4J: HTTPRepository diff --git a/core/repository/manager/pom.xml b/core/repository/manager/pom.xml index 78b50e46d45..bad53d7101e 100644 --- a/core/repository/manager/pom.xml +++ b/core/repository/manager/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-manager RDF4J: Repository manager diff --git a/core/repository/pom.xml b/core/repository/pom.xml index 33b17ad0162..873757a9580 100644 --- a/core/repository/pom.xml +++ b/core/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository pom diff --git a/core/repository/sail/pom.xml b/core/repository/sail/pom.xml index ad2a8dcd0c5..fb8fc483b2b 100644 --- a/core/repository/sail/pom.xml +++ b/core/repository/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-sail RDF4J: SailRepository diff --git a/core/repository/sparql/pom.xml b/core/repository/sparql/pom.xml index 423477c2978..66d5ee263ce 100644 --- a/core/repository/sparql/pom.xml +++ b/core/repository/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-sparql RDF4J: SPARQL Repository diff --git a/core/rio/api/pom.xml b/core/rio/api/pom.xml index 38dd0f527cf..3733b074423 100644 --- a/core/rio/api/pom.xml +++ b/core/rio/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-api RDF4J: Rio - API diff --git a/core/rio/binary/pom.xml b/core/rio/binary/pom.xml index a55c22103dd..4ff5469eddf 100644 --- a/core/rio/binary/pom.xml +++ b/core/rio/binary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-binary RDF4J: Rio - Binary diff --git a/core/rio/datatypes/pom.xml b/core/rio/datatypes/pom.xml index 8076ab49477..b9d9dcadcfb 100644 --- a/core/rio/datatypes/pom.xml +++ b/core/rio/datatypes/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-datatypes RDF4J: Rio - Datatypes diff --git a/core/rio/hdt/pom.xml b/core/rio/hdt/pom.xml index 6d1f19ea39c..f3647663a65 100644 --- a/core/rio/hdt/pom.xml +++ b/core/rio/hdt/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-hdt jar diff --git a/core/rio/jsonld-legacy/pom.xml b/core/rio/jsonld-legacy/pom.xml index 12e15ff0f1b..7509ef4b834 100644 --- a/core/rio/jsonld-legacy/pom.xml +++ b/core/rio/jsonld-legacy/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-jsonld-legacy RDF4J: Rio - JSON-LD 1.0 (legacy) diff --git a/core/rio/jsonld/pom.xml b/core/rio/jsonld/pom.xml index 1969e4cde46..998013a10ec 100644 --- a/core/rio/jsonld/pom.xml +++ b/core/rio/jsonld/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-jsonld RDF4J: Rio - JSON-LD diff --git a/core/rio/languages/pom.xml b/core/rio/languages/pom.xml index e10c2c57053..b35931702bb 100644 --- a/core/rio/languages/pom.xml +++ b/core/rio/languages/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-languages RDF4J: Rio - Languages diff --git a/core/rio/n3/pom.xml b/core/rio/n3/pom.xml index 90ba130bd98..54ae5fe573c 100644 --- a/core/rio/n3/pom.xml +++ b/core/rio/n3/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-n3 RDF4J: Rio - N3 (writer-only) diff --git a/core/rio/nquads/pom.xml b/core/rio/nquads/pom.xml index 8a939a96bd8..044216524fb 100644 --- a/core/rio/nquads/pom.xml +++ b/core/rio/nquads/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-nquads RDF4J: Rio - N-Quads diff --git a/core/rio/ntriples/pom.xml b/core/rio/ntriples/pom.xml index 0c303d36257..37553504ed5 100644 --- a/core/rio/ntriples/pom.xml +++ b/core/rio/ntriples/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-ntriples RDF4J: Rio - N-Triples diff --git a/core/rio/pom.xml b/core/rio/pom.xml index 215e3902e7d..77fc1cee292 100644 --- a/core/rio/pom.xml +++ b/core/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio pom diff --git a/core/rio/rdfjson/pom.xml b/core/rio/rdfjson/pom.xml index e15397b0632..55a3e3ad374 100644 --- a/core/rio/rdfjson/pom.xml +++ b/core/rio/rdfjson/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-rdfjson RDF4J: Rio - RDF/JSON diff --git a/core/rio/rdfxml/pom.xml b/core/rio/rdfxml/pom.xml index a24e5f9e6ad..08a1e0f6eb3 100644 --- a/core/rio/rdfxml/pom.xml +++ b/core/rio/rdfxml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-rdfxml RDF4J: Rio - RDF/XML diff --git a/core/rio/trig/pom.xml b/core/rio/trig/pom.xml index 6326fbb8277..2736b12a916 100644 --- a/core/rio/trig/pom.xml +++ b/core/rio/trig/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-trig RDF4J: Rio - TriG diff --git a/core/rio/trix/pom.xml b/core/rio/trix/pom.xml index 7a7acbbeee4..a8b99df7634 100644 --- a/core/rio/trix/pom.xml +++ b/core/rio/trix/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-trix RDF4J: Rio - TriX diff --git a/core/rio/turtle/pom.xml b/core/rio/turtle/pom.xml index 42c843a6144..087f4221601 100644 --- a/core/rio/turtle/pom.xml +++ b/core/rio/turtle/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-turtle RDF4J: Rio - Turtle diff --git a/core/sail/api/pom.xml b/core/sail/api/pom.xml index d9af73881ef..94ba13deba0 100644 --- a/core/sail/api/pom.xml +++ b/core/sail/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-api RDF4J: Sail API diff --git a/core/sail/base/pom.xml b/core/sail/base/pom.xml index 7448e338277..37f440d24a5 100644 --- a/core/sail/base/pom.xml +++ b/core/sail/base/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-base RDF4J: Sail base implementations diff --git a/core/sail/elasticsearch-store/pom.xml b/core/sail/elasticsearch-store/pom.xml index 320f27dfdfd..8542f4a1aaa 100644 --- a/core/sail/elasticsearch-store/pom.xml +++ b/core/sail/elasticsearch-store/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-elasticsearch-store RDF4J: Elasticsearch Store diff --git a/core/sail/elasticsearch/pom.xml b/core/sail/elasticsearch/pom.xml index 2c59d4b512a..022319c4697 100644 --- a/core/sail/elasticsearch/pom.xml +++ b/core/sail/elasticsearch/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-elasticsearch RDF4J: Elastic Search Sail Index diff --git a/core/sail/extensible-store/pom.xml b/core/sail/extensible-store/pom.xml index b6513200407..e8d6a1af491 100644 --- a/core/sail/extensible-store/pom.xml +++ b/core/sail/extensible-store/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-extensible-store RDF4J: Extensible Store diff --git a/core/sail/inferencer/pom.xml b/core/sail/inferencer/pom.xml index e3a5396fb3c..45babf4a3e2 100644 --- a/core/sail/inferencer/pom.xml +++ b/core/sail/inferencer/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-inferencer RDF4J: Inferencer Sails diff --git a/core/sail/lmdb/pom.xml b/core/sail/lmdb/pom.xml index 85bc8bb4849..787fb19c4d4 100644 --- a/core/sail/lmdb/pom.xml +++ b/core/sail/lmdb/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lmdb RDF4J: LmdbStore diff --git a/core/sail/lucene-api/pom.xml b/core/sail/lucene-api/pom.xml index cc5ec07fbb1..a52d440fc00 100644 --- a/core/sail/lucene-api/pom.xml +++ b/core/sail/lucene-api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lucene-api RDF4J: Lucene Sail API diff --git a/core/sail/lucene/pom.xml b/core/sail/lucene/pom.xml index 6fd90f44ad5..530c6ac3c5d 100644 --- a/core/sail/lucene/pom.xml +++ b/core/sail/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lucene RDF4J: Lucene Sail Index diff --git a/core/sail/memory/pom.xml b/core/sail/memory/pom.xml index 1bba62bd693..ae81a8c5531 100644 --- a/core/sail/memory/pom.xml +++ b/core/sail/memory/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-memory RDF4J: MemoryStore diff --git a/core/sail/model/pom.xml b/core/sail/model/pom.xml index 17a11049da0..531c473d10a 100644 --- a/core/sail/model/pom.xml +++ b/core/sail/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-model RDF4J: Sail Model diff --git a/core/sail/nativerdf/pom.xml b/core/sail/nativerdf/pom.xml index d65d26ba0ed..17dbcc1c962 100644 --- a/core/sail/nativerdf/pom.xml +++ b/core/sail/nativerdf/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-nativerdf RDF4J: NativeStore diff --git a/core/sail/pom.xml b/core/sail/pom.xml index c33139b9b01..cbce9f38bc7 100644 --- a/core/sail/pom.xml +++ b/core/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail pom diff --git a/core/sail/shacl/pom.xml b/core/sail/shacl/pom.xml index ebb430e1d45..9cae94fe046 100644 --- a/core/sail/shacl/pom.xml +++ b/core/sail/shacl/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-shacl RDF4J: SHACL diff --git a/core/sail/solr/pom.xml b/core/sail/solr/pom.xml index 103a53419ca..c79bfa393e7 100644 --- a/core/sail/solr/pom.xml +++ b/core/sail/solr/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-solr RDF4J: Solr Sail Index diff --git a/core/sparqlbuilder/pom.xml b/core/sparqlbuilder/pom.xml index c93d784203b..2e589196e67 100644 --- a/core/sparqlbuilder/pom.xml +++ b/core/sparqlbuilder/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparqlbuilder RDF4J: SparqlBuilder diff --git a/core/spin/pom.xml b/core/spin/pom.xml index 017c5ecf8b7..b80e9d40e04 100644 --- a/core/spin/pom.xml +++ b/core/spin/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spin RDF4J: SPIN diff --git a/core/storage/pom.xml b/core/storage/pom.xml index 215a3819ff5..6775e4bf837 100644 --- a/core/storage/pom.xml +++ b/core/storage/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-storage RDF4J: Storage Libraries diff --git a/examples/pom.xml b/examples/pom.xml index 6ef7a310312..5610e589454 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -7,7 +7,7 @@ org.eclipse.rdf4j rdf4j - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/pom.xml b/pom.xml index 92b7b04dec0..87e98c6a41b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.eclipse.rdf4j rdf4j - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT pom Eclipse RDF4J An extensible Java framework for RDF and SPARQL diff --git a/spring-components/pom.xml b/spring-components/pom.xml index ccf338f2fe1..571eebb5bce 100644 --- a/spring-components/pom.xml +++ b/spring-components/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT pom diff --git a/spring-components/rdf4j-spring-demo/pom.xml b/spring-components/rdf4j-spring-demo/pom.xml index 84faab405cb..e3376a0dd51 100644 --- a/spring-components/rdf4j-spring-demo/pom.xml +++ b/spring-components/rdf4j-spring-demo/pom.xml @@ -7,7 +7,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/spring-components/rdf4j-spring/pom.xml b/spring-components/rdf4j-spring/pom.xml index 1f22494c57f..b3616e04951 100644 --- a/spring-components/rdf4j-spring/pom.xml +++ b/spring-components/rdf4j-spring/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spring RDF4J: Spring diff --git a/spring-components/spring-boot-sparql-web/pom.xml b/spring-components/spring-boot-sparql-web/pom.xml index 47464b78df1..222e9020799 100644 --- a/spring-components/spring-boot-sparql-web/pom.xml +++ b/spring-components/spring-boot-sparql-web/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spring-boot-sparql-web RDF4J: Spring boot component for a HTTP sparql server diff --git a/testsuites/benchmark/pom.xml b/testsuites/benchmark/pom.xml index 9377ba64bad..96c07db5f28 100644 --- a/testsuites/benchmark/pom.xml +++ b/testsuites/benchmark/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-benchmark RDF4J: benchmarks diff --git a/testsuites/geosparql/pom.xml b/testsuites/geosparql/pom.xml index 06ada7f3c46..3ebb0735c3e 100644 --- a/testsuites/geosparql/pom.xml +++ b/testsuites/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-geosparql-testsuite RDF4J: GeoSPARQL compliance test suite diff --git a/testsuites/lucene/pom.xml b/testsuites/lucene/pom.xml index 8bf3fe1800e..66488c8e819 100644 --- a/testsuites/lucene/pom.xml +++ b/testsuites/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-lucene-testsuite RDF4J: Lucene Sail Tests diff --git a/testsuites/model/pom.xml b/testsuites/model/pom.xml index c58f3c946a8..cc4dc7ab767 100644 --- a/testsuites/model/pom.xml +++ b/testsuites/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-testsuite RDF4J: Model API testsuite diff --git a/testsuites/pom.xml b/testsuites/pom.xml index 1e6eb6920ee..42088f21a4d 100644 --- a/testsuites/pom.xml +++ b/testsuites/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-testsuites pom diff --git a/testsuites/queryresultio/pom.xml b/testsuites/queryresultio/pom.xml index 0626b974597..feaf586b953 100644 --- a/testsuites/queryresultio/pom.xml +++ b/testsuites/queryresultio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-testsuite RDF4J: QueryResultIO testsuite diff --git a/testsuites/repository/pom.xml b/testsuites/repository/pom.xml index 5f2ade095f3..d58184a0a20 100644 --- a/testsuites/repository/pom.xml +++ b/testsuites/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-testsuite RDF4J: Repository API testsuite diff --git a/testsuites/rio/pom.xml b/testsuites/rio/pom.xml index aaaa5c2e588..8bab2f967d5 100644 --- a/testsuites/rio/pom.xml +++ b/testsuites/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-testsuite RDF4J: Rio compliance test suite diff --git a/testsuites/sail/pom.xml b/testsuites/sail/pom.xml index 22d9a1d3d3a..4b9b7d6c2d1 100644 --- a/testsuites/sail/pom.xml +++ b/testsuites/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-testsuite RDF4J: Sail API testsuite diff --git a/testsuites/sparql/pom.xml b/testsuites/sparql/pom.xml index 74b8720ac5a..6d81c721943 100644 --- a/testsuites/sparql/pom.xml +++ b/testsuites/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparql-testsuite RDF4J: SPARQL compliance test suite diff --git a/tools/config/pom.xml b/tools/config/pom.xml index 8ff3e1f204a..c6e819ebcc0 100644 --- a/tools/config/pom.xml +++ b/tools/config/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-config RDF4J: application configuration diff --git a/tools/console/pom.xml b/tools/console/pom.xml index adbab917d32..a342f03ad96 100644 --- a/tools/console/pom.xml +++ b/tools/console/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-console RDF4J: Console diff --git a/tools/federation/pom.xml b/tools/federation/pom.xml index 389e777dc4f..5430f89d375 100644 --- a/tools/federation/pom.xml +++ b/tools/federation/pom.xml @@ -8,7 +8,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/tools/pom.xml b/tools/pom.xml index c252e2cbd34..08778066c03 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-tools pom diff --git a/tools/runtime-osgi/pom.xml b/tools/runtime-osgi/pom.xml index 43293b3f970..642959844af 100644 --- a/tools/runtime-osgi/pom.xml +++ b/tools/runtime-osgi/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-runtime-osgi bundle diff --git a/tools/runtime/pom.xml b/tools/runtime/pom.xml index 87e7ff41f4a..ccb07aa2992 100644 --- a/tools/runtime/pom.xml +++ b/tools/runtime/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-runtime RDF4J: Runtime diff --git a/tools/server-spring/pom.xml b/tools/server-spring/pom.xml index c82299b9770..a3cba434f29 100644 --- a/tools/server-spring/pom.xml +++ b/tools/server-spring/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-server-spring RDF4J: HTTP server - core diff --git a/tools/server/pom.xml b/tools/server/pom.xml index 3dd16cf1363..5e6fc517570 100644 --- a/tools/server/pom.xml +++ b/tools/server/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-server war diff --git a/tools/workbench/pom.xml b/tools/workbench/pom.xml index d2bc0b3e7ef..cabca3a9a48 100644 --- a/tools/workbench/pom.xml +++ b/tools/workbench/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.5-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-workbench war From 405d6ce6c1e032a58a20d770af8dd0a639a3c756 Mon Sep 17 00:00:00 2001 From: Jerven Bolleman Date: Thu, 4 Sep 2025 07:08:06 +0200 Subject: [PATCH 17/46] GH-5290 xlsx and ods query result format Uses Apache POI for xlsx support, very basic formatting. Rather basic reverse engineered ODF Spreadsheet via LibreOffice converting the XLSX. Signed-off-by: Jerven Bolleman --- .../resultio/TupleQueryResultFormat.java | 6 + core/queryresultio/ods/pom.xml | 40 + .../sparqlods/SPARQLResultsODSWriter.java | 1014 +++++++++++++++++ .../SPARQLResultsODSWriterFactory.java | 37 + .../query/resultio/sparqlods/package.html | 6 + .../sparqlxml/SPARQLODSTupleTest.java | 83 ++ core/queryresultio/pom.xml | 2 + core/queryresultio/xlsx/pom.xml | 50 + .../sparqlxslx/SPARQLResultsXLSXWriter.java | 415 +++++++ .../SPARQLResultsXLSXWriterFactory.java | 37 + .../query/resultio/sparqlxslx/package.html | 6 + ...ery.resultio.TupleQueryResultWriterFactory | 1 + .../sparqlxml/SPARQLXLSXTupleTest.java | 89 ++ pom.xml | 3 +- 14 files changed, 1788 insertions(+), 1 deletion(-) create mode 100644 core/queryresultio/ods/pom.xml create mode 100644 core/queryresultio/ods/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlods/SPARQLResultsODSWriter.java create mode 100644 core/queryresultio/ods/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlods/SPARQLResultsODSWriterFactory.java create mode 100644 core/queryresultio/ods/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlods/package.html create mode 100644 core/queryresultio/ods/src/test/java/org/eclipse/rdf4j/query/resultio/sparqlxml/SPARQLODSTupleTest.java create mode 100644 core/queryresultio/xlsx/pom.xml create mode 100644 core/queryresultio/xlsx/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlxslx/SPARQLResultsXLSXWriter.java create mode 100644 core/queryresultio/xlsx/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlxslx/SPARQLResultsXLSXWriterFactory.java create mode 100644 core/queryresultio/xlsx/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlxslx/package.html create mode 100644 core/queryresultio/xlsx/src/main/resources/META-INF/services/org.eclipse.rdf4j.query.resultio.TupleQueryResultWriterFactory create mode 100644 core/queryresultio/xlsx/src/test/java/org/eclipse/rdf4j/query/resultio/sparqlxml/SPARQLXLSXTupleTest.java diff --git a/core/queryresultio/api/src/main/java/org/eclipse/rdf4j/query/resultio/TupleQueryResultFormat.java b/core/queryresultio/api/src/main/java/org/eclipse/rdf4j/query/resultio/TupleQueryResultFormat.java index 1ef89ccee24..35ace310d02 100644 --- a/core/queryresultio/api/src/main/java/org/eclipse/rdf4j/query/resultio/TupleQueryResultFormat.java +++ b/core/queryresultio/api/src/main/java/org/eclipse/rdf4j/query/resultio/TupleQueryResultFormat.java @@ -98,6 +98,12 @@ public class TupleQueryResultFormat extends QueryResultFormat { Arrays.asList("text/x-tab-separated-values-star", "application/x-sparqlstar-results+tsv"), StandardCharsets.UTF_8, List.of("tsvs"), null, SUPPORTS_RDF_STAR); + public static final TupleQueryResultFormat XSLX = new TupleQueryResultFormat("SPARQL/XLSX", + "application/vnd.ms-excel", "xlsx"); + + public static final TupleQueryResultFormat ODS = new TupleQueryResultFormat("SPARQL/ODS", + "application/vnd.oasis.opendocument.spreadsheet", "ods"); + /*-----------* * Variables * *-----------*/ diff --git a/core/queryresultio/ods/pom.xml b/core/queryresultio/ods/pom.xml new file mode 100644 index 00000000000..a9ec4418667 --- /dev/null +++ b/core/queryresultio/ods/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + org.eclipse.rdf4j + rdf4j-queryresultio + 5.1.0-SNAPSHOT + + rdf4j-queryresultio-sparqlods + RDF4J: Query result IO - ODS + Query result parser and writer implementation for an non standardized SPARQL Query Results Open Document Format Spreadsheet. + + + ${project.groupId} + rdf4j-queryresultio-api + ${project.version} + + + ${project.groupId} + rdf4j-query + ${project.version} + + + ${project.groupId} + rdf4j-model + ${project.version} + + + ${project.groupId} + rdf4j-common-xml + ${project.version} + + + ${project.groupId} + rdf4j-queryresultio-testsuite + ${project.version} + test + + + diff --git a/core/queryresultio/ods/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlods/SPARQLResultsODSWriter.java b/core/queryresultio/ods/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlods/SPARQLResultsODSWriter.java new file mode 100644 index 00000000000..c71afbd3e93 --- /dev/null +++ b/core/queryresultio/ods/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlods/SPARQLResultsODSWriter.java @@ -0,0 +1,1014 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.query.resultio.sparqlods; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import org.eclipse.rdf4j.common.xml.XMLWriter; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Literal; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.base.CoreDatatype; +import org.eclipse.rdf4j.model.base.CoreDatatype.XSD; +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.query.Binding; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.QueryResultHandlerException; +import org.eclipse.rdf4j.query.TupleQueryResultHandlerException; +import org.eclipse.rdf4j.query.impl.MapBindingSet; +import org.eclipse.rdf4j.query.resultio.QueryResultFormat; +import org.eclipse.rdf4j.query.resultio.TupleQueryResultFormat; +import org.eclipse.rdf4j.query.resultio.TupleQueryResultWriter; +import org.eclipse.rdf4j.rio.RioSetting; +import org.eclipse.rdf4j.rio.WriterConfig; + +// Assume TupleQueryResultFormat.ODS exists or is defined elsewhere +// import static org.eclipse.rdf4j.query.resultio.TupleQueryResultFormat.ODS; + +/** + * Render a SPARQL result set into an ODF spreadsheet file (.ods) by manually generating the XML content and ZIP + * structure. + * + * NOTE: This implementation manually creates XML and does not use any ODF library. It is more complex and potentially + * less robust than using a dedicated library. Auto-sizing columns is not implemented as it's typically handled by the + * viewing application. + * + * @author Adapted from SPARQLResultsXLSXWriter by Jerven Bolleman + */ +public class SPARQLResultsODSWriter implements TupleQueryResultWriter { + + private final ZipOutputStream zos; + // Replace PrintWriter with XMLWriter + private XMLWriter contentXmlWriter; + + private final Map columnIndexes = new HashMap<>(); + private final Map prefixes = new HashMap<>(); + + private int columnCount = 0; + private boolean headerWritten = false; + + // ODF requires specific date/time format + private static final DateTimeFormatter ODF_DATETIME_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME; + private static final DateTimeFormatter ODF_DATE_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE; + + // Style names (must match definitions in styles.xml) + private static final String STYLE_DEFAULT = "DefaultStyle"; + private static final String STYLE_HEADER = "HeaderStyle"; + private static final String STYLE_IRI = "IriStyle"; + private static final String STYLE_ANY_IRI = "AnyIriStyle"; // Differentiate if needed + private static final String STYLE_NUMERIC = "NumericStyle"; + private static final String STYLE_DATE = "DateStyle"; + private static final String STYLE_DATETIME = "DateTimeStyle"; + private static final String STYLE_BOOLEAN = "BooleanStyle"; + + // ODS Namespaces + private static final String OFFICE_NS = "urn:oasis:names:tc:opendocument:xmlns:office:1.0"; + private static final String TABLE_NS = "urn:oasis:names:tc:opendocument:xmlns:table:1.0"; + private static final String TEXT_NS = "urn:oasis:names:tc:opendocument:xmlns:text:1.0"; + private static final String STYLE_NS = "urn:oasis:names:tc:opendocument:xmlns:style:1.0"; + private static final String FO_NS = "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"; + private static final String XLINK_NS = "http://www.w3.org/1999/xlink"; + private static final String DC_NS = "http://purl.org/dc/elements/1.1/"; + private static final String META_NS = "urn:oasis:names:tc:opendocument:xmlns:meta:1.0"; + private static final String NUMBER_NS = "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"; + private static final String SVG_NS = "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"; + private static final String MANIFEST_NS = "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"; + + // ODS Prefixes + private static final String OFFICE_PRE = "office"; + private static final String TABLE_PRE = "table"; + private static final String TEXT_PRE = "text"; + private static final String STYLE_PRE = "style"; + private static final String FO_PRE = "fo"; + private static final String XLINK_PRE = "xlink"; + private static final String DC_PRE = "dc"; + private static final String META_PRE = "meta"; + private static final String NUMBER_PRE = "datastyle"; + private static final String SVG_PRE = "svg"; + private static final String MANIFEST_PRE = "manifest"; + + public SPARQLResultsODSWriter(OutputStream out) { + this.zos = new ZipOutputStream(out); + } + + // --- Core TupleQueryResultWriter Methods --- + + @Override + public void startDocument() throws QueryResultHandlerException { + try { + // 1. Write mimetype (must be first and uncompressed) + ZipEntry mimetypeEntry = new ZipEntry("mimetype"); + mimetypeEntry.setMethod(ZipEntry.STORED); + byte[] mimetype = "application/vnd.oasis.opendocument.spreadsheet".getBytes(StandardCharsets.US_ASCII); + mimetypeEntry.setSize(mimetype.length); // Length of the mimetype string + mimetypeEntry.setCompressedSize(mimetype.length); +// // CRC-32 for "application/vnd.oasis.opendocument.spreadsheet" is 0xadc46ac +// mimetypeEntry.setCrc(0xadc46acL); + mimetypeEntry.setCrc(0x8a396c85L); + zos.putNextEntry(mimetypeEntry); + zos.write(mimetype); + zos.closeEntry(); + zos.setMethod(ZipEntry.DEFLATED); // Use compression for subsequent entries + + // 2. Prepare XMLWriters for main XML files + + // styles.xml + zos.putNextEntry(new ZipEntry("styles.xml")); + // Instantiate XMLWriter + XMLWriter stylesXmlWriter = new XMLWriter(new OutputStreamWriter(zos, StandardCharsets.UTF_8)); + stylesXmlWriter.setPrettyPrint(true); // Enable indentation for readability + writeStylesXml(stylesXmlWriter); // Write boilerplate and style definitions using XMLWriter + stylesXmlWriter.endDocument(); + // stylesXmlWriter.close(); // Don't close underlying stream + zos.closeEntry(); // Close the styles.xml entry + + // --- Write meta.xml --- + zos.putNextEntry(new ZipEntry("meta.xml")); + // Use try-with-resources for the intermediate XMLWriter + XMLWriter metaXmlWriter = new XMLWriter(new OutputStreamWriter(zos, StandardCharsets.UTF_8)); + metaXmlWriter.setPrettyPrint(true); + writeMetaXml(metaXmlWriter); // Use XMLWriter methods + metaXmlWriter.endDocument(); + // Don't close the underlying stream + zos.closeEntry(); // close meta + + // --- Write META-INF/manifest.xml --- + zos.putNextEntry(new ZipEntry("META-INF/manifest.xml")); + + XMLWriter manifestXmlWriter = new XMLWriter(new OutputStreamWriter(zos, StandardCharsets.UTF_8)); + manifestXmlWriter.setPrettyPrint(true); + writeManifestXml(manifestXmlWriter); // Use XMLWriter methods + manifestXmlWriter.endDocument(); + // Don't close the underlying stream + zos.closeEntry(); + + // content.xml + zos.putNextEntry(new ZipEntry("content.xml")); + // Instantiate XMLWriter + contentXmlWriter = new XMLWriter(new OutputStreamWriter(zos, StandardCharsets.UTF_8)); + contentXmlWriter.setPrettyPrint(true); // Enable indentation + writeContentXmlStart(); // Write boilerplate up to using XMLWriter + + } catch (IOException e) { + throw new QueryResultHandlerException("Failed to initialize ODF document structure", e); + } + } + + @Override + public void handleNamespace(String prefix, String uri) throws QueryResultHandlerException { + prefixes.put(uri, prefix); + } + + @Override + public void startQueryResult(List bindingNames) throws TupleQueryResultHandlerException { + this.columnCount = bindingNames.size(); + int columnIndex = 0; + columnIndexes.clear(); // Reset for potential multiple results + for (String bindingName : bindingNames) { + columnIndexes.put(bindingName, columnIndex++); + } + + if (contentXmlWriter == null) { + throw new TupleQueryResultHandlerException("startQueryResult called before startDocument"); + } + + // Write table structures only once + if (!headerWritten) { + try { + // Write Table Definitions in content.xml for "nice" sheet + // Note: Skipping the separate "raw" sheet for simplicity in this refactor + writeTableStart(contentXmlWriter, "QueryResult", columnCount); // Use a descriptive name + writeHeaderRow(contentXmlWriter, bindingNames, STYLE_HEADER); + // Keep the table open for data rows + + headerWritten = true; + } catch (IOException e) { + throw new TupleQueryResultHandlerException("Failed to write table start/header", e); + } + } else { + throw new TupleQueryResultHandlerException( + "startQueryResult called more than once. ODF writer handles only one result set."); + } + } + + @Override + public void handleSolution(BindingSet bindingSet) throws TupleQueryResultHandlerException { + if (!headerWritten || contentXmlWriter == null) { + throw new TupleQueryResultHandlerException( + "handleSolution called before startQueryResult or after endDocument"); + } + + try { + startTag(contentXmlWriter, TABLE_PRE, "table-row"); + // contentXmlWriter.attribute(TABLE_NS, "style-name", "ro1"); // Optional: + // Assuming default row style + + Value[] values = new Value[columnCount]; // To hold values in correct column order + for (Binding binding : bindingSet) { + int colIdx = columnIndexes.getOrDefault(binding.getName(), -1); + if (colIdx != -1) { + values[colIdx] = binding.getValue(); + } + } + + // Iterate through columns to ensure correct order and handle unbound variables + for (int i = 0; i < columnCount; i++) { + Value v = values[i]; + if (v == null) { + // Write empty cell for unbound variable + emptyTag(contentXmlWriter, TABLE_PRE, "table-cell"); + } else { + // Write formatted cell based on value type + writeCell(contentXmlWriter, v); + } + } + endTag(contentXmlWriter, TABLE_PRE, "table-row"); + } catch (IOException e) { + throw new TupleQueryResultHandlerException("Failed to write data row", e); + } + } + + private void emptyTag(XMLWriter writer, String prefix, String element) throws IOException { + writer.startTag(prefix + ':' + element); + writer.endTag(prefix + ':' + element); + } + + @Override + public void endQueryResult() throws TupleQueryResultHandlerException { + if (contentXmlWriter == null) + return; // Nothing was started + + // Close the table if it was opened + if (headerWritten) { + try { + writeTableEnd(contentXmlWriter); // Close the last opened table + } catch (IOException e) { + throw new TupleQueryResultHandlerException("Failed to write table end", e); + } + } + endDocument(); + } + + private void endDocument() throws QueryResultHandlerException { + try { + // --- Finish content.xml --- + if (contentXmlWriter != null) { + writeContentXmlEnd(contentXmlWriter); // Write closing tags + contentXmlWriter.endDocument(); // Finalize XML document + // XMLWriter wraps an OutputStreamWriter which shouldn't be closed directly + // here, + // as closing the ZipEntry handles the underlying stream flushing. + // contentXmlWriter.close(); // Don't close underlying stream + zos.closeEntry(); // Close the content.xml entry + contentXmlWriter = null; // Mark as finished + } + + // --- Finish the ZIP archive --- + zos.finish(); + zos.close(); // Close the main ZipOutputStream + + } catch (IOException e) { + throw new QueryResultHandlerException("Failed to finalize or write ODF document components", e); + } + } + + // --- Helper Methods for ODS XML Generation using XMLWriter --- + + private void declareNamespaces(XMLWriter writer, String... prefixesAndUris) throws IOException { + for (int i = 0; i < prefixesAndUris.length; i += 2) { + writer.setAttribute("xmlns:" + prefixesAndUris[i], prefixesAndUris[i + 1]); + } + } + + private void writeContentXmlStart() throws IOException { + contentXmlWriter.startDocument(); + declareNamespaces(contentXmlWriter, OFFICE_PRE, OFFICE_NS, TABLE_PRE, TABLE_NS, TEXT_PRE, TEXT_NS, FO_PRE, + FO_NS, XLINK_PRE, XLINK_NS, DC_PRE, DC_NS, META_PRE, META_NS, NUMBER_PRE, NUMBER_NS, STYLE_PRE, + STYLE_NS, SVG_PRE, SVG_NS); + startTag(contentXmlWriter, OFFICE_PRE, "document-content"); + setAttribute(contentXmlWriter, OFFICE_PRE, "version", "1.2"); + + emptyTag(contentXmlWriter, OFFICE_PRE, "scripts"); // Required + + startTag(contentXmlWriter, OFFICE_PRE, "font-face-decls"); + { // font + setAttribute(contentXmlWriter, STYLE_PRE, "name", "Liberation Sans"); + setAttribute(contentXmlWriter, SVG_PRE, "font-family", "Liberation Sans"); + setAttribute(contentXmlWriter, STYLE_PRE, "font-family-generic", "swiss"); + setAttribute(contentXmlWriter, STYLE_PRE, "font-pitch", "variable"); + emptyTag(contentXmlWriter, STYLE_PRE, "font-face"); + } + endTag(contentXmlWriter, OFFICE_PRE, "font-face-decls"); // office:font-face-decls + + startTag(contentXmlWriter, OFFICE_PRE, "automatic-styles"); + { + // Define basic column style (co1) and row style (ro1) + setAttribute(contentXmlWriter, STYLE_PRE, "name", "co1"); + setAttribute(contentXmlWriter, STYLE_PRE, "family", "table-column"); + startTag(contentXmlWriter, STYLE_PRE, "style"); + { + + setAttribute(contentXmlWriter, FO_PRE, "break-before", "auto"); + setAttribute(contentXmlWriter, STYLE_PRE, "column-width", "2.257cm"); // Default width + emptyTag(contentXmlWriter, STYLE_PRE, "table-column-properties"); + } + endTag(contentXmlWriter, STYLE_PRE, "style"); // style:style co1 + + setAttribute(contentXmlWriter, STYLE_PRE, "name", "ro1"); + setAttribute(contentXmlWriter, STYLE_PRE, "family", "table-row"); + startTag(contentXmlWriter, STYLE_PRE, "style"); + { + setAttribute(contentXmlWriter, STYLE_PRE, "row-height", "0.453cm"); + setAttribute(contentXmlWriter, FO_PRE, "break-before", "auto"); + setAttribute(contentXmlWriter, STYLE_PRE, "use-optimal-row-height", "true"); + emptyTag(contentXmlWriter, STYLE_PRE, "table-row-properties"); + } + endTag(contentXmlWriter, STYLE_PRE, "style"); // style:style ro1 + // Add automatic data styles if needed (e.g., N1, N2 for specific number + // formats) + } + endTag(contentXmlWriter, OFFICE_PRE, "automatic-styles"); + + startTag(contentXmlWriter, OFFICE_PRE, "body"); + startTag(contentXmlWriter, OFFICE_PRE, "spreadsheet"); + // Tables will be added here by startQueryResult/handleSolution + } + + private void emptyElement(XMLWriter contentXmlWriter, String prefix, String element, String content) + throws IOException { + contentXmlWriter.startTag(prefix + ':' + element); + contentXmlWriter.text(content); + contentXmlWriter.endTag(prefix + ':' + element); + } + + private void startTag(XMLWriter writer, String prefix, String element) throws IOException { + writer.startTag(prefix + ':' + element); + } + + private void writeContentXmlEnd(XMLWriter writer) throws IOException { + endTag(writer, OFFICE_PRE, "spreadsheet"); // office:spreadsheet + endTag(writer, OFFICE_PRE, "body"); // office:body + endTag(writer, OFFICE_PRE, "document-content"); // office: + } + + private void endTag(XMLWriter writer, String officePre, String element) throws IOException { + writer.endTag(officePre + ":" + element); + } + + private void writeStylesXml(XMLWriter stylesXmlWriter) throws IOException { + stylesXmlWriter.startDocument(); + declareNamespaces(stylesXmlWriter, "office", OFFICE_NS, "style", STYLE_NS, "text", TEXT_NS, "table", TABLE_NS, + // "draw", "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0", + "fo", FO_NS, "xlink", XLINK_NS, "dc", DC_NS, "meta", META_NS, "number", NUMBER_NS, "svg", SVG_NS); + setAttribute(stylesXmlWriter, OFFICE_PRE, "version", "1.2"); + startTag(stylesXmlWriter, OFFICE_PRE, "document-styles"); + { + { + startTag(stylesXmlWriter, OFFICE_PRE, "font-face-decls"); + setAttribute(stylesXmlWriter, STYLE_PRE, "name", "Liberation Sans"); + setAttribute(stylesXmlWriter, SVG_PRE, "font-family", "'Liberation Sans'"); + setAttribute(stylesXmlWriter, STYLE_PRE, "font-family-generic", "swiss"); + setAttribute(stylesXmlWriter, STYLE_PRE, "font-pitch", "variable"); + + emptyTag(stylesXmlWriter, STYLE_PRE, "font-face"); + + setAttribute(stylesXmlWriter, STYLE_PRE, "name", "Liberation Mono"); + setAttribute(stylesXmlWriter, SVG_PRE, "font-family", "'Liberation Mono'"); + setAttribute(stylesXmlWriter, STYLE_PRE, "font-family-generic", "modern"); + setAttribute(stylesXmlWriter, STYLE_PRE, "font-pitch", "fixed"); + + endTag(stylesXmlWriter, OFFICE_PRE, "font-face-decls"); // office:font-face-decls + } + startTag(stylesXmlWriter, OFFICE_PRE, "styles"); + { + // --- Default Cell Style --- + + setAttribute(stylesXmlWriter, STYLE_PRE, "name", STYLE_DEFAULT); + setAttribute(stylesXmlWriter, STYLE_PRE, "family", "table-cell"); + setAttribute(stylesXmlWriter, STYLE_PRE, "parent-style-name", "Default"); // Assumes "Default" is + // built-in + // or defined + startTag(stylesXmlWriter, STYLE_PRE, "style"); + { + setAttribute(stylesXmlWriter, FO_PRE, "padding", "0.097cm"); + setAttribute(stylesXmlWriter, FO_PRE, "border", "0.002cm solid #000000"); // Basic border + emptyTag(stylesXmlWriter, STYLE_PRE, "table-cell-properties"); + + setAttribute(stylesXmlWriter, STYLE_PRE, "font-name", "Liberation Sans"); + setAttribute(stylesXmlWriter, FO_PRE, "font-size", "10pt"); + emptyTag(stylesXmlWriter, STYLE_PRE, "text-properties"); + } + endTag(stylesXmlWriter, STYLE_PRE, "style"); + + // --- Header Style --- + setAttribute(stylesXmlWriter, STYLE_PRE, "name", STYLE_HEADER); + setAttribute(stylesXmlWriter, STYLE_PRE, "family", "table-cell"); + setAttribute(stylesXmlWriter, STYLE_PRE, "parent-style-name", STYLE_DEFAULT); + startTag(stylesXmlWriter, STYLE_PRE, "style"); + { + setAttribute(stylesXmlWriter, FO_PRE, "background-color", "#cccccc"); + setAttribute(stylesXmlWriter, FO_PRE, "text-align", "center"); + setAttribute(stylesXmlWriter, STYLE_PRE, "vertical-align", "middle"); + setAttribute(stylesXmlWriter, FO_PRE, "border", "0.002cm solid #000000"); + + emptyTag(stylesXmlWriter, STYLE_PRE, "table-cell-properties"); + + setAttribute(stylesXmlWriter, FO_PRE, "font-weight", "bold"); + setAttribute(stylesXmlWriter, STYLE_PRE, "font-name", "Liberation Sans"); + setAttribute(stylesXmlWriter, FO_PRE, "font-size", "10pt"); + + emptyTag(stylesXmlWriter, STYLE_PRE, "text-properties"); + } + endTag(stylesXmlWriter, STYLE_PRE, "style"); // style:style HeaderStyle + + // --- IRI Hyperlink Style --- + setAttribute(stylesXmlWriter, STYLE_PRE, "name", STYLE_IRI); + setAttribute(stylesXmlWriter, STYLE_PRE, "family", "table-cell"); + setAttribute(stylesXmlWriter, STYLE_PRE, "parent-style-name", STYLE_DEFAULT); + startTag(stylesXmlWriter, STYLE_PRE, "style"); + { + setAttribute(stylesXmlWriter, FO_PRE, "color", "#0000ff"); + setAttribute(stylesXmlWriter, STYLE_PRE, "text-underline-style", "solid"); + setAttribute(stylesXmlWriter, STYLE_PRE, "text-underline-width", "auto"); + setAttribute(stylesXmlWriter, STYLE_PRE, "text-underline-color", "font-color"); // Blue, underlined + emptyTag(stylesXmlWriter, STYLE_PRE, "text-properties"); + } + endTag(stylesXmlWriter, STYLE_PRE, "style"); // style:style IriStyle + + // --- Any IRI Hyperlink Style --- + setAttribute(stylesXmlWriter, STYLE_PRE, "name", STYLE_ANY_IRI); + setAttribute(stylesXmlWriter, STYLE_PRE, "family", "table-cell"); + setAttribute(stylesXmlWriter, STYLE_PRE, "parent-style-name", STYLE_DEFAULT); + startTag(stylesXmlWriter, STYLE_PRE, "style"); + { + setAttribute(stylesXmlWriter, FO_PRE, "color", "#ff00ff"); // Magenta + setAttribute(stylesXmlWriter, STYLE_PRE, "text-underline-style", "solid"); + setAttribute(stylesXmlWriter, STYLE_PRE, "text-underline-width", "auto"); + setAttribute(stylesXmlWriter, STYLE_PRE, "text-underline-color", "font-color"); + emptyTag(stylesXmlWriter, STYLE_PRE, "text-properties"); + } + endTag(stylesXmlWriter, STYLE_PRE, "style"); + + // --- Define Number/Date/Bool Data Styles First (referenced by cell styles) --- + setAttribute(stylesXmlWriter, STYLE_PRE, "name", "N0"); + startTag(stylesXmlWriter, NUMBER_PRE, "number-style"); + { + setAttribute(stylesXmlWriter, NUMBER_PRE, "min-integer-digits", "1"); + setAttribute(stylesXmlWriter, NUMBER_PRE, "decimal-places", "2");// 2 decimal places + setAttribute(stylesXmlWriter, NUMBER_PRE, "grouping", "false"); + emptyTag(stylesXmlWriter, NUMBER_PRE, "number"); + } + endTag(stylesXmlWriter, NUMBER_PRE, "number-style"); + + setAttribute(stylesXmlWriter, STYLE_PRE, "name", "Ndate"); + setAttribute(stylesXmlWriter, NUMBER_PRE, "automatic-order", "true"); + startTag(stylesXmlWriter, NUMBER_PRE, "date-style"); + { + setAttribute(stylesXmlWriter, NUMBER_PRE, "style", "long"); + emptyTag(stylesXmlWriter, NUMBER_PRE, "year"); + + emptyElement(stylesXmlWriter, NUMBER_PRE, "text", "-"); + + setAttribute(stylesXmlWriter, NUMBER_PRE, "style", "long"); + emptyTag(stylesXmlWriter, NUMBER_PRE, "month"); + + emptyElement(stylesXmlWriter, NUMBER_PRE, "text", "-"); + + setAttribute(stylesXmlWriter, NUMBER_PRE, "style", "long"); + emptyTag(stylesXmlWriter, NUMBER_PRE, "day"); + } + endTag(stylesXmlWriter, NUMBER_PRE, "date-style"); + + setAttribute(stylesXmlWriter, STYLE_PRE, "name", "Ndatetime"); + setAttribute(stylesXmlWriter, NUMBER_PRE, "automatic-order", "true"); + startTag(stylesXmlWriter, NUMBER_PRE, "date-style"); + { + setAttribute(stylesXmlWriter, NUMBER_PRE, "style", "long"); + emptyTag(stylesXmlWriter, NUMBER_PRE, "year"); + emptyElement(stylesXmlWriter, NUMBER_PRE, "text", "-"); + + setAttribute(stylesXmlWriter, NUMBER_PRE, "style", "long"); + emptyTag(stylesXmlWriter, NUMBER_PRE, "month"); + + emptyElement(stylesXmlWriter, NUMBER_PRE, "text", "-"); + setAttribute(stylesXmlWriter, NUMBER_PRE, "style", "long"); + emptyTag(stylesXmlWriter, NUMBER_PRE, "day"); + emptyElement(stylesXmlWriter, NUMBER_PRE, "text", " "); // Separator + + setAttribute(stylesXmlWriter, NUMBER_PRE, "style", "long"); + emptyTag(stylesXmlWriter, NUMBER_PRE, "hours"); + + emptyElement(stylesXmlWriter, NUMBER_PRE, "text", "-"); + + setAttribute(stylesXmlWriter, NUMBER_PRE, "style", "long"); + emptyTag(stylesXmlWriter, NUMBER_PRE, "minutes"); + + setAttribute(stylesXmlWriter, NUMBER_PRE, "style", "long"); + emptyElement(stylesXmlWriter, NUMBER_PRE, "text", "-"); + + setAttribute(stylesXmlWriter, NUMBER_PRE, "style", "long"); + emptyTag(stylesXmlWriter, NUMBER_PRE, "seconds"); + } + endTag(stylesXmlWriter, NUMBER_PRE, "date-style"); + + setAttribute(stylesXmlWriter, STYLE_PRE, "name", "Nbool"); + emptyTag(stylesXmlWriter, NUMBER_PRE, "boolean-style"); // Displays TRUE/FALSE + + // --- Cell styles referencing data styles --- + setAttribute(stylesXmlWriter, STYLE_PRE, "name", STYLE_NUMERIC); + setAttribute(stylesXmlWriter, STYLE_PRE, "family", "table-cell"); + setAttribute(stylesXmlWriter, STYLE_PRE, "parent-style-name", STYLE_DEFAULT); + setAttribute(stylesXmlWriter, STYLE_PRE, "data-style-name", "N0"); + // Reference N0 number format + emptyTag(stylesXmlWriter, STYLE_PRE, "style"); + + // Reference Ndate + // date + // format + setAttribute(stylesXmlWriter, STYLE_PRE, "name", STYLE_DATE); + setAttribute(stylesXmlWriter, STYLE_PRE, "family", "table-cell"); + setAttribute(stylesXmlWriter, STYLE_PRE, "parent-style-name", STYLE_DEFAULT); + setAttribute(stylesXmlWriter, STYLE_PRE, "data-style-name", "Ndate"); + emptyTag(stylesXmlWriter, STYLE_PRE, "style"); + + // Reference + // Ndatetime + // date + // format + setAttribute(stylesXmlWriter, STYLE_PRE, "name", STYLE_DATETIME); + setAttribute(stylesXmlWriter, STYLE_PRE, "family", "table-cell"); + setAttribute(stylesXmlWriter, STYLE_PRE, "parent-style-name", STYLE_DEFAULT); + setAttribute(stylesXmlWriter, STYLE_PRE, "data-style-name", "Ndatetime"); + emptyTag(stylesXmlWriter, STYLE_PRE, "style"); + + // Reference + // Nbool + // boolean + // format + setAttribute(stylesXmlWriter, STYLE_PRE, "name", STYLE_BOOLEAN); + setAttribute(stylesXmlWriter, STYLE_PRE, "family", "table-cell"); + setAttribute(stylesXmlWriter, STYLE_PRE, "parent-style-name", STYLE_DEFAULT); + setAttribute(stylesXmlWriter, STYLE_PRE, "data-style-name", "Nbool"); + emptyTag(stylesXmlWriter, STYLE_PRE, "style"); + } + endTag(stylesXmlWriter, OFFICE_PRE, "styles"); // office:styles + + startTag(stylesXmlWriter, OFFICE_PRE, "automatic-styles"); + // Could define column/row styles here too if needed (ta1 example) + { + setAttribute(stylesXmlWriter, STYLE_PRE, "name", "ta1"); + setAttribute(stylesXmlWriter, STYLE_PRE, "family", "table"); + setAttribute(stylesXmlWriter, STYLE_PRE, "master-page-name", "Default"); // Link to master page + startTag(stylesXmlWriter, STYLE_PRE, "style"); + { + setAttribute(stylesXmlWriter, TABLE_PRE, "display", "true"); + setAttribute(stylesXmlWriter, STYLE_PRE, "writing-mode", "lr-tb"); + emptyTag(stylesXmlWriter, STYLE_PRE, "table-properties"); + } + endTag(stylesXmlWriter, STYLE_PRE, "style"); + } + endTag(stylesXmlWriter, OFFICE_PRE, "automatic-styles"); + + startTag(stylesXmlWriter, OFFICE_PRE, "master-styles"); // Required structure + { + setAttribute(stylesXmlWriter, STYLE_PRE, "name", "Default"); + setAttribute(stylesXmlWriter, STYLE_PRE, "page-layout-name", "pm1"); // Needs corresponding page-layout + emptyTag(stylesXmlWriter, STYLE_PRE, "master-page"); + + // Define the page layout referenced above + setAttribute(stylesXmlWriter, STYLE_PRE, "name", "pm1"); + startTag(stylesXmlWriter, STYLE_PRE, "page-layout"); + + setAttribute(stylesXmlWriter, FO_PRE, "margin", "0.7874in"); // Example margins + setAttribute(stylesXmlWriter, FO_PRE, "page-width", "8.5in"); + setAttribute(stylesXmlWriter, FO_PRE, "page-height", "11in"); + setAttribute(stylesXmlWriter, STYLE_PRE, "print-orientation", "portrait"); + emptyTag(stylesXmlWriter, STYLE_PRE, "page-layout-properties"); + // Header/Footer styles would go inside page-layout if used + endTag(stylesXmlWriter, STYLE_PRE, "page-layout"); + } + endTag(stylesXmlWriter, OFFICE_PRE, "master-styles"); // Required structure + } + endTag(stylesXmlWriter, OFFICE_PRE, "document-styles"); // office:document-styles + } + + private void writeMetaXml(XMLWriter writer) throws IOException { + setAttribute(writer, OFFICE_PRE, "version", "1.2"); + writer.startDocument(); + declareNamespaces(writer, "office", OFFICE_NS, "meta", META_NS, "dc", DC_NS, "xlink", XLINK_NS); + startTag(writer, OFFICE_PRE, "document-meta"); + { + startTag(writer, OFFICE_PRE, "meta"); + { + startTag(writer, META_PRE, "generator"); + writer.text("Eclipse RDF4J SPARQLResultsODSWriter (ODSWriter)"); + endTag(writer, META_PRE, "generator"); + } + { + startTag(writer, META_PRE, "creation-date"); + writer.text(ZonedDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); + endTag(writer, META_PRE, "creation-date"); + } + endTag(writer, OFFICE_PRE, "meta"); + } + endTag(writer, OFFICE_PRE, "document-meta"); + } + + private void writeManifestXml(XMLWriter writer) throws IOException { + writer.startDocument(); + writer.setAttribute("xmlns:manifest", MANIFEST_NS); + setAttribute(writer, MANIFEST_PRE, "version", "1.2"); + startTag(writer, MANIFEST_PRE, "manifest"); + { + setAttribute(writer, MANIFEST_PRE, "full-path", "/"); + // Version of the ODF standard for this entry + setAttribute(writer, MANIFEST_PRE, "version", "1.2"); + setAttribute(writer, MANIFEST_PRE, "media-type", "application/vnd.oasis.opendocument.spreadsheet"); + emptyTag(writer, MANIFEST_PRE, "file-entry"); + + addFile(writer, "content.xml"); + addFile(writer, "styles.xml"); + addFile(writer, "meta.xml"); + } + endTag(writer, MANIFEST_PRE, "manifest"); // manifest:manifest + } + + private void addFile(XMLWriter writer, String f) throws IOException { + setAttribute(writer, MANIFEST_PRE, "full-path", f); + setAttribute(writer, MANIFEST_PRE, "media-type", "text/xml"); + emptyTag(writer, MANIFEST_PRE, "file-entry"); + } + + private void writeTableStart(XMLWriter writer, String name, int columnCount) throws IOException { + setAttribute(writer, TABLE_PRE, "name", name); // Use name provided + setAttribute(writer, TABLE_PRE, "style-name", "ta1"); // Use defined table style + startTag(writer, TABLE_PRE, "table"); + + // Define columns - using the automatic style 'co1' defined earlier + for (int i = 0; i < columnCount; i++) { + setAttribute(writer, TABLE_PRE, "style-name", "co1"); + emptyElement(writer, TABLE_PRE, "table-column", name); + } + } + + private void writeTableEnd(XMLWriter writer) throws IOException { + endTag(writer, TABLE_PRE, "table"); // table:table + } + + private void writeHeaderRow(XMLWriter writer, List bindingNames, String headerStyleName) + throws IOException { + startTag(writer, TABLE_PRE, "table-row"); + { + // writer.attribute(TABLE_NS, "style-name", "ro1"); // Optional: Assume default + // row style + + for (String name : bindingNames) { + setAttribute(writer, OFFICE_PRE, "value-type", "string"); + setAttribute(writer, TABLE_PRE, "style-name", headerStyleName); // Apply header style + startTag(writer, TABLE_PRE, "table-cell"); + { + startTag(writer, TEXT_PRE, "p"); + writer.text(name); // XMLWriter handles escaping + endTag(writer, TEXT_PRE, "p"); + } + endTag(writer, TABLE_PRE, "table-cell"); + } + } + endTag(writer, TABLE_PRE, "table-row"); // table:table-row + } + + private void setAttribute(XMLWriter writer, String prefix, String element, String value) { + writer.setAttribute(prefix + ":" + element, value); + + } + + // Main cell writing logic using XMLWriter + private void writeCell(XMLWriter writer, Value value) throws IOException { + if (value.isLiteral()) { + handleLiteralCell(writer, (Literal) value); + } else if (value.isIRI()) { + handleIriCell(writer, (IRI) value, STYLE_IRI); // Default IRI style + } else if (value.isBNode()) { + writeStringCell(writer, value.stringValue(), STYLE_DEFAULT); + } else if (value.isTriple()) { + writeStringCell(writer, value.stringValue(), STYLE_DEFAULT); // Or a dedicated style? + } else { + writeStringCell(writer, value.stringValue(), STYLE_DEFAULT); + } + } + + // --- Cell Type Handling using XMLWriter --- + + private void handleLiteralCell(XMLWriter writer, Literal l) throws IOException { + CoreDatatype cd = l.getCoreDatatype(); + Optional lang = l.getLanguage(); + + if (cd != null && cd.isXSDDatatype()) { + handleXsdLiteral(writer, l, cd.asXSDDatatypeOrNull()); + } else if (lang.isPresent()) { + writeStringCell(writer, l.getLabel(), STYLE_DEFAULT); // Add lang info in comment? maybe later + } else if (cd != null && (cd.isRDFDatatype() || cd.isGEODatatype())) { + writeStringCell(writer, l.getLabel(), STYLE_DEFAULT); + } else { + writeStringCell(writer, l.getLabel(), STYLE_DEFAULT); + } + } + + private void handleXsdLiteral(XMLWriter writer, Literal l, XSD xsdType) throws IOException { + if (xsdType == null) { + writeStringCell(writer, l.getLabel(), STYLE_DEFAULT); + return; + } + + try { + switch (xsdType) { + case BOOLEAN: + writeBooleanCell(writer, l.booleanValue(), STYLE_BOOLEAN); + break; + + case DECIMAL: + case INTEGER: + case NEGATIVE_INTEGER: + case NON_NEGATIVE_INTEGER: + case POSITIVE_INTEGER: + case NON_POSITIVE_INTEGER: + case LONG: + case INT: + case SHORT: + case BYTE: + case UNSIGNED_LONG: + case UNSIGNED_INT: + case UNSIGNED_SHORT: + case UNSIGNED_BYTE: + try { + BigDecimal decVal = l.decimalValue(); + // Pass original label for display, double value for storage + writeNumericCell(writer, decVal.doubleValue(), l.getLabel(), STYLE_NUMERIC); + } catch (NumberFormatException nfe) { + writeStringCell(writer, l.getLabel(), STYLE_NUMERIC); // Fallback + } + break; + + case DOUBLE: + writeNumericCell(writer, l.doubleValue(), l.getLabel(), STYLE_NUMERIC); + break; + case FLOAT: + writeNumericCell(writer, l.floatValue(), l.getLabel(), STYLE_NUMERIC); + break; + + case DATETIME: + case DATETIMESTAMP: + // Use LocalDateTime (no timezone info in ODF value attribute) + writeDateTimeCell(writer, l.calendarValue().toGregorianCalendar().toZonedDateTime().toLocalDateTime(), + STYLE_DATETIME); + break; + case DATE: + writeDateCell(writer, l.calendarValue().toGregorianCalendar().toZonedDateTime().toLocalDate(), + STYLE_DATE); + break; + + case TIME: + case GYEAR: + case GMONTH: + case GDAY: + case GYEARMONTH: + case GMONTHDAY: + case DURATION: + case YEARMONTHDURATION: + case DAYTIMEDURATION: + writeStringCell(writer, l.getLabel(), STYLE_DEFAULT); + break; + + case ANYURI: + try { + handleIriCell(writer, SimpleValueFactory.getInstance().createIRI(l.getLabel()), STYLE_ANY_IRI); + } catch (IllegalArgumentException e) { + writeStringCell(writer, l.getLabel(), STYLE_DEFAULT); + } + break; + + default: + writeStringCell(writer, l.getLabel(), STYLE_DEFAULT); + break; + } + } catch (Exception e) { + System.err.println("Warn: Error converting literal '" + l.stringValue() + "' type " + xsdType + + ". Writing as string. Error: " + e.getMessage()); + writeStringCell(writer, l.getLabel(), STYLE_DEFAULT); // Fallback + } + } + + private void handleIriCell(XMLWriter writer, IRI iri, String styleName) throws IOException { + String displayString = formatIri(iri); + String url = iri.stringValue(); + + setAttribute(writer, OFFICE_PRE, "value-type", "string"); // Hyperlinks are fundamentally text cells + setAttribute(writer, TABLE_PRE, "style-name", styleName); + startTag(writer, TABLE_PRE, "table-cell"); + { + startTag(writer, TEXT_PRE, "p"); + { + // Add hyperlink using text:a + setAttribute(writer, XLINK_PRE, "type", "simple"); + setAttribute(writer, XLINK_PRE, "href", url); // XMLWriter handles attribute escaping + startTag(writer, TEXT_PRE, "a"); + writer.text(displayString); // XMLWriter handles text escaping + endTag(writer, TEXT_PRE, "a"); + } + endTag(writer, TEXT_PRE, "p"); + } + endTag(writer, TABLE_PRE, "table-cell"); + } + + private void writeStringCell(XMLWriter writer, String value, String styleName) throws IOException { + setAttribute(writer, OFFICE_PRE, "value-type", "string"); + setAttribute(writer, TABLE_PRE, "style-name", styleName); + startTag(writer, TABLE_PRE, "table-cell"); + + startTag(writer, TEXT_NS, "p"); + writer.text(value); // XMLWriter handles escaping + endTag(writer, TEXT_PRE, "p"); + + endTag(writer, TABLE_PRE, "table-cell"); + } + + private void writeNumericCell(XMLWriter writer, double value, String displayValue, String styleName) + throws IOException { + setAttribute(writer, OFFICE_PRE, "value-type", "float"); // Use float for numbers + setAttribute(writer, OFFICE_PRE, "value", String.valueOf(value)); // ODF requires string representation of + // number + setAttribute(writer, TABLE_PRE, "style-name", styleName); // Style defines display format + startTag(writer, TABLE_PRE, "table-cell"); + { + startTag(writer, TEXT_PRE, "p"); + writer.text(displayValue); // Text content shows original or formatted string + endTag(writer, TEXT_PRE, "p"); + } + endTag(writer, TABLE_PRE, "table-cell"); + } + + private void writeBooleanCell(XMLWriter writer, boolean value, String styleName) throws IOException { + setAttribute(writer, OFFICE_PRE, "value-type", "boolean"); + setAttribute(writer, OFFICE_PRE, "boolean-value", String.valueOf(value)); // "true" or "false" + setAttribute(writer, TABLE_PRE, "style-name", styleName); + startTag(writer, TABLE_PRE, "table-cell"); + { + + startTag(writer, TEXT_PRE, "p"); + writer.text(value ? "TRUE" : "FALSE"); // Text content for display + endTag(writer, TEXT_PRE, "p"); + } + endTag(writer, TABLE_PRE, "table-cell"); + } + + private void writeDateTimeCell(XMLWriter writer, java.time.LocalDateTime dateTime, String styleName) + throws IOException { + // ODF requires ISO 8601 format YYYY-MM-DDTHH:MM:SS + String isoValue = dateTime.format(ODF_DATETIME_FORMATTER); + + setAttribute(writer, OFFICE_PRE, "value-type", "date"); // Type is "date" for both date and datetime + setAttribute(writer, OFFICE_PRE, "date-value", isoValue); // Stores full date+time + setAttribute(writer, TABLE_PRE, "style-name", styleName); // Style controls display + startTag(writer, TABLE_PRE, "table-cell"); + + startTag(writer, TEXT_PRE, "p"); + // Displayed text can be formatted differently by the style, + // but putting the ISO value here ensures something is shown if style fails. + writer.text(isoValue); + endTag(writer, TEXT_PRE, "p"); + + endTag(writer, TABLE_PRE, "table-cell"); + } + + private void writeDateCell(XMLWriter writer, java.time.LocalDate date, String styleName) throws IOException { + // ODF requires ISO 8601 format YYYY-MM-DD + String isoDateValue = date.format(ODF_DATE_FORMATTER); + // ODF stores dates internally as datetime at midnight + String isoDateTimeValue = date.atStartOfDay().format(ODF_DATETIME_FORMATTER); + + startTag(writer, TABLE_PRE, "table-cell"); + setAttribute(writer, OFFICE_PRE, "value-type", "date"); + setAttribute(writer, OFFICE_PRE, "date-value", isoDateTimeValue); // Store as full date+time + setAttribute(writer, TABLE_PRE, "style-name", styleName); // Style controls display (shows date part) + + startTag(writer, TEXT_PRE, "p"); + writer.text(isoDateValue); // Display the date part + endTag(writer, TEXT_PRE, "p"); + + endTag(writer, TABLE_PRE, "table-cell"); + } + + // --- Formatting Helpers --- + + private String formatIri(IRI iri) { + String iriStr = iri.stringValue(); + String namespace = iri.getNamespace(); + String localName = iri.getLocalName(); + + if (prefixes.containsKey(namespace)) { + String prefix = prefixes.get(namespace); + if (!localName.isEmpty() && iriStr.equals(namespace + localName)) { // Check for clean split + return prefix + ":" + localName; + } + } + // If no prefix or local name is weird, return full IRI (or just local name if + // sensible) + // Let's prefer the full IRI for clarity in the spreadsheet unless prefixed + // if (localName != null && !localName.isEmpty() && iriStr.endsWith(localName)) + // { + // return localName; // Could use this, but full IRI might be less ambiguous + // } + return iriStr; // Fallback to full IRI string + } + + // Remove escapeXml and escapeXmlAttribute methods as XMLWriter handles + // escaping. + // private String escapeXml(String s) { ... } + // private String escapeXmlAttribute(String s) { ... } + + // --- Unimplemented/Simplified Methods from Interface --- + + @Override + public void handleBoolean(boolean value) throws QueryResultHandlerException { + System.err.println("Warning: handleBoolean (SPARQL ASK result) not implemented for ODF writer."); + startDocument(); + MapBindingSet result = new MapBindingSet(); + startQueryResult(List.of("result")); + result.setBinding("result", SimpleValueFactory.getInstance().createLiteral(value)); + handleSolution(result); + endQueryResult(); + endDocument(); + } + + @Override + public void handleLinks(List linkUrls) throws QueryResultHandlerException { + // Could store these in meta.xml meta:user-defined fields if needed + System.err.println("Warning: handleLinks (document-level links) not implemented for ODF writer."); + } + + @Override + public QueryResultFormat getQueryResultFormat() { + return TupleQueryResultFormat.ODS; + } + + @Override + public TupleQueryResultFormat getTupleQueryResultFormat() { + return TupleQueryResultFormat.ODS; + } + + // Keep other interface methods (setWriterConfig, getWriterConfig, etc.) as they + // were + + @Override + public void setWriterConfig(WriterConfig config) { + // Configuration options could be added (e.g., date formats, default styles) + } + + @Override + public WriterConfig getWriterConfig() { + return new WriterConfig(); // Return default/empty config + } + + @Override + public Collection> getSupportedSettings() { + return Collections.emptyList(); // No specific settings supported yet + } + + @Override + public void handleStylesheet(String stylesheetUrl) throws QueryResultHandlerException { + // Not applicable/supported for direct ODF generation + } + + @Override + public void startHeader() throws QueryResultHandlerException { + // Handled within startQueryResult for ODF table structure + } + + @Override + public void endHeader() throws QueryResultHandlerException { + // Handled within startQueryResult/endQueryResult + } + +} diff --git a/core/queryresultio/ods/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlods/SPARQLResultsODSWriterFactory.java b/core/queryresultio/ods/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlods/SPARQLResultsODSWriterFactory.java new file mode 100644 index 00000000000..4f72a02e730 --- /dev/null +++ b/core/queryresultio/ods/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlods/SPARQLResultsODSWriterFactory.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.query.resultio.sparqlods; + +import java.io.OutputStream; + +import org.eclipse.rdf4j.query.resultio.TupleQueryResultFormat; +import org.eclipse.rdf4j.query.resultio.TupleQueryResultWriter; +import org.eclipse.rdf4j.query.resultio.TupleQueryResultWriterFactory; + +public class SPARQLResultsODSWriterFactory implements TupleQueryResultWriterFactory { + + public SPARQLResultsODSWriterFactory() { + super(); + } + + @Override + public TupleQueryResultFormat getTupleQueryResultFormat() { + return TupleQueryResultFormat.ODS; + } + + /** + * Returns a new instance of SPARQLResultsODSWriter. + */ + @Override + public TupleQueryResultWriter getWriter(OutputStream out) { + return new SPARQLResultsODSWriter(out); + } +} diff --git a/core/queryresultio/ods/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlods/package.html b/core/queryresultio/ods/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlods/package.html new file mode 100644 index 00000000000..8e223d4fac3 --- /dev/null +++ b/core/queryresultio/ods/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlods/package.html @@ -0,0 +1,6 @@ + + + + Writers for custom ODS for SPARQL results + + \ No newline at end of file diff --git a/core/queryresultio/ods/src/test/java/org/eclipse/rdf4j/query/resultio/sparqlxml/SPARQLODSTupleTest.java b/core/queryresultio/ods/src/test/java/org/eclipse/rdf4j/query/resultio/sparqlxml/SPARQLODSTupleTest.java new file mode 100644 index 00000000000..9bf748fdd8c --- /dev/null +++ b/core/queryresultio/ods/src/test/java/org/eclipse/rdf4j/query/resultio/sparqlxml/SPARQLODSTupleTest.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.query.resultio.sparqlxml; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.impl.MapBindingSet; +import org.eclipse.rdf4j.query.impl.TupleQueryResultBuilder; +import org.eclipse.rdf4j.query.resultio.TupleQueryResultWriter; +import org.eclipse.rdf4j.query.resultio.sparqlods.SPARQLResultsODSWriter; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +/** + * @author Jerven Bolleman + */ +public class SPARQLODSTupleTest { + + @Test + void simpleCase(@TempDir Path dir) throws IOException { + Files.createDirectories(dir); + + Path tf = dir.resolve("test.ods"); +// Path tf = java.nio.file.Paths.get(System.getProperty("user.home")+"/test.ods"); + TupleQueryResultBuilder b = new TupleQueryResultBuilder(); + b.startQueryResult(List.of("boolean", "iri", "int")); + MapBindingSet bs = new MapBindingSet(); + bs.setBinding("boolean", SimpleValueFactory.getInstance().createLiteral(true)); + bs.setBinding("iri", SimpleValueFactory.getInstance().createIRI("https://example.org/iri")); + bs.setBinding("int", SimpleValueFactory.getInstance().createLiteral(1)); + b.handleSolution(bs); + MapBindingSet bs2 = new MapBindingSet(); + bs2.setBinding("boolean", SimpleValueFactory.getInstance().createLiteral(false)); + bs2.setBinding("iri", SimpleValueFactory.getInstance().createIRI("https://example.org/iri/test")); + bs2.setBinding("int", SimpleValueFactory.getInstance().createLiteral(-9)); + b.handleSolution(bs2); + + MapBindingSet bs3 = new MapBindingSet(); + bs3.setBinding("iri", SimpleValueFactory.getInstance().createIRI("http://purl.uniprot.org/taxonomy/9606")); + bs3.setBinding("int", SimpleValueFactory.getInstance().createLiteral(-9)); + b.handleSolution(bs3); + List links = List.of("http://example.org/link1"); + b.handleLinks(links); + try (var out = Files.newOutputStream(tf)) { + TupleQueryResultWriter writer = new SPARQLResultsODSWriter(out); + writer.startDocument(); + writer.handleNamespace("taxon", "http://purl.uniprot.org/taxonomy/"); + writer.handleLinks(links); + writer.startQueryResult(new ArrayList<>(bs.getBindingNames())); + Iterator iterator = b.getQueryResult().iterator(); + while (iterator.hasNext()) + writer.handleSolution(iterator.next()); + writer.endQueryResult(); + } + assertTrue(Files.size(tf) > 0); + try (ZipFile zipFile = new ZipFile(tf.toFile())) { + Iterator iter = zipFile.stream().iterator(); + assertTrue(iter.hasNext()); + assertNotNull(iter.next().getName()); + } + } + +} diff --git a/core/queryresultio/pom.xml b/core/queryresultio/pom.xml index c1ba95a8c53..6ddaaca2bab 100644 --- a/core/queryresultio/pom.xml +++ b/core/queryresultio/pom.xml @@ -15,6 +15,8 @@ binary sparqljson sparqlxml + xlsx + ods text diff --git a/core/queryresultio/xlsx/pom.xml b/core/queryresultio/xlsx/pom.xml new file mode 100644 index 00000000000..65c3bd02a17 --- /dev/null +++ b/core/queryresultio/xlsx/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + org.eclipse.rdf4j + rdf4j-queryresultio + 5.1.0-SNAPSHOT + + rdf4j-queryresultio-sparqlxlsx + RDF4J: Query result IO - XSLX + Query result parser and writer implementation for an non standardized SPARQL Query Results XSLX Format. + + + ${project.groupId} + rdf4j-queryresultio-api + ${project.version} + + + ${project.groupId} + rdf4j-query + ${project.version} + + + ${project.groupId} + rdf4j-model + ${project.version} + + + ${project.groupId} + rdf4j-common-xml + ${project.version} + + + ${project.groupId} + rdf4j-queryresultio-testsuite + ${project.version} + test + + + org.apache.poi + poi + 5.4.1 + + + org.apache.poi + poi-ooxml + 5.4.1 + + + diff --git a/core/queryresultio/xlsx/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlxslx/SPARQLResultsXLSXWriter.java b/core/queryresultio/xlsx/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlxslx/SPARQLResultsXLSXWriter.java new file mode 100644 index 00000000000..f55cf1b9495 --- /dev/null +++ b/core/queryresultio/xlsx/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlxslx/SPARQLResultsXLSXWriter.java @@ -0,0 +1,415 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.query.resultio.sparqlxslx; + +import java.awt.Color; +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigDecimal; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.poi.common.usermodel.HyperlinkType; +import org.apache.poi.ooxml.POIXMLProperties; +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.xssf.usermodel.IndexedColorMap; +import org.apache.poi.xssf.usermodel.XSSFCell; +import org.apache.poi.xssf.usermodel.XSSFCellStyle; +import org.apache.poi.xssf.usermodel.XSSFColor; +import org.apache.poi.xssf.usermodel.XSSFHyperlink; +import org.apache.poi.xssf.usermodel.XSSFRichTextString; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbookFactory; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Literal; +import org.eclipse.rdf4j.model.Triple; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.base.CoreDatatype; +import org.eclipse.rdf4j.model.base.CoreDatatype.XSD; +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.query.Binding; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.QueryResultHandlerException; +import org.eclipse.rdf4j.query.TupleQueryResultHandlerException; +import org.eclipse.rdf4j.query.resultio.QueryResultFormat; +import org.eclipse.rdf4j.query.resultio.TupleQueryResultFormat; +import org.eclipse.rdf4j.query.resultio.TupleQueryResultWriter; +import org.eclipse.rdf4j.rio.RioSetting; +import org.eclipse.rdf4j.rio.WriterConfig; + +/** + * Render a SPARQL result set into an ooxml file. + * + * @author Jerven Bolleman + */ +public class SPARQLResultsXLSXWriter implements TupleQueryResultWriter { + + private OutputStream out; + private XSSFWorkbook wb; + private XSSFSheet nice; + private XSSFSheet raw; + private int rawRowIndex; + private int niceRowIndex; + private final Map columnIndexes = new HashMap<>(); + private XSSFCellStyle headerCellStyle; + private XSSFCellStyle iriCellStyle; + private XSSFCellStyle anyiriCellStyle; + private final Map prefixes = new HashMap<>(); + + public SPARQLResultsXLSXWriter(OutputStream out) { + this.out = out; + wb = new XSSFWorkbookFactory().create(); + nice = wb.createSheet("nice"); + raw = wb.createSheet("raw"); + headerCellStyle = wb.createCellStyle(); + IndexedColorMap colorMap = wb.getStylesSource().getIndexedColors(); + XSSFColor lightGray = new XSSFColor(Color.LIGHT_GRAY, colorMap); + XSSFColor blue = new XSSFColor(Color.BLUE, colorMap); + XSSFColor magenta = new XSSFColor(Color.MAGENTA, colorMap); + headerCellStyle.setFillForegroundColor(lightGray); + headerCellStyle.setAlignment(HorizontalAlignment.RIGHT); + headerCellStyle.setBorderBottom(BorderStyle.MEDIUM); + headerCellStyle.setBorderLeft(BorderStyle.MEDIUM); + headerCellStyle.setBorderRight(BorderStyle.MEDIUM); + headerCellStyle.setBorderTop(BorderStyle.MEDIUM); + + iriCellStyle = wb.createCellStyle(); + Font hlinkFont = wb.createFont(); + hlinkFont.setUnderline(Font.U_SINGLE); + hlinkFont.setColor(blue.getIndex()); + iriCellStyle.setFont(hlinkFont); + + anyiriCellStyle = wb.createCellStyle(); + Font anyhlinkFont = wb.createFont(); + anyhlinkFont.setUnderline(Font.U_SINGLE); + anyhlinkFont.setColor(magenta.getIndex()); + anyiriCellStyle.setFont(hlinkFont); + + } + + @Override + public void handleBoolean(boolean value) throws QueryResultHandlerException { + raw.createRow(0).createCell(0).setCellValue(value); + } + + @Override + public void handleLinks(List linkUrls) throws QueryResultHandlerException { + POIXMLProperties properties = wb.getProperties(); + properties.getCustomProperties().addProperty("links", linkUrls.stream().collect(Collectors.joining(", "))); + } + + @Override + public void startQueryResult(List bindingNames) throws TupleQueryResultHandlerException { + XSSFRow rawRow = raw.createRow(rawRowIndex++); + XSSFRow niceRow = nice.createRow(niceRowIndex++); + int columnIndex = 0; + for (String bindingName : bindingNames) { + columnIndexes.put(bindingName, columnIndex); + XSSFCell rawHeader = rawRow.createCell(columnIndex); + rawHeader.setCellValue(bindingName); + rawHeader.setCellStyle(headerCellStyle); + XSSFCell niceHeader = niceRow.createCell(columnIndex); + niceHeader.setCellValue(bindingName); + niceHeader.setCellStyle(headerCellStyle); + + columnIndex++; + } + } + + @Override + public void endQueryResult() throws TupleQueryResultHandlerException { + try { + for (int c = 0; c < columnIndexes.size(); c++) { + nice.autoSizeColumn(c); + raw.autoSizeColumn(c); + } + wb.write(out); + wb.close(); + } catch (IOException e) { + throw new TupleQueryResultHandlerException(e); + } + } + + @Override + public void handleSolution(BindingSet bindingSet) throws TupleQueryResultHandlerException { + XSSFRow rawRow = raw.createRow(rawRowIndex++); + XSSFRow niceRow = nice.createRow(niceRowIndex++); + for (Binding b : bindingSet) { + int ci = columnIndexes.get(b.getName()); + rawRow.createCell(ci).setCellValue(b.getValue().stringValue()); + XSSFCell nc = niceRow.createCell(ci); + Value v = b.getValue(); + if (v.isLiteral()) { + handleLiteral(nc, v); + } else if (v instanceof IRI) { + handleIri(nc, v); + } else if (v.isBNode()) { + handleIri(nc, v); + } else if (v instanceof Triple) { + handleTriple(nc, v); + } + } + } + + private void handleLiteral(XSSFCell nc, Value v) { + Literal l = (Literal) v; + CoreDatatype cd = l.getCoreDatatype(); + if (cd != null) { + if (cd.isXSDDatatype()) { + handeXSDDatatype(nc, l); + } else if (cd.isGEODatatype()) { + handeGeoDatatype(nc, l); + } else if (cd.isRDFDatatype()) { + handeRDFDatatype(nc, l); + } + } else if (l.getLanguage().isPresent()) { + handleLanguageString(nc, l); + } else { + nc.setCellValue(v.stringValue()); + } + } + + private void handeRDFDatatype(XSSFCell nc, Literal l) { + defaultFormat(nc, l); + } + + private void handeGeoDatatype(XSSFCell nc, Literal l) { + defaultFormat(nc, l); + } + + private void handeXSDDatatype(XSSFCell nc, Literal l) { + XSD as = l.getCoreDatatype().asXSDDatatypeOrNull(); + if (as == null) { + nc.setCellValue(l.stringValue()); + } else { + switch (as) { + case ANYURI: { + handleIri(nc, SimpleValueFactory.getInstance().createIRI(l.stringValue())); + nc.setCellStyle(anyiriCellStyle); + break; + } + case BOOLEAN: { + nc.setCellValue(l.booleanValue()); + break; + } + case BYTE: { + nc.setCellValue(l.byteValue()); + break; + } + case DATE: { + nc.setCellValue(l.calendarValue().toGregorianCalendar()); + break; + } + case DATETIME: { + nc.setCellValue(l.calendarValue().toGregorianCalendar()); + break; + } + case DATETIMESTAMP: { + nc.setCellValue(l.calendarValue().toGregorianCalendar()); + break; + } + case DAYTIMEDURATION: { + formatAsDate(nc, l); + break; + } + case DECIMAL: { + BigDecimal dv = l.decimalValue(); + nc.setCellValue(dv.toPlainString()); + nc.setCellType(CellType.NUMERIC); + break; + } + case DOUBLE: { + nc.setCellValue(l.doubleValue()); + nc.setCellType(CellType.NUMERIC); + break; + } + + case FLOAT: { + nc.setCellValue(l.floatValue()); + nc.setCellType(CellType.NUMERIC); + break; + } + case GDAY: + formatAsDate(nc, l); + break; + case GMONTH: + formatAsDate(nc, l); + break; + case GYEAR: + formatAsDate(nc, l); + break; + case GMONTHDAY: + formatAsDate(nc, l); + break; + case GYEARMONTH: + formatAsDate(nc, l); + break; + case DURATION: + formatAsDate(nc, l); + break; + case INT: + nc.setCellValue(l.intValue()); + nc.setCellType(CellType.NUMERIC); + break; + case INTEGER: + nc.setCellValue(l.integerValue().doubleValue()); + nc.setCellType(CellType.NUMERIC); + break; + case LONG: { + nc.setCellValue(l.longValue()); + nc.setCellType(CellType.NUMERIC); + break; + } + case NEGATIVE_INTEGER: + nc.setCellValue(l.integerValue().doubleValue()); + nc.setCellType(CellType.NUMERIC); + break; + case NON_NEGATIVE_INTEGER: + nc.setCellValue(l.integerValue().doubleValue()); + nc.setCellType(CellType.NUMERIC); + break; + case NON_POSITIVE_INTEGER: + nc.setCellValue(l.integerValue().doubleValue()); + nc.setCellType(CellType.NUMERIC); + break; + case SHORT: + nc.setCellValue(l.shortValue()); + nc.setCellType(CellType.NUMERIC); + break; + case UNSIGNED_BYTE: + case UNSIGNED_INT: + case UNSIGNED_LONG: + case UNSIGNED_SHORT: + nc.setCellValue(l.longValue()); + nc.setCellType(CellType.NUMERIC); + break; + case YEARMONTHDURATION: + formatAsDate(nc, l); + break; + default: + defaultFormat(nc, l); + break; + } + } + } + + private void defaultFormat(XSSFCell nc, Literal l) { + nc.setCellValue(l.stringValue()); + + } + + private void formatAsDate(XSSFCell nc, Literal l) { + nc.setCellValue(l.stringValue()); + + } + + private void handleTriple(XSSFCell nc, Value v) { + Triple t = (Triple) v; + XSSFRichTextString r = new XSSFRichTextString(); + r.setString( + t.getSubject().stringValue() + " " + formatIri(t.getPredicate()) + " " + t.getObject().stringValue()); + nc.setCellValue(r); + } + + private void handleIri(XSSFCell nc, Value v) { + IRI i = (IRI) v; + XSSFHyperlink link = wb.getCreationHelper().createHyperlink(HyperlinkType.URL); + link.setAddress(v.stringValue()); + nc.setHyperlink(link); + + String ns = formatIri(i); + nc.setCellValue(ns); + } + + private String formatIri(IRI i) { + String ns; + String localName = i.getLocalName(); + String namespace = i.getNamespace(); + if (prefixes.containsKey(namespace)) { + return prefixes.get(namespace) + ":" + i.stringValue().substring(namespace.length()); + } else if (localName == null || localName.isEmpty()) { + ns = i.stringValue(); + } else { + ns = localName; + } + return ns; + } + + private void handleLanguageString(XSSFCell nc, Literal l) { + // TODO indicate language maybe with a comment? or a color + nc.setCellValue(l.stringValue()); + } + + @Override + public QueryResultFormat getQueryResultFormat() { + return TupleQueryResultFormat.XSLX; + } + + @Override + public void handleNamespace(String prefix, String uri) throws QueryResultHandlerException { + prefixes.put(uri, prefix); + } + + @Override + public void startDocument() throws QueryResultHandlerException { + + } + + @Override + public void setWriterConfig(WriterConfig config) { + // TODO Auto-generated method stub + + } + + @Override + public WriterConfig getWriterConfig() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Collection> getSupportedSettings() { + return Collections.emptyList(); + } + + @Override + public TupleQueryResultFormat getTupleQueryResultFormat() { + return TupleQueryResultFormat.XSLX; + } + + @Override + public void handleStylesheet(String stylesheetUrl) throws QueryResultHandlerException { + // TODO Auto-generated method stub + + } + + @Override + public void startHeader() throws QueryResultHandlerException { + // TODO Auto-generated method stub + + } + + @Override + public void endHeader() throws QueryResultHandlerException { + // TODO Auto-generated method stub + + } + +} diff --git a/core/queryresultio/xlsx/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlxslx/SPARQLResultsXLSXWriterFactory.java b/core/queryresultio/xlsx/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlxslx/SPARQLResultsXLSXWriterFactory.java new file mode 100644 index 00000000000..01430ec9019 --- /dev/null +++ b/core/queryresultio/xlsx/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlxslx/SPARQLResultsXLSXWriterFactory.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.query.resultio.sparqlxslx; + +import java.io.OutputStream; + +import org.eclipse.rdf4j.query.resultio.TupleQueryResultFormat; +import org.eclipse.rdf4j.query.resultio.TupleQueryResultWriter; +import org.eclipse.rdf4j.query.resultio.TupleQueryResultWriterFactory; + +public class SPARQLResultsXLSXWriterFactory implements TupleQueryResultWriterFactory { + + public SPARQLResultsXLSXWriterFactory() { + super(); + } + + @Override + public TupleQueryResultFormat getTupleQueryResultFormat() { + return TupleQueryResultFormat.XSLX; + } + + /** + * Returns a new instance of SPARQLResultsXLSXWriter. + */ + @Override + public TupleQueryResultWriter getWriter(OutputStream out) { + return new SPARQLResultsXLSXWriter(out); + } +} diff --git a/core/queryresultio/xlsx/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlxslx/package.html b/core/queryresultio/xlsx/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlxslx/package.html new file mode 100644 index 00000000000..343a35045a7 --- /dev/null +++ b/core/queryresultio/xlsx/src/main/java/org/eclipse/rdf4j/query/resultio/sparqlxslx/package.html @@ -0,0 +1,6 @@ + + + + writers for custom XLSX for SPARQL results + + \ No newline at end of file diff --git a/core/queryresultio/xlsx/src/main/resources/META-INF/services/org.eclipse.rdf4j.query.resultio.TupleQueryResultWriterFactory b/core/queryresultio/xlsx/src/main/resources/META-INF/services/org.eclipse.rdf4j.query.resultio.TupleQueryResultWriterFactory new file mode 100644 index 00000000000..e2c32a427b4 --- /dev/null +++ b/core/queryresultio/xlsx/src/main/resources/META-INF/services/org.eclipse.rdf4j.query.resultio.TupleQueryResultWriterFactory @@ -0,0 +1 @@ +org.eclipse.rdf4j.query.resultio.sparqlxslx.SPARQLResultsXLSXWriterFactory diff --git a/core/queryresultio/xlsx/src/test/java/org/eclipse/rdf4j/query/resultio/sparqlxml/SPARQLXLSXTupleTest.java b/core/queryresultio/xlsx/src/test/java/org/eclipse/rdf4j/query/resultio/sparqlxml/SPARQLXLSXTupleTest.java new file mode 100644 index 00000000000..5039885c354 --- /dev/null +++ b/core/queryresultio/xlsx/src/test/java/org/eclipse/rdf4j/query/resultio/sparqlxml/SPARQLXLSXTupleTest.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.query.resultio.sparqlxml; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.impl.MapBindingSet; +import org.eclipse.rdf4j.query.impl.TupleQueryResultBuilder; +import org.eclipse.rdf4j.query.resultio.sparqlxslx.SPARQLResultsXLSXWriter; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +/** + * @author Jerven Bolleman + */ +public class SPARQLXLSXTupleTest { + + @Test + void simpleCase(@TempDir Path dir) throws IOException { + Files.createDirectories(dir); + + Path tf = dir.resolve("test.xlsx"); +// Path tf = Paths.get("/home/jbollema/test.xlsx"); + TupleQueryResultBuilder b = new TupleQueryResultBuilder(); + b.startQueryResult(List.of("boolean", "iri", "int")); + MapBindingSet bs = new MapBindingSet(); + bs.setBinding("boolean", SimpleValueFactory.getInstance().createLiteral(true)); + bs.setBinding("iri", SimpleValueFactory.getInstance().createIRI("https://example.org/iri")); + bs.setBinding("int", SimpleValueFactory.getInstance().createLiteral(1)); + b.handleSolution(bs); + MapBindingSet bs2 = new MapBindingSet(); + bs2.setBinding("boolean", SimpleValueFactory.getInstance().createLiteral(false)); + bs2.setBinding("iri", SimpleValueFactory.getInstance().createIRI("https://example.org/iri/test")); + bs2.setBinding("int", SimpleValueFactory.getInstance().createLiteral(-9)); + b.handleSolution(bs2); + + MapBindingSet bs3 = new MapBindingSet(); + bs3.setBinding("iri", SimpleValueFactory.getInstance().createIRI("http://purl.uniprot.org/taxonomy/9606")); + bs3.setBinding("int", SimpleValueFactory.getInstance().createLiteral(-9)); + b.handleSolution(bs3); + List links = List.of("http://example.org/link1"); + b.handleLinks(links); + try (var out = Files.newOutputStream(tf)) { + SPARQLResultsXLSXWriter writer = new SPARQLResultsXLSXWriter(out); + writer.handleNamespace("taxon", "http://purl.uniprot.org/taxonomy/"); + writer.handleLinks(links); + writer.startQueryResult(new ArrayList<>(bs.getBindingNames())); + Iterator iterator = b.getQueryResult().iterator(); + while (iterator.hasNext()) + writer.handleSolution(iterator.next()); + writer.endQueryResult(); + } + assertTrue(Files.size(tf) > 0); + + try (XSSFWorkbook wb = new XSSFWorkbook(Files.newInputStream(tf))) { + XSSFSheet raw = wb.getSheet("raw"); + assertNotNull(raw); + assertNotNull(wb.getSheet("nice")); + + XSSFRow headerRow = raw.getRow(0); + assertEquals("boolean", headerRow.getCell(0).getStringCellValue()); + assertEquals("iri", headerRow.getCell(1).getStringCellValue()); + assertEquals("int", headerRow.getCell(2).getStringCellValue()); + } + } + +} diff --git a/pom.xml b/pom.xml index 87e98c6a41b..949eac93350 100644 --- a/pom.xml +++ b/pom.xml @@ -428,10 +428,11 @@ commons-cli 1.4 + commons-io commons-io - 2.14.0 + 2.18.0 commons-codec From 3d77b677194de35704c82fa38ce661f9bfb81851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Sat, 6 Sep 2025 11:54:26 +0200 Subject: [PATCH 18/46] set correct version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Håvard Ottestad --- assembly-descriptors/pom.xml | 2 +- assembly/pom.xml | 2 +- bom/pom.xml | 2 +- compliance/elasticsearch/pom.xml | 2 +- compliance/geosparql/pom.xml | 2 +- compliance/lucene/pom.xml | 2 +- compliance/model/pom.xml | 2 +- compliance/pom.xml | 2 +- compliance/repository/pom.xml | 2 +- compliance/rio/pom.xml | 2 +- compliance/solr/pom.xml | 2 +- compliance/sparql/pom.xml | 2 +- core/client/pom.xml | 2 +- core/collection-factory/api/pom.xml | 2 +- core/collection-factory/mapdb/pom.xml | 2 +- core/collection-factory/mapdb3/pom.xml | 2 +- core/collection-factory/pom.xml | 2 +- core/common/annotation/pom.xml | 2 +- core/common/exception/pom.xml | 2 +- core/common/io/pom.xml | 2 +- core/common/iterator/pom.xml | 2 +- core/common/order/pom.xml | 2 +- core/common/pom.xml | 2 +- core/common/text/pom.xml | 2 +- core/common/transaction/pom.xml | 2 +- core/common/xml/pom.xml | 2 +- core/http/client/pom.xml | 2 +- core/http/pom.xml | 2 +- core/http/protocol/pom.xml | 2 +- core/model-api/pom.xml | 2 +- core/model-vocabulary/pom.xml | 2 +- core/model/pom.xml | 2 +- core/pom.xml | 2 +- core/query/pom.xml | 2 +- core/queryalgebra/evaluation/pom.xml | 2 +- core/queryalgebra/geosparql/pom.xml | 2 +- core/queryalgebra/model/pom.xml | 2 +- core/queryalgebra/pom.xml | 2 +- core/queryparser/api/pom.xml | 2 +- core/queryparser/pom.xml | 2 +- core/queryparser/sparql/pom.xml | 2 +- core/queryrender/pom.xml | 2 +- core/queryresultio/api/pom.xml | 2 +- core/queryresultio/binary/pom.xml | 2 +- core/queryresultio/pom.xml | 2 +- core/queryresultio/sparqljson/pom.xml | 2 +- core/queryresultio/sparqlxml/pom.xml | 2 +- core/queryresultio/text/pom.xml | 2 +- core/repository/api/pom.xml | 2 +- core/repository/contextaware/pom.xml | 2 +- core/repository/dataset/pom.xml | 2 +- core/repository/event/pom.xml | 2 +- core/repository/http/pom.xml | 2 +- core/repository/manager/pom.xml | 2 +- core/repository/pom.xml | 2 +- core/repository/sail/pom.xml | 2 +- core/repository/sparql/pom.xml | 2 +- core/rio/api/pom.xml | 2 +- core/rio/binary/pom.xml | 2 +- core/rio/datatypes/pom.xml | 2 +- core/rio/hdt/pom.xml | 2 +- core/rio/jsonld-legacy/pom.xml | 2 +- core/rio/jsonld/pom.xml | 2 +- core/rio/languages/pom.xml | 2 +- core/rio/n3/pom.xml | 2 +- core/rio/nquads/pom.xml | 2 +- core/rio/ntriples/pom.xml | 2 +- core/rio/pom.xml | 2 +- core/rio/rdfjson/pom.xml | 2 +- core/rio/rdfxml/pom.xml | 2 +- core/rio/trig/pom.xml | 2 +- core/rio/trix/pom.xml | 2 +- core/rio/turtle/pom.xml | 2 +- core/sail/api/pom.xml | 2 +- core/sail/base/pom.xml | 2 +- core/sail/elasticsearch-store/pom.xml | 2 +- core/sail/elasticsearch/pom.xml | 2 +- core/sail/extensible-store/pom.xml | 2 +- core/sail/inferencer/pom.xml | 2 +- core/sail/lmdb/pom.xml | 2 +- core/sail/lucene-api/pom.xml | 2 +- core/sail/lucene/pom.xml | 2 +- core/sail/memory/pom.xml | 2 +- core/sail/model/pom.xml | 2 +- core/sail/nativerdf/pom.xml | 2 +- core/sail/pom.xml | 2 +- core/sail/shacl/pom.xml | 2 +- core/sail/solr/pom.xml | 2 +- core/sparqlbuilder/pom.xml | 2 +- core/spin/pom.xml | 2 +- core/storage/pom.xml | 2 +- examples/pom.xml | 2 +- pom.xml | 2 +- spring-components/pom.xml | 2 +- spring-components/rdf4j-spring-demo/pom.xml | 2 +- spring-components/rdf4j-spring/pom.xml | 2 +- spring-components/spring-boot-sparql-web/pom.xml | 2 +- testsuites/benchmark/pom.xml | 2 +- testsuites/geosparql/pom.xml | 2 +- testsuites/lucene/pom.xml | 2 +- testsuites/model/pom.xml | 2 +- testsuites/pom.xml | 2 +- testsuites/queryresultio/pom.xml | 2 +- testsuites/repository/pom.xml | 2 +- testsuites/rio/pom.xml | 2 +- testsuites/sail/pom.xml | 2 +- testsuites/sparql/pom.xml | 2 +- tools/config/pom.xml | 2 +- tools/console/pom.xml | 2 +- tools/federation/pom.xml | 2 +- tools/pom.xml | 2 +- tools/runtime-osgi/pom.xml | 2 +- tools/runtime/pom.xml | 2 +- tools/server-spring/pom.xml | 2 +- tools/server/pom.xml | 2 +- tools/workbench/pom.xml | 2 +- 116 files changed, 116 insertions(+), 116 deletions(-) diff --git a/assembly-descriptors/pom.xml b/assembly-descriptors/pom.xml index 5c4721a849f..25b0b093889 100644 --- a/assembly-descriptors/pom.xml +++ b/assembly-descriptors/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-assembly-descriptors RDF4J: Assembly Descriptors diff --git a/assembly/pom.xml b/assembly/pom.xml index c4ed09b9904..a88d567f0e5 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-assembly pom diff --git a/bom/pom.xml b/bom/pom.xml index 3d804a0381d..8056082ccc1 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-bom pom diff --git a/compliance/elasticsearch/pom.xml b/compliance/elasticsearch/pom.xml index e5074472063..794dacb7bd7 100644 --- a/compliance/elasticsearch/pom.xml +++ b/compliance/elasticsearch/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-elasticsearch-compliance RDF4J: Elasticsearch Sail Tests diff --git a/compliance/geosparql/pom.xml b/compliance/geosparql/pom.xml index 81aa33c74f8..41e8785595b 100644 --- a/compliance/geosparql/pom.xml +++ b/compliance/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-geosparql-compliance RDF4J: GeoSPARQL compliance tests diff --git a/compliance/lucene/pom.xml b/compliance/lucene/pom.xml index 0032b2d0c5b..dcf84054c6d 100644 --- a/compliance/lucene/pom.xml +++ b/compliance/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-lucene-compliance RDF4J: Lucene Sail Tests diff --git a/compliance/model/pom.xml b/compliance/model/pom.xml index 75e9cae5557..3d3ac105fa9 100644 --- a/compliance/model/pom.xml +++ b/compliance/model/pom.xml @@ -3,7 +3,7 @@ rdf4j-compliance org.eclipse.rdf4j - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT 4.0.0 rdf4j-model-compliance diff --git a/compliance/pom.xml b/compliance/pom.xml index d43b32c6b2c..7a3f7a3e7ff 100644 --- a/compliance/pom.xml +++ b/compliance/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-compliance pom diff --git a/compliance/repository/pom.xml b/compliance/repository/pom.xml index 0b93ae5f1b2..069ade53eec 100644 --- a/compliance/repository/pom.xml +++ b/compliance/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-compliance war diff --git a/compliance/rio/pom.xml b/compliance/rio/pom.xml index a5edac65d27..850062bf2f4 100644 --- a/compliance/rio/pom.xml +++ b/compliance/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-compliance RDF4J: Rio compliance tests diff --git a/compliance/solr/pom.xml b/compliance/solr/pom.xml index 9aa16eabdde..454f5295ca6 100644 --- a/compliance/solr/pom.xml +++ b/compliance/solr/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-solr-compliance RDF4J: Solr Sail Tests diff --git a/compliance/sparql/pom.xml b/compliance/sparql/pom.xml index 7df196bbbe4..3340661c18d 100644 --- a/compliance/sparql/pom.xml +++ b/compliance/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparql-compliance war diff --git a/core/client/pom.xml b/core/client/pom.xml index d5eda75d8c4..845360a26cc 100644 --- a/core/client/pom.xml +++ b/core/client/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-client RDF4J: Client Libraries diff --git a/core/collection-factory/api/pom.xml b/core/collection-factory/api/pom.xml index 42229ebe467..5ba6df646b7 100644 --- a/core/collection-factory/api/pom.xml +++ b/core/collection-factory/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-api RDF4J: Collection Factory - API diff --git a/core/collection-factory/mapdb/pom.xml b/core/collection-factory/mapdb/pom.xml index 02f8c48aa20..c8e843b984e 100644 --- a/core/collection-factory/mapdb/pom.xml +++ b/core/collection-factory/mapdb/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-mapdb RDF4J: Collection Factory - Map DB backed diff --git a/core/collection-factory/mapdb3/pom.xml b/core/collection-factory/mapdb3/pom.xml index bfca3b2dd13..a00d4c8e811 100644 --- a/core/collection-factory/mapdb3/pom.xml +++ b/core/collection-factory/mapdb3/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-mapdb3 RDF4J: Collection Factory - Map DB v3 backed diff --git a/core/collection-factory/pom.xml b/core/collection-factory/pom.xml index b315c27ed62..bb1df7bb226 100644 --- a/core/collection-factory/pom.xml +++ b/core/collection-factory/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory pom diff --git a/core/common/annotation/pom.xml b/core/common/annotation/pom.xml index af81e6b443a..f5f53a65e21 100644 --- a/core/common/annotation/pom.xml +++ b/core/common/annotation/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-annotation RDF4J: common annotation diff --git a/core/common/exception/pom.xml b/core/common/exception/pom.xml index 8ec5ac952f6..bbd533f1adf 100644 --- a/core/common/exception/pom.xml +++ b/core/common/exception/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-exception RDF4J: common exception diff --git a/core/common/io/pom.xml b/core/common/io/pom.xml index a4dfca5dc1a..f99f7f2af4e 100644 --- a/core/common/io/pom.xml +++ b/core/common/io/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-io RDF4J: common IO diff --git a/core/common/iterator/pom.xml b/core/common/iterator/pom.xml index dd332d41149..c8b62012574 100644 --- a/core/common/iterator/pom.xml +++ b/core/common/iterator/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-iterator RDF4J: common iterators diff --git a/core/common/order/pom.xml b/core/common/order/pom.xml index 79ec18f60e4..5228081fc0e 100644 --- a/core/common/order/pom.xml +++ b/core/common/order/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-order RDF4J: common order diff --git a/core/common/pom.xml b/core/common/pom.xml index f60c85d3202..fc821867500 100644 --- a/core/common/pom.xml +++ b/core/common/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common pom diff --git a/core/common/text/pom.xml b/core/common/text/pom.xml index 9a674d41662..f26391a3d11 100644 --- a/core/common/text/pom.xml +++ b/core/common/text/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-text RDF4J: common text diff --git a/core/common/transaction/pom.xml b/core/common/transaction/pom.xml index 3ccdb318cdd..2a327eba54c 100644 --- a/core/common/transaction/pom.xml +++ b/core/common/transaction/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-transaction RDF4J: common transaction diff --git a/core/common/xml/pom.xml b/core/common/xml/pom.xml index cd08ac1ff58..76184d6e127 100644 --- a/core/common/xml/pom.xml +++ b/core/common/xml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-xml RDF4J: common XML diff --git a/core/http/client/pom.xml b/core/http/client/pom.xml index 6aa0f9091c1..8e26e6c8d3f 100644 --- a/core/http/client/pom.xml +++ b/core/http/client/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-http - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-client RDF4J: HTTP client diff --git a/core/http/pom.xml b/core/http/pom.xml index 30c11f8d0c4..ee3e0261ad0 100644 --- a/core/http/pom.xml +++ b/core/http/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http pom diff --git a/core/http/protocol/pom.xml b/core/http/protocol/pom.xml index 82f56970e66..a5d66a693e0 100644 --- a/core/http/protocol/pom.xml +++ b/core/http/protocol/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-http - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-protocol RDF4J: HTTP protocol diff --git a/core/model-api/pom.xml b/core/model-api/pom.xml index 285953f878e..cf07e38798c 100644 --- a/core/model-api/pom.xml +++ b/core/model-api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-api RDF4J: Model API diff --git a/core/model-vocabulary/pom.xml b/core/model-vocabulary/pom.xml index 292b8bff7e1..bd27791c1d8 100644 --- a/core/model-vocabulary/pom.xml +++ b/core/model-vocabulary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-vocabulary RDF4J: RDF Vocabularies diff --git a/core/model/pom.xml b/core/model/pom.xml index 3223807fa4f..17a4deb868b 100644 --- a/core/model/pom.xml +++ b/core/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model RDF4J: Model diff --git a/core/pom.xml b/core/pom.xml index 242470997f1..ed94faedcde 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-core pom diff --git a/core/query/pom.xml b/core/query/pom.xml index 09f0f018e99..6600bd51a4e 100644 --- a/core/query/pom.xml +++ b/core/query/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-query RDF4J: Query diff --git a/core/queryalgebra/evaluation/pom.xml b/core/queryalgebra/evaluation/pom.xml index 2822725c964..a29c5f1ab29 100644 --- a/core/queryalgebra/evaluation/pom.xml +++ b/core/queryalgebra/evaluation/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-evaluation RDF4J: Query algebra - evaluation diff --git a/core/queryalgebra/geosparql/pom.xml b/core/queryalgebra/geosparql/pom.xml index 38f3d8817a3..10fcbc837ce 100644 --- a/core/queryalgebra/geosparql/pom.xml +++ b/core/queryalgebra/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-geosparql RDF4J: Query algebra - GeoSPARQL diff --git a/core/queryalgebra/model/pom.xml b/core/queryalgebra/model/pom.xml index e8f711f02e7..c561441a066 100644 --- a/core/queryalgebra/model/pom.xml +++ b/core/queryalgebra/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-model RDF4J: Query algebra - model diff --git a/core/queryalgebra/pom.xml b/core/queryalgebra/pom.xml index 90b02c52d98..fb04d8339cd 100644 --- a/core/queryalgebra/pom.xml +++ b/core/queryalgebra/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra pom diff --git a/core/queryparser/api/pom.xml b/core/queryparser/api/pom.xml index 4dc73b9fc46..6450c375f42 100644 --- a/core/queryparser/api/pom.xml +++ b/core/queryparser/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryparser - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser-api RDF4J: Query parser - API diff --git a/core/queryparser/pom.xml b/core/queryparser/pom.xml index 94240c87a83..0dfcb8573d3 100644 --- a/core/queryparser/pom.xml +++ b/core/queryparser/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser pom diff --git a/core/queryparser/sparql/pom.xml b/core/queryparser/sparql/pom.xml index 0d1ae0bf29e..d21ecf2b1b2 100644 --- a/core/queryparser/sparql/pom.xml +++ b/core/queryparser/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryparser - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser-sparql RDF4J: Query parser - SPARQL diff --git a/core/queryrender/pom.xml b/core/queryrender/pom.xml index ea7ae6fb2b3..8db12169a80 100644 --- a/core/queryrender/pom.xml +++ b/core/queryrender/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryrender RDF4J: Query Rendering diff --git a/core/queryresultio/api/pom.xml b/core/queryresultio/api/pom.xml index 7b1c7c00533..273fe3b9801 100644 --- a/core/queryresultio/api/pom.xml +++ b/core/queryresultio/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-api RDF4J: Query result IO - API diff --git a/core/queryresultio/binary/pom.xml b/core/queryresultio/binary/pom.xml index ec04ccc05f9..34df6c0b64f 100644 --- a/core/queryresultio/binary/pom.xml +++ b/core/queryresultio/binary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-binary RDF4J: Query result IO - binary diff --git a/core/queryresultio/pom.xml b/core/queryresultio/pom.xml index 826fee3779e..c1ba95a8c53 100644 --- a/core/queryresultio/pom.xml +++ b/core/queryresultio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio pom diff --git a/core/queryresultio/sparqljson/pom.xml b/core/queryresultio/sparqljson/pom.xml index 9918f32af65..c5f589b6457 100644 --- a/core/queryresultio/sparqljson/pom.xml +++ b/core/queryresultio/sparqljson/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-sparqljson RDF4J: Query result IO - SPARQL/JSON diff --git a/core/queryresultio/sparqlxml/pom.xml b/core/queryresultio/sparqlxml/pom.xml index f5c5cc20fd7..8d7bee2745b 100644 --- a/core/queryresultio/sparqlxml/pom.xml +++ b/core/queryresultio/sparqlxml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-sparqlxml RDF4J: Query result IO - SPARQL/XML diff --git a/core/queryresultio/text/pom.xml b/core/queryresultio/text/pom.xml index 3ca2b8a1557..e5f67e0b32e 100644 --- a/core/queryresultio/text/pom.xml +++ b/core/queryresultio/text/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-text RDF4J: Query result IO - plain text booleans diff --git a/core/repository/api/pom.xml b/core/repository/api/pom.xml index 85243b3d6bb..48fdeecfad7 100644 --- a/core/repository/api/pom.xml +++ b/core/repository/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-api RDF4J: Repository - API diff --git a/core/repository/contextaware/pom.xml b/core/repository/contextaware/pom.xml index 5692938bacf..39bcbf0668c 100644 --- a/core/repository/contextaware/pom.xml +++ b/core/repository/contextaware/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-contextaware RDF4J: Repository - context aware (wrapper) diff --git a/core/repository/dataset/pom.xml b/core/repository/dataset/pom.xml index ca8654fa08a..c843dccda42 100644 --- a/core/repository/dataset/pom.xml +++ b/core/repository/dataset/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-dataset RDF4J: DatasetRepository (wrapper) diff --git a/core/repository/event/pom.xml b/core/repository/event/pom.xml index f22e17e4f3c..3a3109cf967 100644 --- a/core/repository/event/pom.xml +++ b/core/repository/event/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-event RDF4J: Repository - event (wrapper) diff --git a/core/repository/http/pom.xml b/core/repository/http/pom.xml index 94e7aa82f76..4fc07dc06e9 100644 --- a/core/repository/http/pom.xml +++ b/core/repository/http/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-http RDF4J: HTTPRepository diff --git a/core/repository/manager/pom.xml b/core/repository/manager/pom.xml index 7265653cccd..bad53d7101e 100644 --- a/core/repository/manager/pom.xml +++ b/core/repository/manager/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-manager RDF4J: Repository manager diff --git a/core/repository/pom.xml b/core/repository/pom.xml index f1d6b6d9a93..873757a9580 100644 --- a/core/repository/pom.xml +++ b/core/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository pom diff --git a/core/repository/sail/pom.xml b/core/repository/sail/pom.xml index 82e4d168491..fb8fc483b2b 100644 --- a/core/repository/sail/pom.xml +++ b/core/repository/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-sail RDF4J: SailRepository diff --git a/core/repository/sparql/pom.xml b/core/repository/sparql/pom.xml index aacf5862c47..66d5ee263ce 100644 --- a/core/repository/sparql/pom.xml +++ b/core/repository/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-sparql RDF4J: SPARQL Repository diff --git a/core/rio/api/pom.xml b/core/rio/api/pom.xml index b40a28c4ffd..3733b074423 100644 --- a/core/rio/api/pom.xml +++ b/core/rio/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-api RDF4J: Rio - API diff --git a/core/rio/binary/pom.xml b/core/rio/binary/pom.xml index db4f0c92948..4ff5469eddf 100644 --- a/core/rio/binary/pom.xml +++ b/core/rio/binary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-binary RDF4J: Rio - Binary diff --git a/core/rio/datatypes/pom.xml b/core/rio/datatypes/pom.xml index 8e35af7d325..b9d9dcadcfb 100644 --- a/core/rio/datatypes/pom.xml +++ b/core/rio/datatypes/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-datatypes RDF4J: Rio - Datatypes diff --git a/core/rio/hdt/pom.xml b/core/rio/hdt/pom.xml index d728f5585a8..f3647663a65 100644 --- a/core/rio/hdt/pom.xml +++ b/core/rio/hdt/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-hdt jar diff --git a/core/rio/jsonld-legacy/pom.xml b/core/rio/jsonld-legacy/pom.xml index 5acbb2e4e60..7509ef4b834 100644 --- a/core/rio/jsonld-legacy/pom.xml +++ b/core/rio/jsonld-legacy/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-jsonld-legacy RDF4J: Rio - JSON-LD 1.0 (legacy) diff --git a/core/rio/jsonld/pom.xml b/core/rio/jsonld/pom.xml index a74f9d46900..998013a10ec 100644 --- a/core/rio/jsonld/pom.xml +++ b/core/rio/jsonld/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-jsonld RDF4J: Rio - JSON-LD diff --git a/core/rio/languages/pom.xml b/core/rio/languages/pom.xml index f49c5f20124..b35931702bb 100644 --- a/core/rio/languages/pom.xml +++ b/core/rio/languages/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-languages RDF4J: Rio - Languages diff --git a/core/rio/n3/pom.xml b/core/rio/n3/pom.xml index bd0a0bc3787..54ae5fe573c 100644 --- a/core/rio/n3/pom.xml +++ b/core/rio/n3/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-n3 RDF4J: Rio - N3 (writer-only) diff --git a/core/rio/nquads/pom.xml b/core/rio/nquads/pom.xml index 615024c8e28..044216524fb 100644 --- a/core/rio/nquads/pom.xml +++ b/core/rio/nquads/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-nquads RDF4J: Rio - N-Quads diff --git a/core/rio/ntriples/pom.xml b/core/rio/ntriples/pom.xml index 9cfb6c3c239..37553504ed5 100644 --- a/core/rio/ntriples/pom.xml +++ b/core/rio/ntriples/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-ntriples RDF4J: Rio - N-Triples diff --git a/core/rio/pom.xml b/core/rio/pom.xml index ab2b94605a4..77fc1cee292 100644 --- a/core/rio/pom.xml +++ b/core/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio pom diff --git a/core/rio/rdfjson/pom.xml b/core/rio/rdfjson/pom.xml index 8e09c92bc39..55a3e3ad374 100644 --- a/core/rio/rdfjson/pom.xml +++ b/core/rio/rdfjson/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-rdfjson RDF4J: Rio - RDF/JSON diff --git a/core/rio/rdfxml/pom.xml b/core/rio/rdfxml/pom.xml index 5d9af51a8dd..08a1e0f6eb3 100644 --- a/core/rio/rdfxml/pom.xml +++ b/core/rio/rdfxml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-rdfxml RDF4J: Rio - RDF/XML diff --git a/core/rio/trig/pom.xml b/core/rio/trig/pom.xml index 32d98e3cd04..2736b12a916 100644 --- a/core/rio/trig/pom.xml +++ b/core/rio/trig/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-trig RDF4J: Rio - TriG diff --git a/core/rio/trix/pom.xml b/core/rio/trix/pom.xml index 3b2e0b33245..a8b99df7634 100644 --- a/core/rio/trix/pom.xml +++ b/core/rio/trix/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-trix RDF4J: Rio - TriX diff --git a/core/rio/turtle/pom.xml b/core/rio/turtle/pom.xml index 01fe4ee0e1c..087f4221601 100644 --- a/core/rio/turtle/pom.xml +++ b/core/rio/turtle/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-turtle RDF4J: Rio - Turtle diff --git a/core/sail/api/pom.xml b/core/sail/api/pom.xml index 04f32ad4f28..94ba13deba0 100644 --- a/core/sail/api/pom.xml +++ b/core/sail/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-api RDF4J: Sail API diff --git a/core/sail/base/pom.xml b/core/sail/base/pom.xml index 4ead34880f3..37f440d24a5 100644 --- a/core/sail/base/pom.xml +++ b/core/sail/base/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-base RDF4J: Sail base implementations diff --git a/core/sail/elasticsearch-store/pom.xml b/core/sail/elasticsearch-store/pom.xml index 0b1071d6ef3..8542f4a1aaa 100644 --- a/core/sail/elasticsearch-store/pom.xml +++ b/core/sail/elasticsearch-store/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-elasticsearch-store RDF4J: Elasticsearch Store diff --git a/core/sail/elasticsearch/pom.xml b/core/sail/elasticsearch/pom.xml index 24f766b80fc..022319c4697 100644 --- a/core/sail/elasticsearch/pom.xml +++ b/core/sail/elasticsearch/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-elasticsearch RDF4J: Elastic Search Sail Index diff --git a/core/sail/extensible-store/pom.xml b/core/sail/extensible-store/pom.xml index af24c9c0702..e8d6a1af491 100644 --- a/core/sail/extensible-store/pom.xml +++ b/core/sail/extensible-store/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-extensible-store RDF4J: Extensible Store diff --git a/core/sail/inferencer/pom.xml b/core/sail/inferencer/pom.xml index 501efe70b3d..45babf4a3e2 100644 --- a/core/sail/inferencer/pom.xml +++ b/core/sail/inferencer/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-inferencer RDF4J: Inferencer Sails diff --git a/core/sail/lmdb/pom.xml b/core/sail/lmdb/pom.xml index 4641494df00..787fb19c4d4 100644 --- a/core/sail/lmdb/pom.xml +++ b/core/sail/lmdb/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lmdb RDF4J: LmdbStore diff --git a/core/sail/lucene-api/pom.xml b/core/sail/lucene-api/pom.xml index 145bea98d30..a52d440fc00 100644 --- a/core/sail/lucene-api/pom.xml +++ b/core/sail/lucene-api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lucene-api RDF4J: Lucene Sail API diff --git a/core/sail/lucene/pom.xml b/core/sail/lucene/pom.xml index 1b2b13f3bf7..530c6ac3c5d 100644 --- a/core/sail/lucene/pom.xml +++ b/core/sail/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lucene RDF4J: Lucene Sail Index diff --git a/core/sail/memory/pom.xml b/core/sail/memory/pom.xml index 01851743cf5..ae81a8c5531 100644 --- a/core/sail/memory/pom.xml +++ b/core/sail/memory/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-memory RDF4J: MemoryStore diff --git a/core/sail/model/pom.xml b/core/sail/model/pom.xml index a010804afbb..531c473d10a 100644 --- a/core/sail/model/pom.xml +++ b/core/sail/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-model RDF4J: Sail Model diff --git a/core/sail/nativerdf/pom.xml b/core/sail/nativerdf/pom.xml index 345fdefaab2..17dbcc1c962 100644 --- a/core/sail/nativerdf/pom.xml +++ b/core/sail/nativerdf/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-nativerdf RDF4J: NativeStore diff --git a/core/sail/pom.xml b/core/sail/pom.xml index 65f8cfa30c9..cbce9f38bc7 100644 --- a/core/sail/pom.xml +++ b/core/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail pom diff --git a/core/sail/shacl/pom.xml b/core/sail/shacl/pom.xml index 4be51f25b05..9cae94fe046 100644 --- a/core/sail/shacl/pom.xml +++ b/core/sail/shacl/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-shacl RDF4J: SHACL diff --git a/core/sail/solr/pom.xml b/core/sail/solr/pom.xml index 8f5be7b29bb..c79bfa393e7 100644 --- a/core/sail/solr/pom.xml +++ b/core/sail/solr/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-solr RDF4J: Solr Sail Index diff --git a/core/sparqlbuilder/pom.xml b/core/sparqlbuilder/pom.xml index 945ce2966db..2e589196e67 100644 --- a/core/sparqlbuilder/pom.xml +++ b/core/sparqlbuilder/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparqlbuilder RDF4J: SparqlBuilder diff --git a/core/spin/pom.xml b/core/spin/pom.xml index 6f333065d8d..b80e9d40e04 100644 --- a/core/spin/pom.xml +++ b/core/spin/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spin RDF4J: SPIN diff --git a/core/storage/pom.xml b/core/storage/pom.xml index fd1e564e943..6775e4bf837 100644 --- a/core/storage/pom.xml +++ b/core/storage/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-storage RDF4J: Storage Libraries diff --git a/examples/pom.xml b/examples/pom.xml index a85565a5a24..5610e589454 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -7,7 +7,7 @@ org.eclipse.rdf4j rdf4j - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/pom.xml b/pom.xml index e7495e3550e..325a8fff43a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.eclipse.rdf4j rdf4j - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT pom Eclipse RDF4J An extensible Java framework for RDF and SPARQL diff --git a/spring-components/pom.xml b/spring-components/pom.xml index 5f1d0e1e819..571eebb5bce 100644 --- a/spring-components/pom.xml +++ b/spring-components/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT pom diff --git a/spring-components/rdf4j-spring-demo/pom.xml b/spring-components/rdf4j-spring-demo/pom.xml index 04a75697525..e3376a0dd51 100644 --- a/spring-components/rdf4j-spring-demo/pom.xml +++ b/spring-components/rdf4j-spring-demo/pom.xml @@ -7,7 +7,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/spring-components/rdf4j-spring/pom.xml b/spring-components/rdf4j-spring/pom.xml index 41e689e371f..b3616e04951 100644 --- a/spring-components/rdf4j-spring/pom.xml +++ b/spring-components/rdf4j-spring/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spring RDF4J: Spring diff --git a/spring-components/spring-boot-sparql-web/pom.xml b/spring-components/spring-boot-sparql-web/pom.xml index 1f50d723fa7..222e9020799 100644 --- a/spring-components/spring-boot-sparql-web/pom.xml +++ b/spring-components/spring-boot-sparql-web/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spring-boot-sparql-web RDF4J: Spring boot component for a HTTP sparql server diff --git a/testsuites/benchmark/pom.xml b/testsuites/benchmark/pom.xml index c2f1db44769..96c07db5f28 100644 --- a/testsuites/benchmark/pom.xml +++ b/testsuites/benchmark/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-benchmark RDF4J: benchmarks diff --git a/testsuites/geosparql/pom.xml b/testsuites/geosparql/pom.xml index 681e7cc523e..3ebb0735c3e 100644 --- a/testsuites/geosparql/pom.xml +++ b/testsuites/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-geosparql-testsuite RDF4J: GeoSPARQL compliance test suite diff --git a/testsuites/lucene/pom.xml b/testsuites/lucene/pom.xml index 18360ad4420..66488c8e819 100644 --- a/testsuites/lucene/pom.xml +++ b/testsuites/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-lucene-testsuite RDF4J: Lucene Sail Tests diff --git a/testsuites/model/pom.xml b/testsuites/model/pom.xml index 70234335ef5..cc4dc7ab767 100644 --- a/testsuites/model/pom.xml +++ b/testsuites/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-testsuite RDF4J: Model API testsuite diff --git a/testsuites/pom.xml b/testsuites/pom.xml index 30f9014d584..42088f21a4d 100644 --- a/testsuites/pom.xml +++ b/testsuites/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-testsuites pom diff --git a/testsuites/queryresultio/pom.xml b/testsuites/queryresultio/pom.xml index d6c456e40c3..feaf586b953 100644 --- a/testsuites/queryresultio/pom.xml +++ b/testsuites/queryresultio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-testsuite RDF4J: QueryResultIO testsuite diff --git a/testsuites/repository/pom.xml b/testsuites/repository/pom.xml index bb811406609..d58184a0a20 100644 --- a/testsuites/repository/pom.xml +++ b/testsuites/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-testsuite RDF4J: Repository API testsuite diff --git a/testsuites/rio/pom.xml b/testsuites/rio/pom.xml index ac9dcf9dc9e..8bab2f967d5 100644 --- a/testsuites/rio/pom.xml +++ b/testsuites/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-testsuite RDF4J: Rio compliance test suite diff --git a/testsuites/sail/pom.xml b/testsuites/sail/pom.xml index 85da81f401a..4b9b7d6c2d1 100644 --- a/testsuites/sail/pom.xml +++ b/testsuites/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-testsuite RDF4J: Sail API testsuite diff --git a/testsuites/sparql/pom.xml b/testsuites/sparql/pom.xml index 4f133b5d562..6d81c721943 100644 --- a/testsuites/sparql/pom.xml +++ b/testsuites/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparql-testsuite RDF4J: SPARQL compliance test suite diff --git a/tools/config/pom.xml b/tools/config/pom.xml index e25367db25b..c6e819ebcc0 100644 --- a/tools/config/pom.xml +++ b/tools/config/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-config RDF4J: application configuration diff --git a/tools/console/pom.xml b/tools/console/pom.xml index 6a88f40b2e4..a342f03ad96 100644 --- a/tools/console/pom.xml +++ b/tools/console/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-console RDF4J: Console diff --git a/tools/federation/pom.xml b/tools/federation/pom.xml index f050926836b..5430f89d375 100644 --- a/tools/federation/pom.xml +++ b/tools/federation/pom.xml @@ -8,7 +8,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/tools/pom.xml b/tools/pom.xml index 2f03e003064..08778066c03 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-tools pom diff --git a/tools/runtime-osgi/pom.xml b/tools/runtime-osgi/pom.xml index 0209c7f0568..642959844af 100644 --- a/tools/runtime-osgi/pom.xml +++ b/tools/runtime-osgi/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-runtime-osgi bundle diff --git a/tools/runtime/pom.xml b/tools/runtime/pom.xml index 02adbf3cdbb..ccb07aa2992 100644 --- a/tools/runtime/pom.xml +++ b/tools/runtime/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-runtime RDF4J: Runtime diff --git a/tools/server-spring/pom.xml b/tools/server-spring/pom.xml index 7e4c9364af6..a3cba434f29 100644 --- a/tools/server-spring/pom.xml +++ b/tools/server-spring/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-server-spring RDF4J: HTTP server - core diff --git a/tools/server/pom.xml b/tools/server/pom.xml index 21599a6c105..5e6fc517570 100644 --- a/tools/server/pom.xml +++ b/tools/server/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-server war diff --git a/tools/workbench/pom.xml b/tools/workbench/pom.xml index 5117672f2db..cabca3a9a48 100644 --- a/tools/workbench/pom.xml +++ b/tools/workbench/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.6-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-workbench war From ebb4939f2a59b4d849c0097826d6a55b99cb73e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Wed, 24 Sep 2025 21:44:35 +0200 Subject: [PATCH 19/46] set correct version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Håvard Ottestad --- assembly-descriptors/pom.xml | 2 +- assembly/pom.xml | 2 +- bom/pom.xml | 2 +- compliance/elasticsearch/pom.xml | 2 +- compliance/geosparql/pom.xml | 2 +- compliance/lucene/pom.xml | 2 +- compliance/model/pom.xml | 2 +- compliance/pom.xml | 2 +- compliance/repository/pom.xml | 2 +- compliance/rio/pom.xml | 2 +- compliance/solr/pom.xml | 2 +- compliance/sparql/pom.xml | 2 +- core/client/pom.xml | 2 +- core/collection-factory/api/pom.xml | 2 +- core/collection-factory/mapdb/pom.xml | 2 +- core/collection-factory/mapdb3/pom.xml | 2 +- core/collection-factory/pom.xml | 2 +- core/common/annotation/pom.xml | 2 +- core/common/exception/pom.xml | 2 +- core/common/io/pom.xml | 2 +- core/common/iterator/pom.xml | 2 +- core/common/order/pom.xml | 2 +- core/common/pom.xml | 2 +- core/common/text/pom.xml | 2 +- core/common/transaction/pom.xml | 2 +- core/common/xml/pom.xml | 2 +- core/http/client/pom.xml | 2 +- core/http/pom.xml | 2 +- core/http/protocol/pom.xml | 2 +- core/model-api/pom.xml | 2 +- core/model-vocabulary/pom.xml | 2 +- core/model/pom.xml | 2 +- core/pom.xml | 2 +- core/query/pom.xml | 2 +- core/queryalgebra/evaluation/pom.xml | 2 +- core/queryalgebra/geosparql/pom.xml | 2 +- core/queryalgebra/model/pom.xml | 2 +- core/queryalgebra/pom.xml | 2 +- core/queryparser/api/pom.xml | 2 +- core/queryparser/pom.xml | 2 +- core/queryparser/sparql/pom.xml | 2 +- core/queryrender/pom.xml | 2 +- core/queryresultio/api/pom.xml | 2 +- core/queryresultio/binary/pom.xml | 2 +- core/queryresultio/pom.xml | 2 +- core/queryresultio/sparqljson/pom.xml | 2 +- core/queryresultio/sparqlxml/pom.xml | 2 +- core/queryresultio/text/pom.xml | 2 +- core/repository/api/pom.xml | 2 +- core/repository/contextaware/pom.xml | 2 +- core/repository/dataset/pom.xml | 2 +- core/repository/event/pom.xml | 2 +- core/repository/http/pom.xml | 2 +- core/repository/manager/pom.xml | 2 +- core/repository/pom.xml | 2 +- core/repository/sail/pom.xml | 2 +- core/repository/sparql/pom.xml | 2 +- core/rio/api/pom.xml | 2 +- core/rio/binary/pom.xml | 2 +- core/rio/datatypes/pom.xml | 2 +- core/rio/hdt/pom.xml | 2 +- core/rio/jsonld-legacy/pom.xml | 2 +- core/rio/jsonld/pom.xml | 2 +- core/rio/languages/pom.xml | 2 +- core/rio/n3/pom.xml | 2 +- core/rio/nquads/pom.xml | 2 +- core/rio/ntriples/pom.xml | 2 +- core/rio/pom.xml | 2 +- core/rio/rdfjson/pom.xml | 2 +- core/rio/rdfxml/pom.xml | 2 +- core/rio/trig/pom.xml | 2 +- core/rio/trix/pom.xml | 2 +- core/rio/turtle/pom.xml | 2 +- core/sail/api/pom.xml | 2 +- core/sail/base/pom.xml | 2 +- core/sail/elasticsearch-store/pom.xml | 2 +- core/sail/elasticsearch/pom.xml | 2 +- core/sail/extensible-store/pom.xml | 2 +- core/sail/inferencer/pom.xml | 2 +- core/sail/lmdb/pom.xml | 2 +- core/sail/lucene-api/pom.xml | 2 +- core/sail/lucene/pom.xml | 2 +- core/sail/memory/pom.xml | 2 +- core/sail/model/pom.xml | 2 +- core/sail/nativerdf/pom.xml | 2 +- core/sail/pom.xml | 2 +- core/sail/shacl/pom.xml | 2 +- core/sail/solr/pom.xml | 2 +- core/sparqlbuilder/pom.xml | 2 +- core/spin/pom.xml | 2 +- core/storage/pom.xml | 2 +- examples/pom.xml | 2 +- pom.xml | 2 +- spring-components/pom.xml | 2 +- spring-components/rdf4j-spring-demo/pom.xml | 2 +- spring-components/rdf4j-spring/pom.xml | 2 +- spring-components/spring-boot-sparql-web/pom.xml | 2 +- testsuites/benchmark/pom.xml | 2 +- testsuites/geosparql/pom.xml | 2 +- testsuites/lucene/pom.xml | 2 +- testsuites/model/pom.xml | 2 +- testsuites/pom.xml | 2 +- testsuites/queryresultio/pom.xml | 2 +- testsuites/repository/pom.xml | 2 +- testsuites/rio/pom.xml | 2 +- testsuites/sail/pom.xml | 2 +- testsuites/sparql/pom.xml | 2 +- tools/config/pom.xml | 2 +- tools/console/pom.xml | 2 +- tools/federation/pom.xml | 2 +- tools/pom.xml | 2 +- tools/runtime-osgi/pom.xml | 2 +- tools/runtime/pom.xml | 2 +- tools/server-spring/pom.xml | 2 +- tools/server/pom.xml | 2 +- tools/workbench/pom.xml | 2 +- 116 files changed, 116 insertions(+), 116 deletions(-) diff --git a/assembly-descriptors/pom.xml b/assembly-descriptors/pom.xml index d7a5ac3d08e..25b0b093889 100644 --- a/assembly-descriptors/pom.xml +++ b/assembly-descriptors/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-assembly-descriptors RDF4J: Assembly Descriptors diff --git a/assembly/pom.xml b/assembly/pom.xml index 11e32ba07b0..a88d567f0e5 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-assembly pom diff --git a/bom/pom.xml b/bom/pom.xml index 6c556f153ac..8056082ccc1 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-bom pom diff --git a/compliance/elasticsearch/pom.xml b/compliance/elasticsearch/pom.xml index 86f1d262125..794dacb7bd7 100644 --- a/compliance/elasticsearch/pom.xml +++ b/compliance/elasticsearch/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-elasticsearch-compliance RDF4J: Elasticsearch Sail Tests diff --git a/compliance/geosparql/pom.xml b/compliance/geosparql/pom.xml index 9062c9a3b78..41e8785595b 100644 --- a/compliance/geosparql/pom.xml +++ b/compliance/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-geosparql-compliance RDF4J: GeoSPARQL compliance tests diff --git a/compliance/lucene/pom.xml b/compliance/lucene/pom.xml index dd3c41c533d..dcf84054c6d 100644 --- a/compliance/lucene/pom.xml +++ b/compliance/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-lucene-compliance RDF4J: Lucene Sail Tests diff --git a/compliance/model/pom.xml b/compliance/model/pom.xml index 2e661418bd1..3d3ac105fa9 100644 --- a/compliance/model/pom.xml +++ b/compliance/model/pom.xml @@ -3,7 +3,7 @@ rdf4j-compliance org.eclipse.rdf4j - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT 4.0.0 rdf4j-model-compliance diff --git a/compliance/pom.xml b/compliance/pom.xml index 848a28b27c0..7a3f7a3e7ff 100644 --- a/compliance/pom.xml +++ b/compliance/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-compliance pom diff --git a/compliance/repository/pom.xml b/compliance/repository/pom.xml index 7c83cfc3506..069ade53eec 100644 --- a/compliance/repository/pom.xml +++ b/compliance/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-compliance war diff --git a/compliance/rio/pom.xml b/compliance/rio/pom.xml index e1ad5da306f..850062bf2f4 100644 --- a/compliance/rio/pom.xml +++ b/compliance/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-compliance RDF4J: Rio compliance tests diff --git a/compliance/solr/pom.xml b/compliance/solr/pom.xml index 9133a827442..454f5295ca6 100644 --- a/compliance/solr/pom.xml +++ b/compliance/solr/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-solr-compliance RDF4J: Solr Sail Tests diff --git a/compliance/sparql/pom.xml b/compliance/sparql/pom.xml index 0d6cb796c46..3340661c18d 100644 --- a/compliance/sparql/pom.xml +++ b/compliance/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-compliance - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparql-compliance war diff --git a/core/client/pom.xml b/core/client/pom.xml index e896238dc4b..845360a26cc 100644 --- a/core/client/pom.xml +++ b/core/client/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-client RDF4J: Client Libraries diff --git a/core/collection-factory/api/pom.xml b/core/collection-factory/api/pom.xml index 515cad668a7..5ba6df646b7 100644 --- a/core/collection-factory/api/pom.xml +++ b/core/collection-factory/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-api RDF4J: Collection Factory - API diff --git a/core/collection-factory/mapdb/pom.xml b/core/collection-factory/mapdb/pom.xml index 647c7ab875a..c8e843b984e 100644 --- a/core/collection-factory/mapdb/pom.xml +++ b/core/collection-factory/mapdb/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-mapdb RDF4J: Collection Factory - Map DB backed diff --git a/core/collection-factory/mapdb3/pom.xml b/core/collection-factory/mapdb3/pom.xml index af688ea3a65..a00d4c8e811 100644 --- a/core/collection-factory/mapdb3/pom.xml +++ b/core/collection-factory/mapdb3/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-collection-factory - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory-mapdb3 RDF4J: Collection Factory - Map DB v3 backed diff --git a/core/collection-factory/pom.xml b/core/collection-factory/pom.xml index ba294882025..bb1df7bb226 100644 --- a/core/collection-factory/pom.xml +++ b/core/collection-factory/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-collection-factory pom diff --git a/core/common/annotation/pom.xml b/core/common/annotation/pom.xml index bcfa5aea40a..f5f53a65e21 100644 --- a/core/common/annotation/pom.xml +++ b/core/common/annotation/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-annotation RDF4J: common annotation diff --git a/core/common/exception/pom.xml b/core/common/exception/pom.xml index 3cd7a477bda..bbd533f1adf 100644 --- a/core/common/exception/pom.xml +++ b/core/common/exception/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-exception RDF4J: common exception diff --git a/core/common/io/pom.xml b/core/common/io/pom.xml index 50b8dbc3c8b..f99f7f2af4e 100644 --- a/core/common/io/pom.xml +++ b/core/common/io/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-io RDF4J: common IO diff --git a/core/common/iterator/pom.xml b/core/common/iterator/pom.xml index e7d415785f1..c8b62012574 100644 --- a/core/common/iterator/pom.xml +++ b/core/common/iterator/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-iterator RDF4J: common iterators diff --git a/core/common/order/pom.xml b/core/common/order/pom.xml index 9fdbb4dac05..5228081fc0e 100644 --- a/core/common/order/pom.xml +++ b/core/common/order/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-order RDF4J: common order diff --git a/core/common/pom.xml b/core/common/pom.xml index 7c2d8995267..fc821867500 100644 --- a/core/common/pom.xml +++ b/core/common/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common pom diff --git a/core/common/text/pom.xml b/core/common/text/pom.xml index 6787dc2930f..f26391a3d11 100644 --- a/core/common/text/pom.xml +++ b/core/common/text/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-text RDF4J: common text diff --git a/core/common/transaction/pom.xml b/core/common/transaction/pom.xml index 997f5e44f96..2a327eba54c 100644 --- a/core/common/transaction/pom.xml +++ b/core/common/transaction/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-transaction RDF4J: common transaction diff --git a/core/common/xml/pom.xml b/core/common/xml/pom.xml index 0b25c265142..76184d6e127 100644 --- a/core/common/xml/pom.xml +++ b/core/common/xml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-common - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-common-xml RDF4J: common XML diff --git a/core/http/client/pom.xml b/core/http/client/pom.xml index 14eb63b82d9..8e26e6c8d3f 100644 --- a/core/http/client/pom.xml +++ b/core/http/client/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-http - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-client RDF4J: HTTP client diff --git a/core/http/pom.xml b/core/http/pom.xml index 2994077b6f1..ee3e0261ad0 100644 --- a/core/http/pom.xml +++ b/core/http/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http pom diff --git a/core/http/protocol/pom.xml b/core/http/protocol/pom.xml index c42876debca..a5d66a693e0 100644 --- a/core/http/protocol/pom.xml +++ b/core/http/protocol/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-http - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-protocol RDF4J: HTTP protocol diff --git a/core/model-api/pom.xml b/core/model-api/pom.xml index bb84bc98273..cf07e38798c 100644 --- a/core/model-api/pom.xml +++ b/core/model-api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-api RDF4J: Model API diff --git a/core/model-vocabulary/pom.xml b/core/model-vocabulary/pom.xml index efd41a48f6c..bd27791c1d8 100644 --- a/core/model-vocabulary/pom.xml +++ b/core/model-vocabulary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-vocabulary RDF4J: RDF Vocabularies diff --git a/core/model/pom.xml b/core/model/pom.xml index 74cca9bb0c3..17a4deb868b 100644 --- a/core/model/pom.xml +++ b/core/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model RDF4J: Model diff --git a/core/pom.xml b/core/pom.xml index 262143c849c..ed94faedcde 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-core pom diff --git a/core/query/pom.xml b/core/query/pom.xml index a82e0d8f8b5..6600bd51a4e 100644 --- a/core/query/pom.xml +++ b/core/query/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-query RDF4J: Query diff --git a/core/queryalgebra/evaluation/pom.xml b/core/queryalgebra/evaluation/pom.xml index 535581f475f..a29c5f1ab29 100644 --- a/core/queryalgebra/evaluation/pom.xml +++ b/core/queryalgebra/evaluation/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-evaluation RDF4J: Query algebra - evaluation diff --git a/core/queryalgebra/geosparql/pom.xml b/core/queryalgebra/geosparql/pom.xml index 57b5553cce1..10fcbc837ce 100644 --- a/core/queryalgebra/geosparql/pom.xml +++ b/core/queryalgebra/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-geosparql RDF4J: Query algebra - GeoSPARQL diff --git a/core/queryalgebra/model/pom.xml b/core/queryalgebra/model/pom.xml index 70cc2d0b945..c561441a066 100644 --- a/core/queryalgebra/model/pom.xml +++ b/core/queryalgebra/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryalgebra - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra-model RDF4J: Query algebra - model diff --git a/core/queryalgebra/pom.xml b/core/queryalgebra/pom.xml index a873d735fd7..fb04d8339cd 100644 --- a/core/queryalgebra/pom.xml +++ b/core/queryalgebra/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryalgebra pom diff --git a/core/queryparser/api/pom.xml b/core/queryparser/api/pom.xml index e9038834d3f..6450c375f42 100644 --- a/core/queryparser/api/pom.xml +++ b/core/queryparser/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryparser - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser-api RDF4J: Query parser - API diff --git a/core/queryparser/pom.xml b/core/queryparser/pom.xml index b7371348ece..0dfcb8573d3 100644 --- a/core/queryparser/pom.xml +++ b/core/queryparser/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser pom diff --git a/core/queryparser/sparql/pom.xml b/core/queryparser/sparql/pom.xml index 7adf023b40e..d21ecf2b1b2 100644 --- a/core/queryparser/sparql/pom.xml +++ b/core/queryparser/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryparser - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryparser-sparql RDF4J: Query parser - SPARQL diff --git a/core/queryrender/pom.xml b/core/queryrender/pom.xml index a765bfdeb90..8db12169a80 100644 --- a/core/queryrender/pom.xml +++ b/core/queryrender/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryrender RDF4J: Query Rendering diff --git a/core/queryresultio/api/pom.xml b/core/queryresultio/api/pom.xml index cd997e7eea0..273fe3b9801 100644 --- a/core/queryresultio/api/pom.xml +++ b/core/queryresultio/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-api RDF4J: Query result IO - API diff --git a/core/queryresultio/binary/pom.xml b/core/queryresultio/binary/pom.xml index 05777c3e2da..34df6c0b64f 100644 --- a/core/queryresultio/binary/pom.xml +++ b/core/queryresultio/binary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-binary RDF4J: Query result IO - binary diff --git a/core/queryresultio/pom.xml b/core/queryresultio/pom.xml index 452df71f2e0..c1ba95a8c53 100644 --- a/core/queryresultio/pom.xml +++ b/core/queryresultio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio pom diff --git a/core/queryresultio/sparqljson/pom.xml b/core/queryresultio/sparqljson/pom.xml index d2ee8b37011..c5f589b6457 100644 --- a/core/queryresultio/sparqljson/pom.xml +++ b/core/queryresultio/sparqljson/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-sparqljson RDF4J: Query result IO - SPARQL/JSON diff --git a/core/queryresultio/sparqlxml/pom.xml b/core/queryresultio/sparqlxml/pom.xml index 3b033592eb8..8d7bee2745b 100644 --- a/core/queryresultio/sparqlxml/pom.xml +++ b/core/queryresultio/sparqlxml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-sparqlxml RDF4J: Query result IO - SPARQL/XML diff --git a/core/queryresultio/text/pom.xml b/core/queryresultio/text/pom.xml index 5415c638425..e5f67e0b32e 100644 --- a/core/queryresultio/text/pom.xml +++ b/core/queryresultio/text/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-queryresultio - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-text RDF4J: Query result IO - plain text booleans diff --git a/core/repository/api/pom.xml b/core/repository/api/pom.xml index 5263b5485fc..48fdeecfad7 100644 --- a/core/repository/api/pom.xml +++ b/core/repository/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-api RDF4J: Repository - API diff --git a/core/repository/contextaware/pom.xml b/core/repository/contextaware/pom.xml index d88f99b4ca9..39bcbf0668c 100644 --- a/core/repository/contextaware/pom.xml +++ b/core/repository/contextaware/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-contextaware RDF4J: Repository - context aware (wrapper) diff --git a/core/repository/dataset/pom.xml b/core/repository/dataset/pom.xml index 03805296331..c843dccda42 100644 --- a/core/repository/dataset/pom.xml +++ b/core/repository/dataset/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-dataset RDF4J: DatasetRepository (wrapper) diff --git a/core/repository/event/pom.xml b/core/repository/event/pom.xml index b33b51bca3c..3a3109cf967 100644 --- a/core/repository/event/pom.xml +++ b/core/repository/event/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-event RDF4J: Repository - event (wrapper) diff --git a/core/repository/http/pom.xml b/core/repository/http/pom.xml index 8098449942f..4fc07dc06e9 100644 --- a/core/repository/http/pom.xml +++ b/core/repository/http/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-http RDF4J: HTTPRepository diff --git a/core/repository/manager/pom.xml b/core/repository/manager/pom.xml index 84acb9a8545..bad53d7101e 100644 --- a/core/repository/manager/pom.xml +++ b/core/repository/manager/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-manager RDF4J: Repository manager diff --git a/core/repository/pom.xml b/core/repository/pom.xml index 203db3590d5..873757a9580 100644 --- a/core/repository/pom.xml +++ b/core/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository pom diff --git a/core/repository/sail/pom.xml b/core/repository/sail/pom.xml index df84b7c318e..fb8fc483b2b 100644 --- a/core/repository/sail/pom.xml +++ b/core/repository/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-sail RDF4J: SailRepository diff --git a/core/repository/sparql/pom.xml b/core/repository/sparql/pom.xml index 6ec77048e76..66d5ee263ce 100644 --- a/core/repository/sparql/pom.xml +++ b/core/repository/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-repository - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-sparql RDF4J: SPARQL Repository diff --git a/core/rio/api/pom.xml b/core/rio/api/pom.xml index 699962c59e8..3733b074423 100644 --- a/core/rio/api/pom.xml +++ b/core/rio/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-api RDF4J: Rio - API diff --git a/core/rio/binary/pom.xml b/core/rio/binary/pom.xml index 2fbeaf163b0..4ff5469eddf 100644 --- a/core/rio/binary/pom.xml +++ b/core/rio/binary/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-binary RDF4J: Rio - Binary diff --git a/core/rio/datatypes/pom.xml b/core/rio/datatypes/pom.xml index 75453263294..b9d9dcadcfb 100644 --- a/core/rio/datatypes/pom.xml +++ b/core/rio/datatypes/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-datatypes RDF4J: Rio - Datatypes diff --git a/core/rio/hdt/pom.xml b/core/rio/hdt/pom.xml index ecc1fe4e84d..f3647663a65 100644 --- a/core/rio/hdt/pom.xml +++ b/core/rio/hdt/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-hdt jar diff --git a/core/rio/jsonld-legacy/pom.xml b/core/rio/jsonld-legacy/pom.xml index e4e24776a49..7509ef4b834 100644 --- a/core/rio/jsonld-legacy/pom.xml +++ b/core/rio/jsonld-legacy/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-jsonld-legacy RDF4J: Rio - JSON-LD 1.0 (legacy) diff --git a/core/rio/jsonld/pom.xml b/core/rio/jsonld/pom.xml index fc9a36629e6..998013a10ec 100644 --- a/core/rio/jsonld/pom.xml +++ b/core/rio/jsonld/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-jsonld RDF4J: Rio - JSON-LD diff --git a/core/rio/languages/pom.xml b/core/rio/languages/pom.xml index 86c0a2365f4..b35931702bb 100644 --- a/core/rio/languages/pom.xml +++ b/core/rio/languages/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-languages RDF4J: Rio - Languages diff --git a/core/rio/n3/pom.xml b/core/rio/n3/pom.xml index d2a38c44896..54ae5fe573c 100644 --- a/core/rio/n3/pom.xml +++ b/core/rio/n3/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-n3 RDF4J: Rio - N3 (writer-only) diff --git a/core/rio/nquads/pom.xml b/core/rio/nquads/pom.xml index 5082d6bac1b..044216524fb 100644 --- a/core/rio/nquads/pom.xml +++ b/core/rio/nquads/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-nquads RDF4J: Rio - N-Quads diff --git a/core/rio/ntriples/pom.xml b/core/rio/ntriples/pom.xml index fddd42e5463..37553504ed5 100644 --- a/core/rio/ntriples/pom.xml +++ b/core/rio/ntriples/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-ntriples RDF4J: Rio - N-Triples diff --git a/core/rio/pom.xml b/core/rio/pom.xml index c2d0ce909cf..77fc1cee292 100644 --- a/core/rio/pom.xml +++ b/core/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio pom diff --git a/core/rio/rdfjson/pom.xml b/core/rio/rdfjson/pom.xml index c8800e3d20f..55a3e3ad374 100644 --- a/core/rio/rdfjson/pom.xml +++ b/core/rio/rdfjson/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-rdfjson RDF4J: Rio - RDF/JSON diff --git a/core/rio/rdfxml/pom.xml b/core/rio/rdfxml/pom.xml index d8ce5a3ca9c..08a1e0f6eb3 100644 --- a/core/rio/rdfxml/pom.xml +++ b/core/rio/rdfxml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-rdfxml RDF4J: Rio - RDF/XML diff --git a/core/rio/trig/pom.xml b/core/rio/trig/pom.xml index 529637be92c..2736b12a916 100644 --- a/core/rio/trig/pom.xml +++ b/core/rio/trig/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-trig RDF4J: Rio - TriG diff --git a/core/rio/trix/pom.xml b/core/rio/trix/pom.xml index 6de5577c41a..a8b99df7634 100644 --- a/core/rio/trix/pom.xml +++ b/core/rio/trix/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-trix RDF4J: Rio - TriX diff --git a/core/rio/turtle/pom.xml b/core/rio/turtle/pom.xml index 39fe9c9db21..087f4221601 100644 --- a/core/rio/turtle/pom.xml +++ b/core/rio/turtle/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-rio - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-turtle RDF4J: Rio - Turtle diff --git a/core/sail/api/pom.xml b/core/sail/api/pom.xml index be83b7d755e..94ba13deba0 100644 --- a/core/sail/api/pom.xml +++ b/core/sail/api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-api RDF4J: Sail API diff --git a/core/sail/base/pom.xml b/core/sail/base/pom.xml index 13a81530f9c..37f440d24a5 100644 --- a/core/sail/base/pom.xml +++ b/core/sail/base/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-base RDF4J: Sail base implementations diff --git a/core/sail/elasticsearch-store/pom.xml b/core/sail/elasticsearch-store/pom.xml index 16cfec25798..8542f4a1aaa 100644 --- a/core/sail/elasticsearch-store/pom.xml +++ b/core/sail/elasticsearch-store/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-elasticsearch-store RDF4J: Elasticsearch Store diff --git a/core/sail/elasticsearch/pom.xml b/core/sail/elasticsearch/pom.xml index efaaeef072e..022319c4697 100644 --- a/core/sail/elasticsearch/pom.xml +++ b/core/sail/elasticsearch/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-elasticsearch RDF4J: Elastic Search Sail Index diff --git a/core/sail/extensible-store/pom.xml b/core/sail/extensible-store/pom.xml index ba19e28d29a..e8d6a1af491 100644 --- a/core/sail/extensible-store/pom.xml +++ b/core/sail/extensible-store/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-extensible-store RDF4J: Extensible Store diff --git a/core/sail/inferencer/pom.xml b/core/sail/inferencer/pom.xml index 7040c569ace..45babf4a3e2 100644 --- a/core/sail/inferencer/pom.xml +++ b/core/sail/inferencer/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-inferencer RDF4J: Inferencer Sails diff --git a/core/sail/lmdb/pom.xml b/core/sail/lmdb/pom.xml index e529a1744ae..787fb19c4d4 100644 --- a/core/sail/lmdb/pom.xml +++ b/core/sail/lmdb/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lmdb RDF4J: LmdbStore diff --git a/core/sail/lucene-api/pom.xml b/core/sail/lucene-api/pom.xml index 14cd1b73a1c..a52d440fc00 100644 --- a/core/sail/lucene-api/pom.xml +++ b/core/sail/lucene-api/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lucene-api RDF4J: Lucene Sail API diff --git a/core/sail/lucene/pom.xml b/core/sail/lucene/pom.xml index 1b64636e76f..530c6ac3c5d 100644 --- a/core/sail/lucene/pom.xml +++ b/core/sail/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-lucene RDF4J: Lucene Sail Index diff --git a/core/sail/memory/pom.xml b/core/sail/memory/pom.xml index b3828b16ea9..ae81a8c5531 100644 --- a/core/sail/memory/pom.xml +++ b/core/sail/memory/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-memory RDF4J: MemoryStore diff --git a/core/sail/model/pom.xml b/core/sail/model/pom.xml index 7ef71dbdd90..531c473d10a 100644 --- a/core/sail/model/pom.xml +++ b/core/sail/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-model RDF4J: Sail Model diff --git a/core/sail/nativerdf/pom.xml b/core/sail/nativerdf/pom.xml index 2b16a33bc33..17dbcc1c962 100644 --- a/core/sail/nativerdf/pom.xml +++ b/core/sail/nativerdf/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-nativerdf RDF4J: NativeStore diff --git a/core/sail/pom.xml b/core/sail/pom.xml index c5efd5a1231..cbce9f38bc7 100644 --- a/core/sail/pom.xml +++ b/core/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail pom diff --git a/core/sail/shacl/pom.xml b/core/sail/shacl/pom.xml index cea9f692e45..9cae94fe046 100644 --- a/core/sail/shacl/pom.xml +++ b/core/sail/shacl/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-shacl RDF4J: SHACL diff --git a/core/sail/solr/pom.xml b/core/sail/solr/pom.xml index ff6f5db3d4c..c79bfa393e7 100644 --- a/core/sail/solr/pom.xml +++ b/core/sail/solr/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-sail - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-solr RDF4J: Solr Sail Index diff --git a/core/sparqlbuilder/pom.xml b/core/sparqlbuilder/pom.xml index af6e8779b7b..2e589196e67 100644 --- a/core/sparqlbuilder/pom.xml +++ b/core/sparqlbuilder/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparqlbuilder RDF4J: SparqlBuilder diff --git a/core/spin/pom.xml b/core/spin/pom.xml index 09cdea71032..b80e9d40e04 100644 --- a/core/spin/pom.xml +++ b/core/spin/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spin RDF4J: SPIN diff --git a/core/storage/pom.xml b/core/storage/pom.xml index 54e506aa367..6775e4bf837 100644 --- a/core/storage/pom.xml +++ b/core/storage/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-core - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-storage RDF4J: Storage Libraries diff --git a/examples/pom.xml b/examples/pom.xml index ac6224a81f3..5610e589454 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -7,7 +7,7 @@ org.eclipse.rdf4j rdf4j - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/pom.xml b/pom.xml index d032d63fd1c..ce901e113c2 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.eclipse.rdf4j rdf4j - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT pom Eclipse RDF4J An extensible Java framework for RDF and SPARQL diff --git a/spring-components/pom.xml b/spring-components/pom.xml index ab3dd692278..571eebb5bce 100644 --- a/spring-components/pom.xml +++ b/spring-components/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT pom diff --git a/spring-components/rdf4j-spring-demo/pom.xml b/spring-components/rdf4j-spring-demo/pom.xml index 5eb03e96408..e3376a0dd51 100644 --- a/spring-components/rdf4j-spring-demo/pom.xml +++ b/spring-components/rdf4j-spring-demo/pom.xml @@ -7,7 +7,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/spring-components/rdf4j-spring/pom.xml b/spring-components/rdf4j-spring/pom.xml index 3aa7e9b9530..b3616e04951 100644 --- a/spring-components/rdf4j-spring/pom.xml +++ b/spring-components/rdf4j-spring/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spring RDF4J: Spring diff --git a/spring-components/spring-boot-sparql-web/pom.xml b/spring-components/spring-boot-sparql-web/pom.xml index fe1edfafdb6..222e9020799 100644 --- a/spring-components/spring-boot-sparql-web/pom.xml +++ b/spring-components/spring-boot-sparql-web/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-spring-components - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-spring-boot-sparql-web RDF4J: Spring boot component for a HTTP sparql server diff --git a/testsuites/benchmark/pom.xml b/testsuites/benchmark/pom.xml index c748380063f..96c07db5f28 100644 --- a/testsuites/benchmark/pom.xml +++ b/testsuites/benchmark/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-benchmark RDF4J: benchmarks diff --git a/testsuites/geosparql/pom.xml b/testsuites/geosparql/pom.xml index 6c030ec52ca..3ebb0735c3e 100644 --- a/testsuites/geosparql/pom.xml +++ b/testsuites/geosparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-geosparql-testsuite RDF4J: GeoSPARQL compliance test suite diff --git a/testsuites/lucene/pom.xml b/testsuites/lucene/pom.xml index f29e463e89f..66488c8e819 100644 --- a/testsuites/lucene/pom.xml +++ b/testsuites/lucene/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-lucene-testsuite RDF4J: Lucene Sail Tests diff --git a/testsuites/model/pom.xml b/testsuites/model/pom.xml index 39ea1c378b0..cc4dc7ab767 100644 --- a/testsuites/model/pom.xml +++ b/testsuites/model/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-model-testsuite RDF4J: Model API testsuite diff --git a/testsuites/pom.xml b/testsuites/pom.xml index b6762c4bdf1..42088f21a4d 100644 --- a/testsuites/pom.xml +++ b/testsuites/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-testsuites pom diff --git a/testsuites/queryresultio/pom.xml b/testsuites/queryresultio/pom.xml index bb9e5ee6f5f..feaf586b953 100644 --- a/testsuites/queryresultio/pom.xml +++ b/testsuites/queryresultio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-queryresultio-testsuite RDF4J: QueryResultIO testsuite diff --git a/testsuites/repository/pom.xml b/testsuites/repository/pom.xml index e748a245120..d58184a0a20 100644 --- a/testsuites/repository/pom.xml +++ b/testsuites/repository/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-repository-testsuite RDF4J: Repository API testsuite diff --git a/testsuites/rio/pom.xml b/testsuites/rio/pom.xml index 46983abcf68..8bab2f967d5 100644 --- a/testsuites/rio/pom.xml +++ b/testsuites/rio/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-rio-testsuite RDF4J: Rio compliance test suite diff --git a/testsuites/sail/pom.xml b/testsuites/sail/pom.xml index 318585f8d14..4b9b7d6c2d1 100644 --- a/testsuites/sail/pom.xml +++ b/testsuites/sail/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sail-testsuite RDF4J: Sail API testsuite diff --git a/testsuites/sparql/pom.xml b/testsuites/sparql/pom.xml index 2ff05ed717c..6d81c721943 100644 --- a/testsuites/sparql/pom.xml +++ b/testsuites/sparql/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-testsuites - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-sparql-testsuite RDF4J: SPARQL compliance test suite diff --git a/tools/config/pom.xml b/tools/config/pom.xml index 592a7f968fd..c6e819ebcc0 100644 --- a/tools/config/pom.xml +++ b/tools/config/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-config RDF4J: application configuration diff --git a/tools/console/pom.xml b/tools/console/pom.xml index 615bee26c98..a342f03ad96 100644 --- a/tools/console/pom.xml +++ b/tools/console/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-console RDF4J: Console diff --git a/tools/federation/pom.xml b/tools/federation/pom.xml index e56caa6ab06..5430f89d375 100644 --- a/tools/federation/pom.xml +++ b/tools/federation/pom.xml @@ -8,7 +8,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT diff --git a/tools/pom.xml b/tools/pom.xml index f5f3a0a1d8f..08778066c03 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-tools pom diff --git a/tools/runtime-osgi/pom.xml b/tools/runtime-osgi/pom.xml index f20f59df869..642959844af 100644 --- a/tools/runtime-osgi/pom.xml +++ b/tools/runtime-osgi/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-runtime-osgi bundle diff --git a/tools/runtime/pom.xml b/tools/runtime/pom.xml index dc30259dc7e..ccb07aa2992 100644 --- a/tools/runtime/pom.xml +++ b/tools/runtime/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-runtime RDF4J: Runtime diff --git a/tools/server-spring/pom.xml b/tools/server-spring/pom.xml index 551895277b5..a3cba434f29 100644 --- a/tools/server-spring/pom.xml +++ b/tools/server-spring/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-server-spring RDF4J: HTTP server - core diff --git a/tools/server/pom.xml b/tools/server/pom.xml index 598004804b0..5e6fc517570 100644 --- a/tools/server/pom.xml +++ b/tools/server/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-server war diff --git a/tools/workbench/pom.xml b/tools/workbench/pom.xml index b0bb86a7c5d..cabca3a9a48 100644 --- a/tools/workbench/pom.xml +++ b/tools/workbench/pom.xml @@ -4,7 +4,7 @@ org.eclipse.rdf4j rdf4j-tools - 5.1.7-SNAPSHOT + 5.1.0-SNAPSHOT rdf4j-http-workbench war From 4d92bd6015f51ddf377887633c004cc25b30073d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Thu, 2 Oct 2025 10:46:29 +0200 Subject: [PATCH 20/46] updated AGENTS.md and added support for jacoco --- AGENTS.md | 643 +++++++++++++++++++++++++----------------------------- pom.xml | 33 ++- 2 files changed, 323 insertions(+), 353 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c0961300c37..4a00fb1b000 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,4 @@ -AGENTS.md +# AGENTS.md Welcome, AI Agent! Your persistence, curiosity, and craftsmanship make a difference. Take your time, work methodically, validate thoroughly, and iterate. This repository is large and tests can take time — that’s expected and supported. @@ -8,113 +8,166 @@ You need to read the entire AGENTS.md file and follow all instructions exactly. --- -## Read‑Me‑Now: Zero‑Exception Test‑First Rule (Stricter) +## Read‑Me‑Now: Proportional Test‑First Rule (Default) -**You may not touch production code until a smallest‑scope failing automated test exists inside this repo and you have captured its report snippet.** -No exceptions. A user‑provided stack trace or “obvious” contract violation is **not** a substitute for a failing test in the repository. +**Default:** Use **test‑first (TDD)** for any change that alters externally observable behavior. -**Auto‑stop:** If you realize you patched production before creating the failing test, **stop**, revert the patch, and resume from “Reproduce first”. +**Proportional exceptions:** You may **skip writing a new failing test** *only* when **all** Routine B gates (below) pass, or when using Routine C (Spike/Investigate) with **no production code changes**. -**Traceability trio (must appear in your handoff):** +**You may not touch production code for behavior‑changing work until a smallest‑scope failing automated test exists inside this repo and you have captured its report snippet.** A user‑provided stack trace or “obvious” contract violation is **not** a substitute for an in‑repo failing test. + +**Auto‑stop:** If you realize you patched production before creating/observing the failing test for behavior‑changing work, **stop**, revert the patch, and resume from “Reproduce first”. -1. **Preamble** (what you’re about to do + exact commands) -2. **Evidence** (surefire/failsafe snippet from this repo) +**Traceability trio (must appear in your handoff):** +1. **Descritpion** (what you’re about to do) +2. **Evidence** (Surefire/Failsafe snippet from this repo) 3. **Plan** (one and only one `in_progress` step) It is illegal to `-am` when running tests! It is illegal to `-q` when running tests! +> **Clarification:** For **strictly behavior‑neutral refactors** that are already **fully exercised by existing tests**, or for **bugfixes with an existing failing test**, you may use **Routine B — Change without new tests**. In that case you must capture **pre‑change passing evidence** at the smallest scope that hits the code you’re about to edit, prove **Hit Proof**, then show **post‑change passing evidence** from the **same selection**. +> **No exceptions for any behavior‑changing change** — for those, you must follow **Routine A — Full TDD**. + +--- + +## Three Routines: Choose Your Path + +**Routine A — Full TDD (Default)** +**Routine B — Change without new tests (Proportional, gated)** +**Routine C — Spike/Investigate (No production changes)** + +### Decision quickstart + +1. **Is new externally observable behavior required?** + → **Yes:** **Routine A (Full TDD)**. Add the smallest failing test first. + → **No:** continue. + +2. **Does a failing test already exist in this repo that pinpoints the issue?** + → **Yes:** **Routine B (Bugfix using existing failing test).** + → **No:** continue. + +3. **Is the edit strictly behavior‑neutral, local in scope, and clearly hit by existing tests?** + → **Yes:** **Routine B (Refactor/micro‑perf/documentation/build).** + → **No or unsure:** continue. + +4. **Is this purely an investigation/design spike with no production code changes?** + → **Yes:** **Routine C (Spike/Investigate).** + → **No or unsure:** **Routine A.** + +**When in doubt, choose Routine A (Full TDD).** Ambiguity is risk; tests are insurance. + +--- + +## Proportionality Model (Think before you test) + +Score the change on these lenses. If any are **High**, prefer **Routine A**. + +- **Behavioral surface:** affects outputs, serialization, parsing, APIs, error text, timing/order? +- **Blast radius:** number of modules/classes touched; public vs internal. +- **Reversibility:** quick revert vs migration/data change. +- **Observability:** can existing tests or assertions expose regressions? +- **Coverage depth:** do existing tests directly hit the edited code? +- **Concurrency / IO / Time:** any risk here is **High** by default. + --- ## Purpose & Contract -* **Bold goal:** deliver correct, minimal, well‑tested changes with clear handoff. No monkey‑patching or band‑aid fixes — always fix the underlying problem at its source. +* **Bold goal:** deliver correct, minimal, well‑tested changes with clear handoff. Fix root causes; avoid hacks. * **Bias to action:** when inputs are ambiguous, choose a reasonable path, state assumptions, and proceed. -* **Ask only when blocked or irreversible:** escalate only if truly blocked (permissions, missing deps, conflicting requirements) or if a choice is high‑risk/irreversible. +* **Ask only when blocked or irreversible:** permissions, missing deps, conflicting requirements, destructive repo‑wide changes. * **Definition of Done** - * Code formatted and imports sorted. * Compiles with a quick profile / targeted modules. * Relevant module tests pass; failures triaged or crisply explained. * Only necessary files changed; headers correct for new files. * Clear final summary: what changed, why, where, how verified, next steps. - * **Evidence present:** failing test output (pre‑fix) and passing output (post‑fix) are both shown. + * **Evidence present:** failing test output (pre‑fix) and passing output (post‑fix) are shown for Routine A; for Routine B show **pre/post green** from the **same selection** plus **Hit Proof**. ### No Monkey‑Patching or Band‑Aid Fixes (Non‑Negotiable) -This repository requires durable, root‑cause fixes. Superficial changes that mask symptoms, mute tests, or add ad‑hoc toggles are not acceptable. - -What this means in practice - -* Find and fix the root cause in the correct layer/module. -* Add or adjust targeted tests that fail before the fix and pass after. -* Keep changes minimal and surgical; do not widen APIs/configs to “make tests green”. -* Maintain consistency with existing style and architecture; prefer refactoring over hacks. - -Strictly avoid - -* Sleeping/timeouts to hide race conditions or flakiness. -* Broad catch‑and‑ignore or logging‑and‑continue of exceptions. -* Muting, deleting, or weakening assertions in tests to pass builds. -* Reflection or internal state manipulation to bypass proper interfaces. -* Feature flags/toggles that disable validation or logic instead of fixing it. -* Changing public APIs or configs without necessity and clear rationale tied to the root cause. +Durable, root‑cause fixes only. No muting tests, no broad catch‑and‑ignore, no widening APIs “to make green”. -Preferred approach (fast and rigorous) +**Strictly avoid** +* Sleeping/timeouts to hide flakiness. +* Swallowing exceptions or weakening assertions. +* Reflection/internal state manipulation to bypass interfaces. +* Feature flags that disable validation instead of fixing logic. +* Changing public APIs/configs without necessity tied to root cause. +**Preferred approach** * Reproduce the issue and isolate the smallest failing test (class → method). -* Trace to the true source; fix it in the right module. -* Add focused tests covering the behavior and any critical edge cases. -* Run tight, targeted verifies for the impacted module(s) and broaden scope only if needed. - -Review bar and enforcement - -* Treat this policy as a blocking requirement. Changes that resemble workarounds will be rejected. -* Your final handoff must demonstrate: failing test before the fix, explanation of the root cause, minimal fix at source, and passing targeted tests after. +* Trace to the true source; fix in the right module. +* Add focused tests for behavior/edge cases (Routine A) or prove coverage/neutrality (Routine B). +* Run tight, targeted verifies; broaden only if needed. --- -## Enforcement & Auto‑Fail Triggers +## Enforcement & Auto‑Fail Triggers -Your run is **invalid** and must be restarted from “Reproduce first” if any of the following occur: +Your run is **invalid** and must be restarted from “Reproduce first” if any occur: -* You modify production code before adding and running the smallest failing test in this repo. -* You proceed without pasting a surefire/failsafe report snippet from `target/*-reports/`. +* You modify production code before adding and running the smallest failing test in this repo **for behavior‑changing work**. +* You proceed without pasting a Surefire/Failsafe report snippet from `target/*-reports/`. * Your plan does not have **exactly one** `in_progress` step. * You run tests using `-am` or `-q`. * You treat a narrative failure description or external stack trace as equivalent to an in‑repo failing test. +* **Routine B specific:** you cannot demonstrate that existing tests exercise the edited code (**Hit Proof**), or you fail to capture both pre‑ and post‑change **matching** passing snippets from the same selection. +* **Routine C breach:** you change production code while in a spike. **Recovery procedure:** -Update the plan (`in_progress: create failing test`), post a preamble, create the failing test, run it, capture the report snippet, then resume. +Update the plan (`in_progress: create failing test`), post a description of your next step, create the failing test, run it, capture the report snippet, then resume. +For Routine B refactors: if any gate fails, **switch to Full TDD** and add the smallest failing test. --- -## Preamble & Evidence Protocol (Mandatory) - -Before any grouped actions (builds, tests, patches), post a **short preamble**: - -**Preamble template** +## Evidence Protocol (Mandatory) -``` -Preamble: Reproduce bug at smallest scope. -Module: -Commands: - mvn -o -pl -Dtest=Class#method verify | tail -500 -Expectation: test fails with the reported error. -``` - -After each grouped action, post an **Evidence block**: +After each grouped action, post an **Evidence block**, then continue working: **Evidence template** - ``` Evidence: Command: mvn -o -pl -Dtest=Class#method verify Report: /target/surefire-reports/.txt Snippet: - +\ ``` +**Routine B additions** +* **Pre‑green:** capture a pre‑change **passing** snippet from the **most specific** test selection that hits your code (ideally a class or method). +* **Hit Proof (choose one):** + * An existing test class/method that directly calls the edited class/method, plus a short `rg -n` snippet showing the call site; **or** + * A Surefire/Failsafe output line containing the edited class/method names; **or** + * A temporary assertion or deliberate, isolated failing check in a **scratch test** proving the path is executed (then remove). +* **Post‑green:** after the patch, re‑run the **same selection** and capture a passing snippet. + +--- + +### Initial Evidence Capture (Required) + +To avoid losing the first test evidence when later runs overwrite `target/*-reports/`, immediately persist the initial verify results to a top‑level `initial-evidence.txt` file. + +• On a fully green verify run: + +- Capture and store the last 200 lines of the Maven verify output. +- Example (module‑scoped): + - `mvn -o -pl verify | tee .initial-verify.log` + - `tail -200 .initial-verify.log > initial-evidence.txt` + +• On any failing verify run (unit or IT failures): + +- Concatenate the Surefire and/or Failsafe report text files into `initial-evidence.txt`. +- Example (repo‑root): + - `find . -type f \( -path "*/target/surefire-reports/*.txt" -o -path "*/target/failsafe-reports/*.txt" \) -print0 | xargs -0 cat > initial-evidence.txt` + +Notes + +- Keep `initial-evidence.txt` at the repository root alongside your final handoff. +- Do not rely on `target/*-reports/` for the final report; they may be overwritten by subsequent runs. +- Continue to include the standard Evidence block(s) in your messages as usual. + --- ## Living Plan Protocol (Sharper) @@ -122,17 +175,19 @@ Snippet: Maintain a **living plan** with checklist items (5–7 words each). Keep **exactly one** `in_progress`. **Plan format** - ``` + Plan -- [done] sanity build quick profile -- [in_progress] add smallest failing test -- [todo] minimal root-cause fix -- [todo] rerun focused then module tests -- [todo] format, verify, summary -``` -**Rule:** If you deviate, update the plan **first** (switch `in_progress`), then proceed. Do not let plan and actions drift out of sync. +* \[done] sanity build quick profile +* \[in\_progress] add smallest failing test +* \[todo] minimal root-cause fix +* \[todo] rerun focused then module tests +* \[todo] format, verify, summary + +```` + +**Rule:** If you deviate, update the plan **first**, then proceed. --- @@ -140,22 +195,18 @@ Plan * **JDK:** 11 (minimum). The project builds and runs on Java 11+. * **Maven default:** run **offline** using `-o` whenever possible. -* **Network:** only when needed to fetch missing deps/plugins; then rerun the exact command **without** `-o` once, and return to offline. -* **Large project:** some module test suites can take **5–10 minutes**. Be patient, but bias toward **targeted** runs to keep momentum. +* **Network:** only to fetch missing deps/plugins; then rerun once without `-o`, and return offline. +* **Large project:** some module test suites can take **5–10 minutes**. Prefer **targeted** runs. ### Maven `-am` usage (house rule) -`-am` (also-make) pulls in required upstream modules. That’s helpful for **compiles**, but hazardous for **tests**: Maven will advance included modules to the same lifecycle phase and run **their** tests too. - -**Rule of thumb** - -* ✅ Use `-am` **only** for compile/verify with tests skipped (e.g. `-Pquick`).: +`-am` is helpful for **compiles**, hazardous for **tests**. +* ✅ Use `-am` **only** for compile/verify with tests skipped (e.g. `-Pquick`): * `mvn -o -pl -am -Pquick install` * ❌ Do **not** use `-am` with `verify` when tests are enabled. **Two-step pattern (fast + safe)** - 1. **Compile deps fast (skip tests):** `mvn -o -pl -am -Pquick install` 2. **Run tests:** @@ -171,35 +222,28 @@ It is illegal to `-q` when running tests! The Maven reactor resolves inter-module dependencies from the local Maven repository (`~/.m2/repository`). Running `install` publishes your changed modules there so downstream modules and tests pick up the correct versions. -- Always run `mvn -o -Pquick install | tail -200` before you start working. This command typically takes between 10 and 30 seconds. -- Always run `mvn -o -pl -am -Pquick install | tail -200` before any `verify` or test runs. -- If offline resolution fails due to a missing dependency or plugin, rerun the exact `install` command once without `-o`, then return offline. -- Skipping this step can lead to stale or missing artifacts during tests, producing confusing compilation or linkage errors. -- Never ever change the repo location. Never use `-Dmaven.repo.local=.m2_repo`. Instead, ask for permission the first time you run `mvn -o -Pquick install | tail -200`. +* Always run `mvn -o -Pquick install | tail -200` before you start working. This command typically takes up to 30 seconds. Never use a small timeout than 30,000 ms. +* Always run `mvn -o -Pquick install | tail -200` before any `verify` or test runs. +* If offline resolution fails due to a missing dependency or plugin, rerun the exact `install` command once without `-o`, then return offline. +* Skipping this step can lead to stale or missing artifacts during tests, producing confusing compilation or linkage errors. +* Never ever change the repo location. Never use `-Dmaven.repo.local=.m2_repo`. +* Always try to run these commands first to see if they run without needing any approvals from the user w.r.t. the sandboxing. --- ## Quick Start (First 10 Minutes) 1. **Discover** - - * List modules: inspect root `pom.xml` (aggregator) and the module tree (see “Maven Module Overview” below). + * Inspect root `pom.xml` and module tree (see “Maven Module Overview”). * Search fast with ripgrep: `rg -n ""` 2. **Build sanity (fast, skip tests)** - - * **Preferred:** `mvn -o -Pquick install | tail -200` - * **Alternative:** `mvn -o -Pquick install | tail -200` - * This step is required before any tests. It installs artifacts to `~/.m2` so the reactor resolves fresh inter-module dependencies. + * `mvn -o -Pquick install | tail -200` 3. **Format (Java, imports, XML)** - * `mvn -o -q -T 2C formatter:format impsort:sort xml-format:xml-format` 4. **Targeted tests (tight loops)** - - * By module: `mvn -o -pl verify | tail -500` - * Single class: `mvn -o -pl -Dtest=ClassName verify | tail -500` - * Single method: `mvn -o -pl -Dtest=ClassName#method verify | tail -500` - * Prerequisite: ensure `mvn -o -Pquick install` (root or `-pl -am`) has just run so artifacts are available in `~/.m2`. + * Module: `mvn -o -pl verify | tail -500` + * Class: `mvn -o -pl -Dtest=ClassName verify | tail -500` + * Method: `mvn -o -pl -Dtest=ClassName#method verify | tail -500` 5. **Inspect failures** - * **Unit (Surefire):** `/target/surefire-reports/` * **IT (Failsafe):** `/target/failsafe-reports/` @@ -208,82 +252,95 @@ It is illegal to `-q` when running tests! --- -## Bugfix Workflow (Mandatory) +## Routine A — Full TDD (Default) -* **Reproduce first:** write the smallest focused test (class/method) that reproduces the reported bug **inside this repo**. Run it and confirm it fails with the same error/stacktrace. **Do not proceed without this.** -* **Keep the test as‑is:** do not weaken assertions or mute the failure. The failing test is your proof you’ve hit the right code path. -* **Fix at the root:** implement the minimal, surgical change in the correct module that addresses the underlying cause (no band‑aids). -* **Verify locally:** re‑run the focused test, then the surrounding module’s tests. Use targeted Maven invocations (class/method → module). Avoid `-am` with tests. -* **Broaden if needed:** only after green targeted runs, expand scope to neighboring modules when changes cross boundaries. -* **Document clearly:** in your final handoff, show the failing test before the fix, the root cause, the minimal fix, and passing tests after. Include **preamble** and **evidence** blocks. +> Use for **all behavior‑changing work** and whenever Routine B gates do not all pass. -### Hard Gates (Do Not Proceed Unless True) +### Bugfix Workflow (Mandatory) -* A failing test exists at the smallest scope (method/class) reproducing the report. +* **Reproduce first:** write the smallest focused test (class/method) that reproduces the reported bug **inside this repo**. Confirm it fails. +* **Keep the test as‑is:** do not weaken assertions or mute the failure. +* **Fix at the root:** minimal, surgical change in the correct module. +* **Verify locally:** re‑run the focused test, then the module’s tests. Avoid `-am`/`-q` with tests. +* **Broaden if needed:** expand scope only after targeted greens. +* **Document clearly:** failing output (pre‑fix), root cause, minimal fix, passing output (post‑fix). - * Show the failing command and include a snippet of the error/stack from `target/*-reports/`. +### Hard Gates + +* A failing test exists at the smallest scope (method/class). * **No production patch before the failing test is observed and recorded.** * Test runs avoid `-am` and `-q`. - * Use `-am` only with `-Pquick` to compile deps with tests skipped, then run tests without `-am`. -* Maintain a living plan with exactly one `in_progress` step; send a short preamble before long actions. - -### Required Sequence +--- -1. **Reproduce first** +## Routine B — Change without new tests (Proportional, gated) - * Add the smallest failing test in the correct module. - * Run it directly: `mvn -o -pl -Dtest=Class#method verify | tail -500` - * Inspect `target/surefire-reports/` (or `target/failsafe-reports/`) and capture the failure. -2. **Fix at the root (minimal, surgical)** +> Use **only** when at least one Allowed Case applies **and** all Routine B **Gates** pass. - * Change the correct layer; avoid widening APIs/configs. -3. **Verify locally (tight loops)** +### Allowed cases (one or more) +1. **Bugfix with existing failing test** in this repo (pinpoints class/method). +2. **Strictly behavior‑neutral refactor / cleanup / micro‑perf** with clear existing coverage hitting the edited path. +3. **Migration/rename/autogen refresh** where behavior is already characterized by existing tests. +4. **Build/CI/docs/logging/message changes** that do not alter runtime behavior or asserted outputs. +5. **Data/resource tweaks** not asserted by tests and not affecting behavior. - * Re-run the exact test selection; then run the whole module. -4. **Broaden only if necessary** +### Routine B Gates (all must pass) +- **Neutrality/Scope:** No externally observable behavior change. Localized edit. +- **Hit Proof:** Demonstrate tests exercise the edited code. +- **Pre/Post Green Match:** Same smallest‑scope selection, passing before and after. +- **Risk Check:** No concurrency/time/IO semantics touched; no public API, serialization, parsing, or ordering changes. +- **Reversibility:** Change is easy to revert if needed. - * Expand scope when changes cross module boundaries or neighbors fail. -5. **Document clearly** +**If any gate fails → switch to Routine A.** - * Include: failing output (pre‑fix), root cause, minimal fix, passing output (post‑fix). +--- -### Quick Self‑Check Before First Code Patch +## Routine C — Spike / Investigate (No production changes) -1. Do I have a failing test and its report snippet saved? -2. Am I using legal Maven flags for tests (no `-am`, no `-q`)? -3. Is my next step in the plan marked `in_progress` and did I state a preamble? -4. Is my fix located at the correct source of truth, not a workaround? +> Use for exploration, triage, design spikes, and measurement. **No production code edits.** ---- +**You may:** +- Add temporary scratch tests, assertions, scripts, or notes. +- Capture measurements, traces, logs. -## Working Loop +**Hand‑off must include:** +- Description, commands, and artifacts (logs/notes). +- Findings, options, and a proposed next routine (A or B). +- Removal of any temporary code if not adopted. -* **Plan** +--- - * Break task into **small, verifiable steps**; keep one step in progress. - * Announce a short preamble before long actions (builds/tests). - * Decide and proceed autonomously; document assumptions inline. -* **Change** +## Where to Draw the Line — A Short Debate - * Make minimal, surgical edits. Keep style and structure consistent. -* **Format** +> **Purist:** “All changes must start with a failing test.” +> **Pragmatist:** “For refactors that can’t fail first without faking it, prove coverage and equality of behavior.” - * `mvn -o -q -T 2C formatter:format impsort:sort xml-format:xml-format` -* **Compile (fast)** +**In‑scope for Routine B (examples)** +* Rename private methods; extract helper; dead‑code removal. +* Replace straightforward loop with stream (same results, same ordering). +* Tighten generics/nullability/annotations without observable change. +* Micro‑perf cache within a method with deterministic inputs and strong coverage. +* Logging/message tweaks **not** asserted by tests. +* Build/CI config that doesn’t alter runtime behavior. - * **Iterate locally:** `mvn -o -pl -am -Pquick install | tail -500` -* **Test** +**Out‑of‑scope (use Routine A)** +* Changing query results, serialization, or parsing behavior. +* Altering error messages that tests assert. +* Anything touching concurrency, timeouts, IO, or ordering. +* New SPARQL function support or extended syntax (even “tiny”). +* Public API changes or cross‑module migrations with unclear blast radius. - * Start with the smallest scope that exercises your change (class → module). - * For integration‑impacted changes, run module `verify` (includes ITs). -* **Triage** +--- - * Read reports; fix root cause; expand scope **only when needed**. -* **Iterate** +## Working Loop - * Keep moving without waiting for permission between steps. Escalate only at blocking points. - * Repeat until **Definition of Done** is satisfied. +* **Plan:** small, verifiable steps; keep one `in_progress`. +* **Change:** minimal, surgical edits; keep style/structure consistent. +* **Format:** `mvn -o -q -T 2C formatter:format impsort:sort xml-format:xml-format` +* **Compile (fast):** `mvn -o -pl -am -Pquick install | tail -500` +* **Test:** start smallest (class/method → module). For integration, run module `verify`. +* **Triage:** read reports; fix root cause; expand scope only when needed. +* **Iterate:** keep momentum; escalate only when blocked or irreversible. It is illegal to `-am` when running tests! It is illegal to `-q` when running tests! @@ -293,17 +350,12 @@ It is illegal to `-q` when running tests! ## Testing Strategy * **Prefer module tests you touched:** `-pl ` -* **Narrow further** to a class/method for tight loops; then broaden to the module. -* **Expand scope** when: - - * Your change crosses module boundaries, or - * Neighbor module failures indicate integration impact. +* **Narrow further** to a class/method; then broaden to the module. +* **Expand scope** when changes cross boundaries or neighbor modules fail. * **Read reports** - * Surefire (unit): `target/surefire-reports/` * Failsafe (IT): `target/failsafe-reports/` * **Helpful flags** - * `-Dtest=Class#method` (unit selection) * `-Dit.test=ITClass#method` (integration selection) * `-DtrimStackTrace=false` (full traces) @@ -311,156 +363,53 @@ It is illegal to `-q` when running tests! * `-DfailIfNoTests=false` (when selecting a class that has no tests on some platforms) ### Optional: Redirect test stdout/stderr to files - -To help automated agents inspect what a test printed to the console, you may redirect `System.out`/`System.err` to per‑class files generated by Surefire/Failsafe. This is **optional** and should be used only when it aids triage—house rules still apply (no `-am` with tests, no `-q`). - -**Unit tests (Surefire):** - ```bash mvn -o -pl -Dtest=ClassName[#method] -Dmaven.test.redirectTestOutputToFile=true verify | tail -500 -``` +```` -Logs will appear under: +Logs under: ``` /target/surefire-reports/ClassName-output.txt ``` -**Integration tests (Failsafe):** - -```bash -mvn -o -pl -Dit.test=ITClassName[#method] -Dmaven.test.redirectTestOutputToFile=true verify | tail -500 -``` - -Logs will appear under: - -``` -/target/failsafe-reports/ITClassName-output.txt -``` - -Notes: -* Capture is **per test class**, not per method. Multiple methods in the same class share one `*-output.txt`. -* Only output actually written to the console is captured. If your logging configuration writes solely to files, you won’t see it here. -* Continue to include the normal **Evidence** snippet from the Surefire/Failsafe report. You may additionally quote lines from the corresponding `*-output.txt` when useful for debugging. +(Use similarly for Failsafe via `-Dit.test=`.) --- ## Assertions: Make invariants explicit -Assertions are executable claims about what must be true. They’re the fastest way to surface “impossible” states and to localize bugs at the line that crossed a boundary it had no business crossing. Use them both as **temporary tripwires** during investigation and as **permanent contracts** once an invariant is known to matter. - -**Two useful flavors** - -* **Temporary tripwires (debug asserts):** Add while hunting a failing test or weird behavior. Keep them cheap, contextual, and local to the suspect path. Remove after the mystery is solved **or** convert to permanent checks if the invariant is genuinely important. -* **Permanent contracts:** Encode **preconditions** (valid inputs), **postconditions** (valid outputs), and **invariants** (state that must always hold). These stay and prevent regressions. +Assertions are executable claims about what must be true. Use **temporary tripwires** during investigation and **permanent contracts** once an invariant matters. -**Where to add assertions** - -* At **module boundaries** and **after parsing/external calls** (validate assumptions about returned/decoded data). -* Around **state transitions** (illegal transitions should fail loudly). -* In **concurrency hotspots** (e.g., “lock must be held”, “no concurrent mutation”). -* Before/after **caching, batching, or memoization** (keys, sizes, ordering, monotonicity). -* For **exhaustive enums** in `switch` statements (treat unexpected values as hard errors). - -**How to write good assertions** - -* One fact per assert. Fail **fast**, fail **usefully**. -* Include **stable context** in the message (ids, sizes, states) so the failure is self‑explanatory. -* Avoid side effects in the condition or message. Assertions may be disabled in some runtimes. -* Keep them **cheap**: no I/O, heavy allocations, or deep logging in the message. -* Don’t use asserts for **user‑facing validation**. Raise exceptions for expected bad inputs. +* One fact per assert; fail fast and usefully. +* Include stable context in messages; avoid side effects. +* Keep asserts cheap; don’t replace user input validation with asserts. **Java specifics** -* **Enable VM assertions in tests.** Tests must run with `-ea` so `assert` is active. -* Use **`assert`** for debug‑only invariants that “cannot happen.” Use **exceptions** for runtime guarantees: - - * Preconditions: `IllegalArgumentException` / `Objects.requireNonNull` (or Guava `Preconditions` if present). - * Invariants: `IllegalStateException`. -* Prefer treating unexpected enum values as **hard errors** rather than adding a quiet `default` path. - -**Concrete examples** - -Precondition (permanent) - -```java -void setPort(int port) { - if (port < 1 || port > 65_535) { - throw new IllegalArgumentException("port out of range: " + port); - } - this.port = port; -} -``` - -Invariant (permanent) - -```java -void advance(State next) { - if (!allowedTransitions.get(state).contains(next)) { - throw new IllegalStateException("Illegal transition " + state + " → " + next); - } - state = next; -} -``` - -Debug tripwire (temporary; remove or convert later) +* Enable VM assertions in tests (`-ea`). +* Use exceptions for runtime guarantees; `assert` for “cannot happen”. -```java -// Narrow a flaky failure around ordering -assert isSorted(results) : "unsorted results, size=" + results.size() + " ids=" + ids(results); -``` - -Unreachable (hard error) - -```java -switch (kind) { - case A: return handleA(); - case B: return handleB(); - default: - throw new IllegalStateException("Unhandled kind: " + kind); -} -``` - -Concurrency assumption - -```java -synchronized void put(String k, String v) { - assert Thread.holdsLock(this) : "put must hold instance monitor"; - // ... -} -``` - -House rule: Asserts are allowed and encouraged. Removing or weakening an assertion to “make it pass” is strictly forbidden — fix the cause, not the guardrail. +(Concrete examples omitted here for brevity; keep your current patterns.) --- ## Triage Playbook -* **Missing dep/plugin offline** - - * Remedy: **rerun the exact command without `-o`** once to fetch; then return offline. -* **Compilation errors** - - * Fix imports, generics, visibility; re‑run quick install (skip tests) in the **module**. -* **Flaky/slow tests** - - * Run the specific failing test; read its report; stabilize root cause before broad runs. -* **Formatting failures** - - * Run formatter/import/XML sort; re‑verify. -* **License header missing** - - * Add header for **new** files only (see “Source File Headers”); **do not** change years on existing files. +* **Missing dep/plugin offline:** rerun the exact command once **without** `-o`, then return offline. +* **Compilation errors:** fix imports/generics/visibility; quick install in the module. +* **Flaky/slow tests:** run the specific failing test; stabilize root cause before broad runs. +* **Formatting failures:** run formatter/import/XML sort; re‑verify. +* **License header missing:** add for **new** files only; do not change years on existing files. --- ## Code Formatting -* **Always run before finalizing:** +* Always run before finalizing: * `mvn -o -q -T 2C formatter:format impsort:sort xml-format:xml-format` -* **Style:** no wildcard imports; 120‑char width; curly braces always; LF line endings. -* **Tip:** formatting/import sort may be validated during `verify`. Running the commands proactively avoids CI/style failures. +* Style: no wildcard imports; 120‑char width; curly braces always; LF endings. --- @@ -481,8 +430,6 @@ Use this exact header for **new Java files only** (replace `${year}` with curren *******************************************************************************/ ``` -Use this exact header. Be very precise. - Do **not** modify existing headers’ years. --- @@ -491,50 +438,35 @@ Do **not** modify existing headers’ years. * **Format:** `mvn -o -q -T 2C formatter:format impsort:sort xml-format:xml-format` * **Compile (fast path):** `mvn -o -Pquick install | tail -200` -* **Tests (targeted):** `mvn -o -pl verify | tail -500` (broaden scope if needed) -* **Reports:** zero new failures in `target/surefire-reports/` or `target/failsafe-reports/`, or explain precisely. -* **Evidence:** include pre‑fix failing snippet and post‑fix passing summary. +* **Tests (targeted):** `mvn -o -pl verify | tail -500` (broaden as needed) +* **Reports:** zero new failures in Surefire/Failsafe, or explain precisely. +* **Evidence:** Routine A — failing pre‑fix + passing post‑fix. + Routine B — **pre/post green** from same selection + **Hit Proof**. --- ## Branching & Commit Conventions -- Branch names: start with `GH-XXXX` where `XXXX` is the GitHub issue number. Prefer a short, kebab‑case slug after the number when helpful, e.g., `GH-1234-add-trig-writer-check`. -- Commit messages: start with the same prefix, `GH-XXXX `, on every commit in the branch. -- Keep summaries concise, in imperative mood (e.g., “Fix NPE in TriG writer”). -- Example: - - Branch: `GH-1234-add-shacl-validation-metric` - - Commit: `GH-1234 Fix NPE when serializing empty graph` +* Branch names: start with `GH-XXXX` (GitHub issue number). Optional short slug, e.g., `GH-1234-trig-writer-check`. +* Commit messages: `GH-XXXX ` on every commit. --- ## Branch & PR Workflow (Agent) -- Confirm issue number first (mandatory): before creating a branch, pause and request/confirm the GitHub issue number. Do not proceed to branch creation until the issue number is provided or confirmed. -- Name branch: `GH--` (kebab‑case slug). -- Create branch: `git checkout -b GH-XXXX-your-slug`. -- Stage changes: `git add -A` (ensure new Java files have the required header). -- Optional but recommended: run format + quick install. - - `mvn -o -q -T 2C formatter:format impsort:sort xml-format:xml-format` - - `mvn -o -Pquick install | tail -200` -- Commit: `git commit -m "GH-XXXX "`. -- Push branch: `git push -u origin GH-XXXX-your-slug`. -- Create PR using default template: - - Preferred: `gh pr create --title "GH-XXXX

    " --body-file .github/pull_request_template.md` - - Fallback: `gh pr create --title "GH-XXXX " --body "$(cat .github/pull_request_template.md)"` -- Immediately fill the template (do not leave placeholders): - - Set `GitHub issue resolved: #XXXX`. - - Write a short, accurate change summary (what/why). - - Tick applicable checklist items only (self-contained, tests, squashed, commit message prefix, formatting if run). - - Include `Fixes #XXXX` to auto-close the issue on merge. -- Target the repo default branch (e.g., `origin/HEAD`). +* Confirm issue number first (mandatory). +* Branch: `git checkout -b GH-XXXX-your-slug` +* Stage: `git add -A` (ensure new Java files have the required header). +* Optional: formatter + quick install. +* Commit: `git commit -m "GH-XXXX "` +* Push & PR: use the default template; fill all fields; include `Fixes #XXXX`. --- ## Navigation & Search -* Fast file search: `rg --files` -* Fast content search: `rg -n ""` +* Files: `rg --files` +* Content: `rg -n ""` * Read big files in chunks: * `sed -n '1,200p' path/to/File.java` @@ -544,29 +476,17 @@ Do **not** modify existing headers’ years. ## Autonomy Rules (Act > Ask) -* **Default:** act with assumptions. Document assumptions in your plan and final answer. -* **Keep going:** chain steps without waiting for permission; send short progress updates before long actions. -* **Ask only when:** - - * Blocked by sandbox/approvals/network policy or missing secrets. - * The decision is destructive/irreversible, repo‑wide, or impacts public APIs. - * Adding dependencies, changing build profiles, or altering licensing. -* **Prefer reversible moves:** take the smallest local change that unblocks progress; validate with targeted tests before expanding scope. -* **Choose defaults** - - * **Tests:** start with `-pl `, then `-Dtest=Class#method` / `-Dit.test=ITClass#method`. - * **Build:** use `-o` quick/profiled commands; briefly drop `-o` to fetch missing deps, then return offline. - * **Formatting:** run formatter/impsort/xml‑format proactively before verify. - * **Reports:** read surefire/failsafe locally; expand scope only when necessary. -* **Error handling** +* **Default:** act with assumptions; document them. +* **Keep going:** chain steps; short progress updates before long actions. +* **Ask only when:** blocked by sandbox/approvals/network, or change is destructive/irreversible, or impacts public APIs/dependencies/licensing. +* **Prefer reversible moves:** smallest local change that unblocks progress; validate with targeted tests first. - * On compile/test failure: fix root cause locally, rerun targeted tests, then broaden. - * On flaky tests: rerun class/method; stabilize cause before repo‑wide runs. - * On formatting/license issues: apply prescribed commands/headers immediately. -* **Communication** +**Defaults** - * **Preambles:** 1–2 sentences grouping upcoming actions. - * **Updates:** inform to maintain visibility; do **not** request permission unless in “Ask only when” above. +* **Tests:** start with `-pl `, then `-Dtest=Class#method` / `-Dit.test=ITClass#method`. +* **Build:** use `-o`; drop `-o` once only to fetch; return offline. +* **Formatting:** run formatter/import/XML before verify. +* **Reports:** read surefire/failsafe locally; expand scope only when necessary. --- @@ -576,36 +496,31 @@ Do **not** modify existing headers’ years. * **Files touched:** list file paths. * **Commands run:** key build/test commands. * **Verification:** which tests passed, where you checked reports. -* **Evidence:** failing output (pre‑fix) and passing output (post‑fix) snippets. -* **Assumptions:** key assumptions and autonomous decisions you made. +* **Evidence:** + *Routine A:* failing output (pre‑fix) and passing output (post‑fix). + *Routine B:* pre‑ and post‑green snippets from the **same selection** + **Hit Proof**. + *Routine C:* artifacts from investigation (logs/notes/measurements) and proposed next steps. +* **Assumptions:** key assumptions and autonomous decisions. * **Limitations:** anything left or risky edge cases. -* **Next steps:** optional suggestions for follow‑ups. +* **Next steps:** optional follow‑ups. --- ## Running Tests -* By module: - - * `mvn -o -pl core/sail/shacl verify | tail -500` -* Entire repo: - - * `mvn -o verify` (long; only when appropriate) +* By module: `mvn -o -pl core/sail/shacl verify | tail -500` +* Entire repo: `mvn -o verify` (long; only when appropriate) * Slow tests (entire repo): - - * `mvn -o verify -PslowTestsOnly,-skipSlowTests | tail -500` + `mvn -o verify -PslowTestsOnly,-skipSlowTests | tail -500` * Slow tests (by module): - - * `mvn -o -pl verify -PslowTestsOnly,-skipSlowTests | tail -500` + `mvn -o -pl verify -PslowTestsOnly,-skipSlowTests | tail -500` * Slow tests (specific test): - * `mvn -o -pl core/sail/shacl -PslowTestsOnly,-skipSlowTests -Dtest=ClassName#method verify | tail -500` + * `mvn -o -pl core/sail/shacl -PslowTestsOnly,-skipSlowTests -Dtest=ClassName#method verify | tail -500` * Integration tests (entire repo): - - * `mvn -o verify -PskipUnitTests | tail -500` + `mvn -o verify -PskipUnitTests | tail -500` * Integration tests (by module): - - * `mvn -o -pl verify -PskipUnitTests | tail -500` + `mvn -o -pl verify -PskipUnitTests | tail -500` * Useful flags: * `-Dtest=ClassName` @@ -618,23 +533,49 @@ Do **not** modify existing headers’ years. ## Build * **Build without tests (fast path):** - - * `mvn -o -Pquick install` + `mvn -o -Pquick install` * **Verify with tests:** - - * Targeted module(s): `mvn -o -pl verify` - * Entire repo: `mvn -o verify` (use only when appropriate) + Targeted module(s): `mvn -o -pl verify` + Entire repo: `mvn -o verify` (use judiciously) * **When offline fails due to missing deps:** + Re‑run the **exact** command **without** `-o` once to fetch, then return to `-o`. + +--- + +## Using JaCoCo (Coverage) + +JaCoCo is configured via the `jacoco` Maven profile in the root POM. Surefire/Failsafe honor the prepared agent `argLine`, so no extra flags are required beyond `-Pjacoco`. + +- Run with coverage + - Module: `mvn -o -pl -Pjacoco verify | tail -500` + - Class: `mvn -o -pl -Pjacoco -Dtest=ClassName verify | tail -500` + - Method: `mvn -o -pl -Pjacoco -Dtest=ClassName#method verify | tail -500` + +- Where to find reports (per module) + - Exec data: `/target/jacoco.exec` + - HTML report: `/target/site/jacoco/index.html` + - XML report: `/target/site/jacoco/jacoco.xml` + +- Check if a specific test covers code X + - Run only that test (class or method) with `-Dtest=...` (see above) and `-Pjacoco`. + - Open the HTML report and navigate to the class/method of interest; non-zero line/branch coverage indicates the selected test touched it. + - For multiple tests, run them in small subsets to localize coverage quickly. + +- Troubleshooting + - If you see “Skipping JaCoCo execution due to missing execution data file”, ensure you passed `-Pjacoco` and ran the install step first. + - If offline resolution fails for the JaCoCo plugin, rerun the exact command once without `-o`, then return offline. - * Re‑run the **exact** command **without** `-o` once to fetch, then return to `-o`. +- Notes + - The default JaCoCo reports do not list “which individual tests” hit each line. Use single-test runs to infer per-test coverage. If you need true per-test mapping, add a JUnit 5 extension that sets a JaCoCo session per test and writes per-test exec files. + - Do not use `-am` when running tests; keep runs targeted by module/class/method. --- -## Prohibited Misinterpretations +## Prohibited Misinterpretations -* A user stack trace, reproduction script, or verbal description **is not evidence**. You must implement the smallest failing test **inside this repo**. -* “Obvious” contract violations (e.g., iterator returns `null`) still require a failing test first. Assumptions are not substitutes for evidence. -* “Quick fixes” are not quick if they bypass the workflow. They create audit gaps and regressions. +* A user stack trace, reproduction script, or verbal description **is not evidence** for behavior‑changing work. You must implement the smallest failing test **inside this repo**. +* For Routine B, a stack trace is neither required nor sufficient; **Hit Proof** plus **pre/post green** snippets are mandatory. +* Routine C must not change production code. --- diff --git a/pom.xml b/pom.xml index d032d63fd1c..3cd54359b76 100644 --- a/pom.xml +++ b/pom.xml @@ -264,6 +264,32 @@ + + jacoco + + + + org.jacoco + jacoco-maven-plugin + 0.8.13 + + + + prepare-agent + + + + report + verify + + report + + + + + + + formatting @@ -378,6 +404,7 @@ 5.9.3 9.4.54.v20240208 4.1.111.Final + @@ -813,7 +840,8 @@ maven-surefire-plugin 3.5.4 - -Xmx2G + + @{argLine} -Xmx2G @@ -823,7 +851,8 @@ 1 false - -Xmx2G + + @{argLine} -Xmx2G **/*IT.java From 8fe8094c4a4a9599fca0016cef5ed2d9dc8aa31f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Thu, 2 Oct 2025 10:47:44 +0200 Subject: [PATCH 21/46] Add support for getting the type directly from a Value without using instance of --- .../java/org/eclipse/rdf4j/model/BNode.java | 5 +++++ .../java/org/eclipse/rdf4j/model/IRI.java | 5 +++++ .../java/org/eclipse/rdf4j/model/Literal.java | 5 +++++ .../java/org/eclipse/rdf4j/model/Triple.java | 5 +++++ .../java/org/eclipse/rdf4j/model/Value.java | 22 +++++++++++++++++++ .../nativerdf/model/CorruptIRIOrBNode.java | 5 +++++ 6 files changed, 47 insertions(+) diff --git a/core/model-api/src/main/java/org/eclipse/rdf4j/model/BNode.java b/core/model-api/src/main/java/org/eclipse/rdf4j/model/BNode.java index e49019643f5..53a56e61575 100644 --- a/core/model-api/src/main/java/org/eclipse/rdf4j/model/BNode.java +++ b/core/model-api/src/main/java/org/eclipse/rdf4j/model/BNode.java @@ -27,6 +27,11 @@ default boolean isBNode() { return true; } + @Override + default Type getType() { + return Value.Type.BNode; + } + /** * Retrieves this blank node's identifier. * diff --git a/core/model-api/src/main/java/org/eclipse/rdf4j/model/IRI.java b/core/model-api/src/main/java/org/eclipse/rdf4j/model/IRI.java index cb99a4d4e5b..2f4c641c436 100644 --- a/core/model-api/src/main/java/org/eclipse/rdf4j/model/IRI.java +++ b/core/model-api/src/main/java/org/eclipse/rdf4j/model/IRI.java @@ -38,6 +38,11 @@ default boolean isIRI() { return true; } + @Override + default Type getType() { + return Value.Type.IRI; + } + /** * Gets the namespace part of this IRI. *

    diff --git a/core/model-api/src/main/java/org/eclipse/rdf4j/model/Literal.java b/core/model-api/src/main/java/org/eclipse/rdf4j/model/Literal.java index b261e76061c..551afa50afd 100644 --- a/core/model-api/src/main/java/org/eclipse/rdf4j/model/Literal.java +++ b/core/model-api/src/main/java/org/eclipse/rdf4j/model/Literal.java @@ -47,6 +47,11 @@ default boolean isLiteral() { return true; } + @Override + default Type getType() { + return Value.Type.Literal; + } + /** * Gets the label (the lexical value) of this literal. * diff --git a/core/model-api/src/main/java/org/eclipse/rdf4j/model/Triple.java b/core/model-api/src/main/java/org/eclipse/rdf4j/model/Triple.java index 29ad625bae7..13ee787c130 100644 --- a/core/model-api/src/main/java/org/eclipse/rdf4j/model/Triple.java +++ b/core/model-api/src/main/java/org/eclipse/rdf4j/model/Triple.java @@ -33,6 +33,11 @@ default boolean isTriple() { return true; } + @Override + default Type getType() { + return Value.Type.Triple; + } + /** * Gets the subject of this triple. * diff --git a/core/model-api/src/main/java/org/eclipse/rdf4j/model/Value.java b/core/model-api/src/main/java/org/eclipse/rdf4j/model/Value.java index d54f18e762d..1fbd933dbc4 100644 --- a/core/model-api/src/main/java/org/eclipse/rdf4j/model/Value.java +++ b/core/model-api/src/main/java/org/eclipse/rdf4j/model/Value.java @@ -17,6 +17,13 @@ */ public interface Value extends Serializable { + enum Type { + IRI, + BNode, + Literal, + Triple + } + /** * Check if the object is an instance of the given type. Typically 2x than using instanceof. *

    @@ -72,6 +79,21 @@ default boolean isTriple() { return false; } + default Type getType() { + + if (isIRI()) { + return Type.IRI; + } else if (isBNode()) { + return Type.BNode; + } else if (isLiteral()) { + return Type.Literal; + } else if (isTriple()) { + return Type.Triple; + } else { + throw new IllegalStateException("Unknown value type"); + } + } + /** * Returns the String-value of a Value object. This returns either a {@link Literal}'s label, a * {@link IRI}'s URI or a {@link BNode}'s ID. diff --git a/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/model/CorruptIRIOrBNode.java b/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/model/CorruptIRIOrBNode.java index 83cdb9e6658..455a88878d3 100644 --- a/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/model/CorruptIRIOrBNode.java +++ b/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/model/CorruptIRIOrBNode.java @@ -50,6 +50,11 @@ public String stringValue() { return "CorruptIRIOrBNode_with_ID_" + getInternalID(); } + @Override + public Type getType() { + return IRI.super.getType(); + } + @Override public String getNamespace() { return "urn:CorruptIRIOrBNode:"; From 1e80c2f83bdc0da6f00def54da774259ee579348 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Thu, 2 Oct 2025 10:51:47 +0200 Subject: [PATCH 22/46] GH-5447 improve group by and value factory --- .../common/iteration/CloseableIteration.java | 4 +++ .../common/iteration/DualUnionIteration.java | 18 +++++++++-- .../org/eclipse/rdf4j/model/ValueFactory.java | 10 ++++++ .../rdf4j/model/base/AbstractLiteral.java | 4 +++ .../model/base/AbstractValueFactory.java | 32 +++++++++++++++++++ .../rdf4j/model/impl/SimpleValueFactory.java | 6 ++++ .../model/impl/ValidatingValueFactory.java | 5 +++ .../evaluation/iterator/GroupIterator.java | 2 +- .../rdf4j/sail/base/SailDatasetImpl.java | 3 +- 9 files changed, 78 insertions(+), 6 deletions(-) diff --git a/core/common/iterator/src/main/java/org/eclipse/rdf4j/common/iteration/CloseableIteration.java b/core/common/iterator/src/main/java/org/eclipse/rdf4j/common/iteration/CloseableIteration.java index 15890fe2aa7..6023a7eb0c0 100644 --- a/core/common/iterator/src/main/java/org/eclipse/rdf4j/common/iteration/CloseableIteration.java +++ b/core/common/iterator/src/main/java/org/eclipse/rdf4j/common/iteration/CloseableIteration.java @@ -14,6 +14,8 @@ import java.util.Iterator; import java.util.stream.Stream; +import org.eclipse.rdf4j.model.Statement; + /** * An {@link CloseableIteration} that can be closed to free resources that it is holding. CloseableIterations * automatically free their resources when exhausted. If not read until exhaustion or if you want to make sure the @@ -33,6 +35,8 @@ */ public interface CloseableIteration extends Iterator, AutoCloseable { + EmptyIteration EMPTY_STATEMENT_ITERATION = new EmptyIteration<>(); + /** * Convert the results to a Java 8 Stream. * diff --git a/core/common/iterator/src/main/java/org/eclipse/rdf4j/common/iteration/DualUnionIteration.java b/core/common/iterator/src/main/java/org/eclipse/rdf4j/common/iteration/DualUnionIteration.java index 40a95ec76bd..2a490e43f2a 100644 --- a/core/common/iterator/src/main/java/org/eclipse/rdf4j/common/iteration/DualUnionIteration.java +++ b/core/common/iterator/src/main/java/org/eclipse/rdf4j/common/iteration/DualUnionIteration.java @@ -50,7 +50,11 @@ public DualUnionIteration(Comparator cmp, public static CloseableIteration getWildcardInstance( CloseableIteration leftIteration, CloseableIteration rightIteration) { - if (rightIteration instanceof EmptyIteration) { + if (leftIteration == EMPTY_STATEMENT_ITERATION) { + return rightIteration; + } else if (rightIteration == EMPTY_STATEMENT_ITERATION) { + return leftIteration; + } else if (rightIteration instanceof EmptyIteration) { return leftIteration; } else if (leftIteration instanceof EmptyIteration) { return rightIteration; @@ -63,7 +67,11 @@ public static CloseableIteration getWildcardInstance( public static CloseableIteration getWildcardInstance(Comparator cmp, CloseableIteration leftIteration, CloseableIteration rightIteration) { - if (rightIteration instanceof EmptyIteration) { + if (leftIteration == EMPTY_STATEMENT_ITERATION) { + return rightIteration; + } else if (rightIteration == EMPTY_STATEMENT_ITERATION) { + return leftIteration; + } else if (rightIteration instanceof EmptyIteration) { return leftIteration; } else if (leftIteration instanceof EmptyIteration) { return rightIteration; @@ -75,7 +83,11 @@ public static CloseableIteration getWildcardInstance(Comparator public static CloseableIteration getInstance(CloseableIteration leftIteration, CloseableIteration rightIteration) { - if (rightIteration instanceof EmptyIteration) { + if (leftIteration == EMPTY_STATEMENT_ITERATION) { + return rightIteration; + } else if (rightIteration == EMPTY_STATEMENT_ITERATION) { + return leftIteration; + } else if (rightIteration instanceof EmptyIteration) { return leftIteration; } else if (leftIteration instanceof EmptyIteration) { return rightIteration; diff --git a/core/model-api/src/main/java/org/eclipse/rdf4j/model/ValueFactory.java b/core/model-api/src/main/java/org/eclipse/rdf4j/model/ValueFactory.java index 00441731e57..058fe6dac08 100644 --- a/core/model-api/src/main/java/org/eclipse/rdf4j/model/ValueFactory.java +++ b/core/model-api/src/main/java/org/eclipse/rdf4j/model/ValueFactory.java @@ -155,6 +155,16 @@ public interface ValueFactory { */ Literal createLiteral(long value); + /** + * Creates a new typed numerical literal representing the specified value. + * + * @param value The value for the literal. + * @param xsd The XSD datatype to use. + */ + default Literal createLiteral(long value, CoreDatatype.XSD xsd) { + return createLiteral(Long.toString(value), xsd); + } + /** * Creates a new xsd:float-typed literal representing the specified value. * diff --git a/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractLiteral.java b/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractLiteral.java index 635c12a8847..dd412527a9d 100644 --- a/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractLiteral.java +++ b/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractLiteral.java @@ -402,6 +402,10 @@ private static String toString(double value) { this(value, Long.toString(value), CoreDatatype.XSD.LONG); } + NumberLiteral(long value, CoreDatatype.XSD datatype) { + this(value, Long.toString(value), datatype); + } + NumberLiteral(float value) { this(value, toString(value), CoreDatatype.XSD.FLOAT); } diff --git a/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractValueFactory.java b/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractValueFactory.java index e88070a5af3..c2f49a56adf 100644 --- a/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractValueFactory.java +++ b/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractValueFactory.java @@ -128,9 +128,36 @@ public Literal createLiteral(String label, CoreDatatype datatype) { throw new IllegalArgumentException("reserved datatype <" + datatype + ">"); } + if (datatype == CoreDatatype.XSD.INTEGER) { + if (label.length() <= 3 && !label.startsWith("+") && !label.startsWith("-")) { + try { + int v = Integer.parseInt(label); + if (v >= 0 && v <= 999) { + return smallIntLiterals[v]; + } + } catch (NumberFormatException e) { + // ignore + } + } + } else if (datatype == CoreDatatype.XSD.BOOLEAN) { + if ("true".equals(label) || "1".equals(label)) { + return TRUE; + } else if ("false".equals(label) || "0".equals(label)) { + return FALSE; + } + } + return new TypedLiteral(label, datatype); } + static TypedLiteral[] smallIntLiterals = new TypedLiteral[1000]; + + static { + for (int i = 0; i <= 999; i++) { + smallIntLiterals[i] = new TypedLiteral(i + "", CoreDatatype.XSD.INTEGER); + } + } + @Override public Literal createLiteral(String label, IRI datatype, CoreDatatype coreDatatype) { Objects.requireNonNull(label, "Label may not be null"); @@ -182,6 +209,11 @@ public Literal createLiteral(long value) { return new NumberLiteral(value); } + @Override + public Literal createLiteral(long value, CoreDatatype.XSD datatype) { + return new NumberLiteral(value, datatype); + } + @Override public Literal createLiteral(float value) { return new NumberLiteral(value); diff --git a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleValueFactory.java b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleValueFactory.java index b9b685b7fcd..6720d9d034e 100644 --- a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleValueFactory.java +++ b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleValueFactory.java @@ -165,6 +165,12 @@ public Literal createLiteral(long value) { return createIntegerLiteral(value, CoreDatatype.XSD.LONG); } + @Override + public Literal createLiteral(long value, CoreDatatype.XSD xsd) { + assert xsd.isIntegerDatatype(); + return createIntegerLiteral(value, xsd); + } + /** * Calls {@link #createNumericLiteral(Number, IRI)} with the supplied value and datatype as parameters. */ diff --git a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/ValidatingValueFactory.java b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/ValidatingValueFactory.java index 0cfe2860938..62664cbfc5f 100644 --- a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/ValidatingValueFactory.java +++ b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/ValidatingValueFactory.java @@ -175,6 +175,11 @@ public Literal createLiteral(long value) { return delegate.createLiteral(value); } + @Override + public Literal createLiteral(long value, CoreDatatype.XSD xsd) { + return delegate.createLiteral(value, xsd); + } + @Override public Literal createLiteral(float value) { return delegate.createLiteral(value); diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/GroupIterator.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/GroupIterator.java index 9a362365fdb..bffb4e422fb 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/GroupIterator.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/GroupIterator.java @@ -505,7 +505,7 @@ public CountCollector(ValueFactory vf) { @Override public Value getFinalValue() { - return vf.createLiteral(Long.toString(value), CoreDatatype.XSD.INTEGER); + return vf.createLiteral(value, CoreDatatype.XSD.INTEGER); } } diff --git a/core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/SailDatasetImpl.java b/core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/SailDatasetImpl.java index b90a9008657..5f6fd74407d 100644 --- a/core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/SailDatasetImpl.java +++ b/core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/SailDatasetImpl.java @@ -46,7 +46,6 @@ class SailDatasetImpl implements SailDataset { private static final EmptyIteration TRIPLE_EMPTY_ITERATION = new EmptyIteration<>(); private static final EmptyIteration NAMESPACES_EMPTY_ITERATION = new EmptyIteration<>(); - private static final EmptyIteration STATEMENT_EMPTY_ITERATION = new EmptyIteration<>(); /** * {@link SailDataset} of the backing {@link SailSource}. @@ -286,7 +285,7 @@ public CloseableIteration getStatements(Resource subj, IRI } else if (iter != null) { return iter; } else { - return STATEMENT_EMPTY_ITERATION; + return CloseableIteration.EMPTY_STATEMENT_ITERATION; } } From 8d8c498f37a80092ed31b5e70545c3469ebf8680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Thu, 2 Oct 2025 10:53:12 +0200 Subject: [PATCH 23/46] GH-5447 improve queries using MINUS --- core/query/pom.xml | 5 + .../rdf4j/query/AbstractBindingSet.java | 5 +- .../org/eclipse/rdf4j/query/BindingSet.java | 15 ++ .../org/eclipse/rdf4j/query/QueryResults.java | 11 +- .../rdf4j/query/BindingSetCompatibleTest.java | 201 ++++++++++++++++ .../algebra/evaluation/ArrayBindingSet.java | 106 +++++++++ .../BadlyDesignedLeftJoinIterator.java | 5 +- .../iterator/SPARQLMinusIteration.java | 122 +++++++--- .../SPARQLMinusIterationFuzzTest.java | 218 ++++++++++++++++++ .../iterator/SPARQLMinusIterationTest.java | 199 ++++++++++++++++ 10 files changed, 848 insertions(+), 39 deletions(-) create mode 100644 core/query/src/test/java/org/eclipse/rdf4j/query/BindingSetCompatibleTest.java create mode 100644 core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/SPARQLMinusIterationFuzzTest.java create mode 100644 core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/SPARQLMinusIterationTest.java diff --git a/core/query/pom.xml b/core/query/pom.xml index a82e0d8f8b5..afa328fd1f2 100644 --- a/core/query/pom.xml +++ b/core/query/pom.xml @@ -41,5 +41,10 @@ junit-platform-suite-engine test + + org.junit.jupiter + junit-jupiter-params + test + diff --git a/core/query/src/main/java/org/eclipse/rdf4j/query/AbstractBindingSet.java b/core/query/src/main/java/org/eclipse/rdf4j/query/AbstractBindingSet.java index dd5108c77cf..d5b34acda58 100644 --- a/core/query/src/main/java/org/eclipse/rdf4j/query/AbstractBindingSet.java +++ b/core/query/src/main/java/org/eclipse/rdf4j/query/AbstractBindingSet.java @@ -26,6 +26,9 @@ public abstract class AbstractBindingSet implements BindingSet { @Override public boolean equals(Object other) { + if (other == null) { + return false; + } if (this == other) { return true; } @@ -61,7 +64,7 @@ public boolean equals(Object other) { } @Override - public final int hashCode() { + public int hashCode() { int hashCode = 0; for (Binding binding : this) { diff --git a/core/query/src/main/java/org/eclipse/rdf4j/query/BindingSet.java b/core/query/src/main/java/org/eclipse/rdf4j/query/BindingSet.java index 312932949ad..222146ac04a 100644 --- a/core/query/src/main/java/org/eclipse/rdf4j/query/BindingSet.java +++ b/core/query/src/main/java/org/eclipse/rdf4j/query/BindingSet.java @@ -101,4 +101,19 @@ public interface BindingSet extends Iterable, Serializable { default boolean isEmpty() { return size() == 0; } + + /** + * Check whether this BindingSet is compatible with another. Two binding sets are compatible if they have equal + * values for each variable that is bound in both binding sets. A variable that is unbound in either set is + * considered compatible. + * + *

    + * Default implementation mirrors {@link QueryResults#bindingSetsCompatible(BindingSet, BindingSet)}. + * + * @param other the other binding set to compare with + * @return true if compatible + */ + default boolean isCompatible(BindingSet other) { + return QueryResults.bindingSetsCompatible(this, other); + } } diff --git a/core/query/src/main/java/org/eclipse/rdf4j/query/QueryResults.java b/core/query/src/main/java/org/eclipse/rdf4j/query/QueryResults.java index 659d3e97d2f..73cf7be1bca 100644 --- a/core/query/src/main/java/org/eclipse/rdf4j/query/QueryResults.java +++ b/core/query/src/main/java/org/eclipse/rdf4j/query/QueryResults.java @@ -540,20 +540,19 @@ private static boolean bindingSetsMatch(BindingSet bs1, BindingSet bs2, Map bs1BindingNames = bs1.getBindingNames(); - if (bs1BindingNames.isEmpty()) { + if (bs1.isEmpty() || bs2.isEmpty()) { return true; } - Set bs2BindingNames = bs2.getBindingNames(); - for (Binding binding : bs1) { - if (bs2BindingNames.contains(binding.getName())) { + Binding other = bs2.getBinding(binding.getName()); + + if (other != null) { Value value1 = binding.getValue(); // if a variable is unbound in one set it is compatible if (value1 != null) { - Value value2 = bs2.getValue(binding.getName()); + Value value2 = other.getValue(); // if a variable is unbound in one set it is compatible if (value2 != null && !value1.equals(value2)) { diff --git a/core/query/src/test/java/org/eclipse/rdf4j/query/BindingSetCompatibleTest.java b/core/query/src/test/java/org/eclipse/rdf4j/query/BindingSetCompatibleTest.java new file mode 100644 index 00000000000..0d579fef3f6 --- /dev/null +++ b/core/query/src/test/java/org/eclipse/rdf4j/query/BindingSetCompatibleTest.java @@ -0,0 +1,201 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + ******************************************************************************/ + +package org.eclipse.rdf4j.query; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.Random; +import java.util.stream.Stream; + +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.query.impl.ListBindingSet; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * Tests for BindingSet compatibility API. Verifies that BindingSet#isCompatible has identical semantics to + * QueryResults#bindingSetsCompatible across a variety of scenarios. + */ +public class BindingSetCompatibleTest { + + private static final ValueFactory VF = SimpleValueFactory.getInstance(); + + @Test + public void isCompatible_exists_and_matches_QueryResults() throws Exception { + // Prefer the current API name; fallback to legacy name if present + Method m; + try { + m = BindingSet.class.getMethod("isCompatible", BindingSet.class); + } catch (NoSuchMethodException e1) { + try { + m = BindingSet.class.getMethod("bindingSetCompatible", BindingSet.class); + } catch (NoSuchMethodException e2) { + fail("BindingSet#isCompatible(BindingSet) method is missing"); + return; + } + } + + // Verify semantics align with QueryResults.bindingSetsCompatible on a few basic cases + List names = Arrays.asList("a", "b"); + + BindingSet s1 = new ListBindingSet(names, VF.createIRI("urn:x"), VF.createLiteral(1)); + BindingSet s2 = new ListBindingSet(names, VF.createIRI("urn:x"), VF.createLiteral(2)); + boolean expected = QueryResults.bindingSetsCompatible(s1, s2); + boolean actual = (Boolean) m.invoke(s1, s2); + assertThat(actual).isEqualTo(expected); + + BindingSet s3 = new ListBindingSet(names, VF.createIRI("urn:x"), VF.createLiteral(1)); + BindingSet s4 = new ListBindingSet(names, VF.createIRI("urn:x"), VF.createLiteral(1)); + expected = QueryResults.bindingSetsCompatible(s3, s4); + actual = (Boolean) m.invoke(s3, s4); + assertThat(actual).isEqualTo(expected); + + BindingSet s5 = new ListBindingSet(names, null, VF.createLiteral(1)); + BindingSet s6 = new ListBindingSet(names, null, VF.createLiteral(2)); + expected = QueryResults.bindingSetsCompatible(s5, s6); + actual = (Boolean) m.invoke(s5, s6); + assertThat(actual).isEqualTo(expected); + } + + @Test + public void isCompatible_empty_sets_true() { + BindingSet empty1 = new ListBindingSet(List.of()); + BindingSet empty2 = new ListBindingSet(List.of()); + assertThat(empty1.isCompatible(empty2)).isTrue(); + assertThat(empty2.isCompatible(empty1)).isTrue(); + } + + @Test + public void isCompatible_one_empty_true() { + List names = Arrays.asList("a", "b"); + BindingSet nonEmpty = new ListBindingSet(names, VF.createIRI("urn:x"), VF.createLiteral(1)); + BindingSet empty = new ListBindingSet(List.of()); + assertThat(nonEmpty.isCompatible(empty)).isTrue(); + assertThat(empty.isCompatible(nonEmpty)).isTrue(); + } + + @Test + public void isCompatible_disjoint_names_true() { + BindingSet aOnly = new ListBindingSet(List.of("a"), VF.createLiteral(1)); + BindingSet bOnly = new ListBindingSet(List.of("b"), VF.createLiteral(2)); + assertThat(aOnly.isCompatible(bOnly)).isTrue(); + assertThat(bOnly.isCompatible(aOnly)).isTrue(); + } + + @Test + public void isCompatible_partial_overlap_true_when_equal() { + List namesAB = Arrays.asList("a", "b"); + BindingSet ab = new ListBindingSet(namesAB, VF.createIRI("urn:x"), VF.createLiteral(1)); + BindingSet b = new ListBindingSet(List.of("b"), VF.createLiteral(1)); + assertThat(ab.isCompatible(b)).isTrue(); + assertThat(b.isCompatible(ab)).isTrue(); + } + + @Test + public void isCompatible_partial_overlap_false_when_conflict() { + List namesAB = Arrays.asList("a", "b"); + BindingSet ab = new ListBindingSet(namesAB, VF.createIRI("urn:x"), VF.createLiteral(1)); + BindingSet bConflict = new ListBindingSet(List.of("b"), VF.createLiteral(2)); + assertThat(ab.isCompatible(bConflict)).isFalse(); + assertThat(bConflict.isCompatible(ab)).isFalse(); + } + + @Test + public void isCompatible_null_variable_ignored_when_overlap_equal() { + List namesAB = Arrays.asList("a", "b"); + BindingSet s1 = new ListBindingSet(namesAB, null, VF.createLiteral(1)); + BindingSet s2 = new ListBindingSet(namesAB, null, VF.createLiteral(1)); + assertThat(s1.isCompatible(s2)).isTrue(); + assertThat(s2.isCompatible(s1)).isTrue(); + } + + @ParameterizedTest(name = "fuzz case {index}") + @MethodSource("fuzzCases") + public void isCompatible_fuzz_parity_and_symmetry(BindingSet s1, BindingSet s2) { + boolean expected = QueryResults.bindingSetsCompatible(s1, s2); + assertThat(s1.isCompatible(s2)).isEqualTo(expected); + // symmetry + boolean expectedReverse = QueryResults.bindingSetsCompatible(s2, s1); + assertThat(s2.isCompatible(s1)).isEqualTo(expectedReverse); + } + + static Stream fuzzCases() { + Random rnd = new Random(424242); + List universe = Arrays.asList("a", "b", "c", "d", "e", "x", "y", "z"); + int cases = 128; // balanced coverage and speed + Stream.Builder b = Stream.builder(); + for (int i = 0; i < cases; i++) { + BindingSet s1 = randomBindingSet(rnd, universe); + BindingSet s2 = randomBindingSet(rnd, universe); + b.add(Arguments.of(s1, s2)); + } + return b.build(); + } + + private static BindingSet randomBindingSet(Random rnd, List universe) { + // Randomly decide how many variables to include (possibly zero) + int n = rnd.nextInt(universe.size() + 1); // 0..universe.size() + // Shuffle-like selection via random threshold + java.util.ArrayList selected = new java.util.ArrayList<>(universe.size()); + for (String name : universe) { + if (selected.size() >= n) { + break; + } + // ~50% chance to include each name until we reach n + if (rnd.nextBoolean()) { + selected.add(name); + } + } + // If selection under-filled, top up from remaining deterministically + for (String name : universe) { + if (selected.size() >= n) { + break; + } + if (!selected.contains(name)) { + selected.add(name); + } + } + + Value[] values = new Value[selected.size()]; + for (int i = 0; i < selected.size(); i++) { + values[i] = randomValueOrNull(rnd, i); + } + return new ListBindingSet(selected, values); + } + + private static org.eclipse.rdf4j.model.Value randomValueOrNull(Random rnd, int salt) { + int pick = rnd.nextInt(6); + switch (pick) { + case 0: + return null; // unbound + case 1: + return VF.createIRI("urn:res:" + rnd.nextInt(10)); + case 2: + return VF.createLiteral(rnd.nextInt(5)); + case 3: + return VF.createLiteral("s" + rnd.nextInt(5)); + case 4: + return VF.createBNode("b" + rnd.nextInt(5)); + default: + return VF.createIRI("urn:x:" + ((salt + rnd.nextInt(5)) % 7)); + } + } + +} diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/ArrayBindingSet.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/ArrayBindingSet.java index 08b7960e499..9b0fa63b047 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/ArrayBindingSet.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/ArrayBindingSet.java @@ -17,6 +17,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Function; @@ -53,6 +54,7 @@ public class ArrayBindingSet extends AbstractBindingSet implements MutableBindin private boolean empty; private final Value[] values; + private int cachedHashCode; /** * Creates a new Array-based BindingSet for the supplied bindings names. The supplied list of binding names is @@ -292,6 +294,62 @@ public int size() { return size; } + @Override + public boolean isCompatible(BindingSet other) { + if (isEmpty() || other.isEmpty()) { + return true; + } + + if (other instanceof ArrayBindingSet) { + ArrayBindingSet o = (ArrayBindingSet) other; + // Fast path when we share the same bindingNames array (identity equality) + + if (this.bindingNames == o.bindingNames) { + return fastIsCompatible(o); + } + } + + return slowIsCompatible(other); + } + + private boolean fastIsCompatible(ArrayBindingSet o) { + for (int i = 0; i < this.values.length; i++) { + Value v1 = this.values[i]; + if (v1 != null && v1 != NULL_VALUE) { + Value v2 = o.values[i]; + if (v2 != null && v2 != NULL_VALUE) { + if (v1.getType() != v2.getType()) { + return false; + } + if (!v1.equals(v2)) { + return false; + } + } + } + } + return true; + } + + private boolean slowIsCompatible(BindingSet other) { + // General path: iterate our bound values and compare against the other's value by name + for (int i = 0; i < this.bindingNames.length; i++) { + Value v1 = this.values[i]; + if (v1 != null && v1 != NULL_VALUE) { + Value v2 = other.getValue(this.bindingNames[i]); + if (v2 != null) { + if (v1.getType() != v2.getType()) { + return false; + } + if (!v1.equals(v2)) { + return false; + } + } + } + } + + return true; + } + List sortedBindingNames = null; public List getSortedBindingNames() { @@ -379,6 +437,7 @@ public boolean isEmpty() { private void clearCache() { bindingNamesSetCache = null; + cachedHashCode = 0; } public void addAll(ArrayBindingSet other) { @@ -403,6 +462,53 @@ public void addAll(ArrayBindingSet other) { } + @Override + public int hashCode() { + if (cachedHashCode == 0) { + cachedHashCode = super.hashCode(); + } + return cachedHashCode; + } + + @Override + public boolean equals(Object other) { + if (other == null) { + return false; + } + if (other == this) { + return true; + } + + if (other.getClass() != ArrayBindingSet.class) { + return super.equals(other); + } + + ArrayBindingSet o = (ArrayBindingSet) other; + if (empty && o.empty) { + return true; + } + if (empty != o.empty) { + return false; + } + if (size() != o.size()) { + return false; + } + + if (bindingNames == o.bindingNames) { + for (int i = 0; i < values.length; i++) { + if (values[i] != o.values[i]) { + if (!Objects.equals(values[i], o.values[i])) { + return false; + } + } + } + return true; + } + + return super.equals(other); + + } + private class ArrayBindingSetIterator implements Iterator { private int index = 0; diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/BadlyDesignedLeftJoinIterator.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/BadlyDesignedLeftJoinIterator.java index a80e93c01b6..be0eae6c875 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/BadlyDesignedLeftJoinIterator.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/BadlyDesignedLeftJoinIterator.java @@ -15,13 +15,10 @@ import org.eclipse.rdf4j.query.BindingSet; import org.eclipse.rdf4j.query.MutableBindingSet; import org.eclipse.rdf4j.query.QueryEvaluationException; -import org.eclipse.rdf4j.query.QueryResults; import org.eclipse.rdf4j.query.algebra.LeftJoin; import org.eclipse.rdf4j.query.algebra.evaluation.EvaluationStrategy; import org.eclipse.rdf4j.query.algebra.evaluation.QueryBindingSet; import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep; -import org.eclipse.rdf4j.query.algebra.evaluation.QueryValueEvaluationStep; -import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext; /** * @author Arjohn Kampman @@ -71,7 +68,7 @@ protected BindingSet getNextElement() throws QueryEvaluationException { BindingSet result = super.getNextElement(); // Ignore all results that are not compatible with the input bindings - while (result != null && !QueryResults.bindingSetsCompatible(inputBindings, result)) { + while (result != null && !inputBindings.isCompatible(result)) { result = super.getNextElement(); } diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/SPARQLMinusIteration.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/SPARQLMinusIteration.java index e0a8a429ec8..4e29b534b6a 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/SPARQLMinusIteration.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/SPARQLMinusIteration.java @@ -10,16 +10,19 @@ *******************************************************************************/ package org.eclipse.rdf4j.query.algebra.evaluation.iterator; +import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.eclipse.rdf4j.common.iteration.CloseableIteration; import org.eclipse.rdf4j.common.iteration.FilterIteration; import org.eclipse.rdf4j.common.iteration.Iterations; +import org.eclipse.rdf4j.model.Value; import org.eclipse.rdf4j.query.BindingSet; -import org.eclipse.rdf4j.query.QueryResults; /** * An Iteration that returns the results of an Iteration (the left argument) MINUS any results that are compatible with @@ -42,6 +45,10 @@ public class SPARQLMinusIteration extends FilterIteration { private Set excludeSet; private Set excludeSetBindingNames; private boolean excludeSetBindingNamesAreAllTheSame; + private BindingSet[] excludeSetList; + + // Index of rightArg binding sets by (variable name, value) to quickly find candidates + private Map> rightIndex; /*--------------* * Constructors * @@ -73,6 +80,7 @@ protected boolean accept(BindingSet bindingSet) { if (!initialized) { // Build set of elements-to-exclude from right argument excludeSet = makeSet(getRightArg()); + excludeSetList = excludeSet.toArray(new BindingSet[0]); excludeSetBindingNames = excludeSet.stream() .map(BindingSet::getBindingNames) .flatMap(Set::stream) @@ -85,50 +93,108 @@ protected boolean accept(BindingSet bindingSet) { return false; }); + // Build right-side index by (name,value) -> list of rows + HashMap>> tmpIndex = new HashMap<>(); + for (BindingSet bs : excludeSetList) { + for (String name : bs.getBindingNames()) { + Value v = bs.getValue(name); + if (v != null) { + tmpIndex + .computeIfAbsent(name, k -> new HashMap<>()) + .computeIfAbsent(v, k -> new ArrayList<>()) + .add(bs); + } + } + } + var built = new HashMap>(tmpIndex.size() * 2); + for (var e : tmpIndex.entrySet()) { + var inner = new HashMap(e.getValue().size() * 2); + for (var e2 : e.getValue().entrySet()) { + inner.put(e2.getKey(), e2.getValue().toArray(new BindingSet[0])); + } + built.put(e.getKey(), inner); + } + this.rightIndex = built; + initialized = true; } Set bindingNames = bindingSet.getBindingNames(); boolean hasSharedBindings = false; - if (excludeSetBindingNamesAreAllTheSame) { - for (String bindingName : excludeSetBindingNames) { - if (bindingNames.contains(bindingName)) { - hasSharedBindings = true; - break; + // Fast union check: if no variable is shared with the union of right variables, accept immediately + if (!excludeSetBindingNames.isEmpty()) { + final Set left = bindingNames; + final Set rightUnion = excludeSetBindingNames; + if (left.size() <= rightUnion.size()) { + for (String name : left) { + if (rightUnion.contains(name)) { + hasSharedBindings = true; + break; + } + } + } else { + for (String name : rightUnion) { + if (left.contains(name)) { + hasSharedBindings = true; + break; + } } } + } - if (!hasSharedBindings) { - return true; - } + if (!hasSharedBindings) { + return true; } - for (BindingSet excluded : excludeSet) { + // Use right-side index to find only candidates that match on at least one shared (name,value) + if (rightIndex != null && !rightIndex.isEmpty()) { + for (String name : bindingNames) { + Value leftVal = bindingSet.getValue(name); + if (leftVal == null) { + continue; // unbound on left does not participate in compatibility + } + Map byValue = rightIndex.get(name); + if (byValue == null) { + continue; + } + BindingSet[] candidates = byValue.get(leftVal); + if (candidates == null) { + continue; + } + for (int j = 0; j < candidates.length; j++) { + BindingSet excluded = candidates[j]; + if (excluded.isCompatible(bindingSet)) { + return false; + } + } + } + return true; + } + // Fallback: scan all (should be rare or small) + for (BindingSet excluded : excludeSetList) { if (!excludeSetBindingNamesAreAllTheSame) { hasSharedBindings = false; - for (String bindingName : excluded.getBindingNames()) { - if (bindingNames.contains(bindingName)) { - hasSharedBindings = true; - break; + final Set excludedNames = excluded.getBindingNames(); + if (bindingNames.size() <= excludedNames.size()) { + for (String name : bindingNames) { + if (excludedNames.contains(name)) { + hasSharedBindings = true; + break; + } + } + } else { + for (String name : excludedNames) { + if (bindingNames.contains(name)) { + hasSharedBindings = true; + break; + } } } - } - - // two bindingsets that share no variables are compatible by - // definition, however, the formal - // definition of SPARQL MINUS indicates that such disjoint sets should - // be filtered out. - // See http://www.w3.org/TR/sparql11-query/#sparqlAlgebra - if (hasSharedBindings) { - if (QueryResults.bindingSetsCompatible(excluded, bindingSet)) { - // at least one compatible bindingset has been found in the - // exclude set, therefore the object is compatible, therefore it - // should not be accepted. - return false; - } + if (hasSharedBindings && excluded.isCompatible(bindingSet)) { + return false; } } diff --git a/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/SPARQLMinusIterationFuzzTest.java b/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/SPARQLMinusIterationFuzzTest.java new file mode 100644 index 00000000000..4a79e7188a7 --- /dev/null +++ b/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/SPARQLMinusIterationFuzzTest.java @@ -0,0 +1,218 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + ******************************************************************************/ + +package org.eclipse.rdf4j.query.algebra.evaluation.iterator; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; + +import org.eclipse.rdf4j.common.iteration.CloseableIteration; +import org.eclipse.rdf4j.common.iteration.CloseableIteratorIteration; +import org.eclipse.rdf4j.model.BNode; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Literal; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.query.Binding; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.QueryResults; +import org.eclipse.rdf4j.query.algebra.evaluation.QueryBindingSet; +import org.junit.jupiter.api.Test; + +/** + * Randomized fuzz tests validating SPARQLMinusIteration against a reference MINUS implementation. + */ +public class SPARQLMinusIterationFuzzTest { + + private static final ValueFactory VF = SimpleValueFactory.getInstance(); + private static final String[] UNIVERSE = new String[] { "a", "b", "c", "d", "e", "x", "y", "z", "id" }; + + @Test + public void randomizedMinusParity() { + long[] seeds = new long[] { 42L, 4242L, 424242L, 7L, 123456789L }; + for (long seed : seeds) { + Random rnd = new Random(seed); + int leftSize = rnd.nextInt(15) + 5; // 5..19 + int rightSize = rnd.nextInt(15) + 5; // 5..19 + + List left = new ArrayList<>(leftSize); + List right = new ArrayList<>(rightSize); + + for (int i = 0; i < leftSize; i++) { + left.add(randomBindingSet(rnd, i)); + } + for (int i = 0; i < rightSize; i++) { + right.add(randomBindingSet(rnd, i + 1000)); + } + + Set actual = collect(replay(new SPARQLMinusIteration(iter(left), iter(right)))); + Set expected = collect(mapToKeys(referenceMinus(left, right))); + + assertThat(actual).as("minus parity for seed=" + seed).isEqualTo(expected); + } + } + + private static CloseableIteration iter(List list) { + return new CloseableIteratorIteration<>(list.iterator()); + } + + private static BindingSet randomBindingSet(Random rnd, int salt) { + // Random subset of variables + List vars = new ArrayList<>(UNIVERSE.length); + Collections.addAll(vars, UNIVERSE); + // shuffle-like selection + List selected = new ArrayList<>(UNIVERSE.length); + for (String v : vars) { + if (rnd.nextBoolean()) { + selected.add(v); + } + } + // ensure at least one var sometimes + if (selected.isEmpty() && rnd.nextBoolean()) { + selected.add(UNIVERSE[rnd.nextInt(UNIVERSE.length)]); + } + + QueryBindingSet q = new QueryBindingSet(); + for (String name : selected) { + // sometimes unbound (skip) + if (rnd.nextInt(5) == 0) { + continue; + } + q.setBinding(name, randomValueOrNull(rnd, salt + name.hashCode())); + } + // give some rows an explicit id for easier debugging + if (q.getValue("id") == null && rnd.nextInt(3) == 0) { + q.setBinding("id", VF.createIRI("urn:id:" + salt)); + } + return q; + } + + private static Value randomValueOrNull(Random rnd, int salt) { + switch (rnd.nextInt(7)) { + case 0: + return null; // unbound-like + case 1: + return VF.createIRI("urn:res:" + (salt % 13)); + case 2: + return VF.createLiteral(rnd.nextInt(5)); + case 3: + return VF.createLiteral("s" + rnd.nextInt(5)); + case 4: + return VF.createBNode("b" + Math.abs(salt % 5)); + case 5: { + // typed literal distinct from string + IRI dt = VF.createIRI("urn:dt:" + (salt % 3)); + return VF.createLiteral("v" + (salt % 7), dt); + } + default: { + // language-tagged literal + String lang = (salt % 2 == 0) ? "en" : "NO"; + return VF.createLiteral("l" + (salt % 7), lang); + } + } + } + + private static List referenceMinus(List left, List right) { + List out = new ArrayList<>(left.size()); + for (BindingSet L : left) { + boolean eliminate = false; + for (BindingSet R : right) { + if (hasSharedBoundVar(L, R) && QueryResults.bindingSetsCompatible(L, R)) { + eliminate = true; + break; + } + } + if (!eliminate) { + out.add(L); + } + } + return out; + } + + private static boolean hasSharedBoundVar(BindingSet a, BindingSet b) { + // Iterate the smaller set of bindings for efficiency + int sizeA = sizeOfBound(a); + int sizeB = sizeOfBound(b); + BindingSet first = sizeA <= sizeB ? a : b; + BindingSet second = sizeA <= sizeB ? b : a; + for (Binding binding : first) { + if (binding.getValue() == null) + continue; + if (second.getBinding(binding.getName()) != null && second.getValue(binding.getName()) != null) { + return true; + } + } + return false; + } + + private static int sizeOfBound(BindingSet bs) { + int n = 0; + for (Binding ignored : bs) { + n++; + } + return n; + } + + private static List replay(SPARQLMinusIteration it) { + List res = new ArrayList<>(); + try { + while (it.hasNext()) { + res.add(toKey(it.next())); + } + } finally { + it.close(); + } + return res; + } + + private static Set collect(List rows) { + return new HashSet<>(rows); + } + + private static List mapToKeys(List rows) { + List out = new ArrayList<>(rows.size()); + for (BindingSet bs : rows) { + out.add(toKey(bs)); + } + return out; + } + + private static String toKey(BindingSet bs) { + // Build a canonical representation: sorted by variable name, include value type info + List parts = new ArrayList<>(); + for (Binding b : bs) { + Value v = b.getValue(); + String vs; + if (v instanceof Literal) { + Literal lit = (Literal) v; + String dt = lit.getDatatype() != null ? lit.getDatatype().stringValue() : ""; + String lang = lit.getLanguage().orElse(""); + vs = "L(" + lit.getLabel() + ")^" + dt + "@" + lang; + } else if (v instanceof IRI) { + vs = "I(" + v.stringValue() + ")"; + } else if (v instanceof BNode) { + vs = "B(" + v.stringValue() + ")"; + } else { + vs = v.toString(); + } + parts.add(b.getName() + "=" + vs); + } + Collections.sort(parts); + return String.join("|", parts); + } +} diff --git a/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/SPARQLMinusIterationTest.java b/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/SPARQLMinusIterationTest.java new file mode 100644 index 00000000000..3b2c887cfb7 --- /dev/null +++ b/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/SPARQLMinusIterationTest.java @@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + ******************************************************************************/ + +package org.eclipse.rdf4j.query.algebra.evaluation.iterator; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.rdf4j.common.iteration.CloseableIteration; +import org.eclipse.rdf4j.common.iteration.CloseableIteratorIteration; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.algebra.evaluation.ArrayBindingSet; +import org.eclipse.rdf4j.query.algebra.evaluation.QueryBindingSet; +import org.junit.jupiter.api.Test; + +/** + * Behavioral tests for SPARQLMinusIteration to ensure semantics match SPARQL 1.1 MINUS. + */ +public class SPARQLMinusIterationTest { + + private static final ValueFactory VF = SimpleValueFactory.getInstance(); + + private static CloseableIteration iter(List list) { + return new CloseableIteratorIteration<>(list.iterator()); + } + + private static QueryBindingSet qbs(Object... nv) { + QueryBindingSet q = new QueryBindingSet(); + for (int i = 0; i + 1 < nv.length; i += 2) { + String name = (String) nv[i]; + Object val = nv[i + 1]; + if (val == null) { + q.setBinding(name, null); + } else if (val instanceof Integer) { + q.setBinding(name, VF.createLiteral((Integer) val)); + } else if (val instanceof String && ((String) val).startsWith("urn:")) { + q.setBinding(name, VF.createIRI((String) val)); + } else { + q.setBinding(name, VF.createLiteral(String.valueOf(val))); + } + } + return q; + } + + private static ArrayBindingSet abs(String[] names, Object... nv) { + ArrayBindingSet a = new ArrayBindingSet(names); + for (int i = 0; i + 1 < nv.length; i += 2) { + String name = (String) nv[i]; + Object val = nv[i + 1]; + if (val == null) { + a.setBinding(name, null); + } else if (val instanceof Integer) { + a.setBinding(name, VF.createLiteral((Integer) val)); + } else if (val instanceof String && ((String) val).startsWith("urn:")) { + a.setBinding(name, VF.createIRI((String) val)); + } else { + a.setBinding(name, VF.createLiteral(String.valueOf(val))); + } + } + return a; + } + + private static Set runAndCollectIds(SPARQLMinusIteration it, String idVar) { + Set ids = new HashSet<>(); + while (it.hasNext()) { + BindingSet bs = it.next(); + var v = bs.getValue(idVar); + ids.add(v == null ? "" : v.stringValue()); + } + return ids; + } + + @Test + public void emptyRight_acceptAllLeft() { + List left = Arrays.asList( + qbs("id", "urn:L1", "a", 1), + qbs("id", "urn:L2") + ); + List right = List.of(); + SPARQLMinusIteration it = new SPARQLMinusIteration(iter(left), iter(right)); + assertThat(runAndCollectIds(it, "id")).containsExactlyInAnyOrder("urn:L1", "urn:L2"); + } + + @Test + public void emptyLeft_yieldsEmpty() { + List left = List.of(); + List right = Arrays.asList(qbs("x", 1)); + SPARQLMinusIteration it = new SPARQLMinusIteration(iter(left), iter(right)); + assertThat(runAndCollectIds(it, "id")).isEmpty(); + } + + @Test + public void noSharedVariables_acceptAll() { + List left = Arrays.asList( + qbs("id", "urn:L1", "a", 1), + qbs("id", "urn:L2", "b", 2) + ); + List right = Arrays.asList( + qbs("x", "urn:R1"), + qbs("y", 3) + ); + SPARQLMinusIteration it = new SPARQLMinusIteration(iter(left), iter(right)); + assertThat(runAndCollectIds(it, "id")).containsExactlyInAnyOrder("urn:L1", "urn:L2"); + } + + @Test + public void sharedVar_conflictingValues_accept() { + List left = Arrays.asList( + qbs("id", "urn:L1", "a", 1), + qbs("id", "urn:L2", "a", 2) + ); + List right = Arrays.asList(qbs("a", 3)); + SPARQLMinusIteration it = new SPARQLMinusIteration(iter(left), iter(right)); + assertThat(runAndCollectIds(it, "id")).containsExactlyInAnyOrder("urn:L1", "urn:L2"); + } + + @Test + public void sharedVar_matchingValue_reject() { + List left = Arrays.asList( + qbs("id", "urn:L1", "a", 1), + qbs("id", "urn:L2", "a", 2) + ); + List right = Arrays.asList(qbs("a", 2)); + SPARQLMinusIteration it = new SPARQLMinusIteration(iter(left), iter(right)); + assertThat(runAndCollectIds(it, "id")).containsExactlyInAnyOrder("urn:L1"); + } + + @Test + public void multipleSharedVars_requireAllMatchToReject() { + List left = Arrays.asList( + qbs("id", "urn:L1", "a", 1, "b", 2), + qbs("id", "urn:L2", "a", 1, "b", 3) + ); + List right = Arrays.asList( + qbs("a", 1, "b", 2), // should reject L1 + qbs("a", 1, "b", 9) // does not reject L2 (b differs) + ); + SPARQLMinusIteration it = new SPARQLMinusIteration(iter(left), iter(right)); + assertThat(runAndCollectIds(it, "id")).containsExactlyInAnyOrder("urn:L2"); + } + + @Test + public void leftNullValue_treatedAsUnbound_notRejected() { + List left = Arrays.asList( + qbs("id", "urn:L1", "a", null), // a is set but null + qbs("id", "urn:L2", "a", 2) + ); + List right = Arrays.asList(qbs("a", 2)); + SPARQLMinusIteration it = new SPARQLMinusIteration(iter(left), iter(right)); + assertThat(runAndCollectIds(it, "id")).containsExactlyInAnyOrder("urn:L1"); + } + + @Test + public void arrayBindingSet_compatibleAndDisjoint() { + String[] names = new String[] { "id", "a", "b" }; + List left = Arrays.asList( + abs(names, "id", "urn:L1", "a", 1, "b", 2), + abs(names, "id", "urn:L2", "a", 5) + ); + List right = Arrays.asList( + abs(names, "a", 1), // overlaps a=1 => reject L1 + abs(names, "x", 9) // disjoint with both => should not reject + ); + SPARQLMinusIteration it = new SPARQLMinusIteration(iter(left), iter(right)); + assertThat(runAndCollectIds(it, "id")).containsExactlyInAnyOrder("urn:L2"); + } + + @Test + public void unionNoOverlap_acceptFastPath() { + // Right union names = {a,b}; left rows only have {z} + List left = Arrays.asList(qbs("id", "urn:L1", "z", 1)); + List right = Arrays.asList(qbs("a", 1), qbs("b", 2)); + SPARQLMinusIteration it = new SPARQLMinusIteration(iter(left), iter(right)); + assertThat(runAndCollectIds(it, "id")).containsExactly("urn:L1"); + } + + @Test + public void duplicateRightRows_doNotChangeResult() { + List left = Arrays.asList(qbs("id", "urn:L1", "a", 1), qbs("id", "urn:L2", "a", 2)); + List right = Arrays.asList(qbs("a", 2), qbs("a", 2)); + SPARQLMinusIteration it = new SPARQLMinusIteration(iter(left), iter(right)); + assertThat(runAndCollectIds(it, "id")).containsExactly("urn:L1"); + } +} From f10929e51874eb5a6ffebfb8600367c4b3a692a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Thu, 2 Oct 2025 10:54:05 +0200 Subject: [PATCH 24/46] GH-5447 improve performance of statement iterator --- ...tatementConvertorWithoutBindingChecks.java | 158 ++++++++++++++++++ .../StatementPatternQueryEvaluationStep.java | 121 ++++++++++++-- ...atementPatternQueryEvaluationStepTest.java | 133 +++++++++++++++ 3 files changed, 399 insertions(+), 13 deletions(-) create mode 100644 core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/StatementConvertorWithoutBindingChecks.java create mode 100644 core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/StatementPatternQueryEvaluationStepTest.java diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/StatementConvertorWithoutBindingChecks.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/StatementConvertorWithoutBindingChecks.java new file mode 100644 index 00000000000..bcc0b654798 --- /dev/null +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/StatementConvertorWithoutBindingChecks.java @@ -0,0 +1,158 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ + +package org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps; + +import java.util.function.BiConsumer; + +import org.eclipse.rdf4j.model.Statement; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.query.MutableBindingSet; +import org.eclipse.rdf4j.query.algebra.Var; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext; + +class StatementConvertorWithoutBindingChecks { + + private StatementConvertorWithoutBindingChecks() { + } + + public static BiConsumer s(QueryEvaluationContext context, Var s) { + BiConsumer setS = context.addBinding(s.getName()); + return (result, st) -> setS.accept(st.getSubject(), result); + } + + public static BiConsumer p(QueryEvaluationContext context, Var p) { + BiConsumer setP = context.addBinding(p.getName()); + return (result, st) -> setP.accept(st.getPredicate(), result); + } + + public static BiConsumer o(QueryEvaluationContext context, Var o) { + BiConsumer setO = context.addBinding(o.getName()); + return (result, st) -> setO.accept(st.getObject(), result); + } + + public static BiConsumer c(QueryEvaluationContext context, Var c) { + BiConsumer setC = context.addBinding(c.getName()); + return (result, st) -> setC.accept(st.getContext(), result); + } + + public static BiConsumer sp(QueryEvaluationContext context, Var s, Var p) { + BiConsumer setS = context.addBinding(s.getName()); + BiConsumer setP = context.addBinding(p.getName()); + return (result, st) -> { + setS.accept(st.getSubject(), result); + setP.accept(st.getPredicate(), result); + }; + } + + public static BiConsumer so(QueryEvaluationContext context, Var s, Var o) { + BiConsumer setS = context.addBinding(s.getName()); + BiConsumer setO = context.addBinding(o.getName()); + return (result, st) -> { + setS.accept(st.getSubject(), result); + setO.accept(st.getObject(), result); + }; + } + + public static BiConsumer sc(QueryEvaluationContext context, Var s, Var c) { + BiConsumer setS = context.addBinding(s.getName()); + BiConsumer setC = context.addBinding(c.getName()); + return (result, st) -> { + setS.accept(st.getSubject(), result); + setC.accept(st.getContext(), result); + }; + } + + public static BiConsumer po(QueryEvaluationContext context, Var p, Var o) { + BiConsumer setP = context.addBinding(p.getName()); + BiConsumer setO = context.addBinding(o.getName()); + return (result, st) -> { + setP.accept(st.getPredicate(), result); + setO.accept(st.getObject(), result); + }; + } + + public static BiConsumer pc(QueryEvaluationContext context, Var p, Var c) { + BiConsumer setP = context.addBinding(p.getName()); + BiConsumer setC = context.addBinding(c.getName()); + return (result, st) -> { + setP.accept(st.getPredicate(), result); + setC.accept(st.getContext(), result); + }; + } + + public static BiConsumer oc(QueryEvaluationContext context, Var o, Var c) { + BiConsumer setO = context.addBinding(o.getName()); + BiConsumer setC = context.addBinding(c.getName()); + return (result, st) -> { + setO.accept(st.getObject(), result); + setC.accept(st.getContext(), result); + }; + } + + public static BiConsumer spo(QueryEvaluationContext context, Var s, Var p, Var o) { + BiConsumer setS = context.addBinding(s.getName()); + BiConsumer setP = context.addBinding(p.getName()); + BiConsumer setO = context.addBinding(o.getName()); + return (result, st) -> { + setS.accept(st.getSubject(), result); + setP.accept(st.getPredicate(), result); + setO.accept(st.getObject(), result); + }; + } + + public static BiConsumer spc(QueryEvaluationContext context, Var s, Var p, Var c) { + BiConsumer setS = context.addBinding(s.getName()); + BiConsumer setP = context.addBinding(p.getName()); + BiConsumer setC = context.addBinding(c.getName()); + return (result, st) -> { + setS.accept(st.getSubject(), result); + setP.accept(st.getPredicate(), result); + setC.accept(st.getContext(), result); + }; + } + + public static BiConsumer soc(QueryEvaluationContext context, Var s, Var o, Var c) { + BiConsumer setS = context.addBinding(s.getName()); + BiConsumer setO = context.addBinding(o.getName()); + BiConsumer setC = context.addBinding(c.getName()); + return (result, st) -> { + setS.accept(st.getSubject(), result); + setO.accept(st.getObject(), result); + setC.accept(st.getContext(), result); + }; + } + + public static BiConsumer poc(QueryEvaluationContext context, Var p, Var o, Var c) { + BiConsumer setP = context.addBinding(p.getName()); + BiConsumer setO = context.addBinding(o.getName()); + BiConsumer setC = context.addBinding(c.getName()); + return (result, st) -> { + setP.accept(st.getPredicate(), result); + setO.accept(st.getObject(), result); + setC.accept(st.getContext(), result); + }; + } + + public static BiConsumer spoc(QueryEvaluationContext context, Var s, Var p, Var o, + Var c) { + BiConsumer setS = context.addBinding(s.getName()); + BiConsumer setP = context.addBinding(p.getName()); + BiConsumer setO = context.addBinding(o.getName()); + BiConsumer setC = context.addBinding(c.getName()); + return (result, st) -> { + setS.accept(st.getSubject(), result); + setP.accept(st.getPredicate(), result); + setO.accept(st.getObject(), result); + setC.accept(st.getContext(), result); + }; + } +} diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/StatementPatternQueryEvaluationStep.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/StatementPatternQueryEvaluationStep.java index f816aea617b..c9e525bd172 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/StatementPatternQueryEvaluationStep.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/StatementPatternQueryEvaluationStep.java @@ -53,7 +53,8 @@ public class StatementPatternQueryEvaluationStep implements QueryEvaluationStep private final TripleSource tripleSource; private final boolean emptyGraph; private final Function contextSup; - private final BiConsumer converter; + private BiConsumer converter; + private BiConsumer convertStatementConverter; private final QueryEvaluationContext context; private final StatementOrder order; @@ -64,6 +65,11 @@ public class StatementPatternQueryEvaluationStep implements QueryEvaluationStep private final Function getPredicateVar; private final Function getObjectVar; + private final Var normalizedSubjectVar; + private final Var normalizedPredicateVar; + private final Var normalizedObjectVar; + private final Var normalizedContextVar; + // We try to do as much work as possible in the constructor. // With the aim of making the evaluate method as cheap as possible. public StatementPatternQueryEvaluationStep(StatementPattern statementPattern, QueryEvaluationContext context, @@ -137,9 +143,13 @@ public StatementPatternQueryEvaluationStep(StatementPattern statementPattern, Qu } } - converter = makeConverter(context, subjVar, predVar, objVar, conVar); + normalizedSubjectVar = subjVar; + normalizedPredicateVar = predVar; + normalizedObjectVar = objVar; + normalizedContextVar = conVar; - unboundTest = getUnboundTest(context, subjVar, predVar, objVar, conVar); + unboundTest = getUnboundTest(context, normalizedSubjectVar, normalizedPredicateVar, normalizedObjectVar, + normalizedContextVar); } @@ -276,7 +286,7 @@ private JoinStatementWithBindingSetIterator getIteration(BindingSet bindings) { iteration = handleFilter(contexts, (Resource) subject, (IRI) predicate, object, iteration); // Return an iterator that converts the statements to var bindings - return new JoinStatementWithBindingSetIterator(iteration, converter, bindings, context); + return new JoinStatementWithBindingSetIterator(iteration, getConverter(), bindings, context); } catch (Throwable t) { if (iteration != null) { iteration.close(); @@ -328,7 +338,7 @@ private ConvertStatementToBindingSetIterator getIteration() { iteration = handleFilter(contexts, (Resource) subject, (IRI) predicate, object, iteration); // Return an iterator that converts the statements to var bindings - return new ConvertStatementToBindingSetIterator(iteration, converter, context); + return new ConvertStatementToBindingSetIterator(iteration, getConvertStatementConverter(), context); } catch (Throwable t) { if (iteration != null) { iteration.close(); @@ -340,6 +350,36 @@ private ConvertStatementToBindingSetIterator getIteration() { } } + private BiConsumer getConverter() { + BiConsumer localConverter = converter; + if (localConverter == null) { + synchronized (this) { + localConverter = converter; + if (localConverter == null) { + localConverter = makeConverter(context, normalizedSubjectVar, normalizedPredicateVar, + normalizedObjectVar, normalizedContextVar); + converter = localConverter; + } + } + } + return localConverter; + } + + private BiConsumer getConvertStatementConverter() { + BiConsumer localConverter = convertStatementConverter; + if (localConverter == null) { + synchronized (this) { + localConverter = convertStatementConverter; + if (localConverter == null) { + localConverter = makeConvertStatementConverter(context, normalizedSubjectVar, + normalizedPredicateVar, normalizedObjectVar, normalizedContextVar); + convertStatementConverter = localConverter; + } + } + } + return localConverter; + } + private CloseableIteration handleFilter(Resource[] contexts, Resource subject, IRI predicate, Value object, CloseableIteration iteration) { @@ -526,23 +566,23 @@ private static Resource[] fillContextsFromDatasSetGraphs(Set graphs) { private static final class ConvertStatementToBindingSetIterator implements CloseableIteration { - private final BiConsumer action; + private final BiConsumer converter; private final QueryEvaluationContext context; private final CloseableIteration iteration; private boolean closed = false; private ConvertStatementToBindingSetIterator( CloseableIteration iteration, - BiConsumer action, QueryEvaluationContext context) { + BiConsumer converter, QueryEvaluationContext context) { assert iteration != null; this.iteration = iteration; - this.action = action; + this.converter = converter; this.context = context; } private BindingSet convert(Statement st) { MutableBindingSet made = context.createBindingSet(); - action.accept(made, st); + converter.accept(made, st); return made; } @@ -573,7 +613,7 @@ public void close() throws QueryEvaluationException { private static final class JoinStatementWithBindingSetIterator implements CloseableIteration { - private final BiConsumer action; + private final BiConsumer converter; private final QueryEvaluationContext context; private final BindingSet bindings; private final CloseableIteration iteration; @@ -581,11 +621,12 @@ private static final class JoinStatementWithBindingSetIterator private JoinStatementWithBindingSetIterator( CloseableIteration iteration, - BiConsumer action, BindingSet bindings, QueryEvaluationContext context) { + BiConsumer converter, BindingSet bindings, + QueryEvaluationContext context) { assert iteration != null; this.iteration = iteration; assert !bindings.isEmpty(); - this.action = action; + this.converter = converter; this.context = context; this.bindings = bindings; @@ -593,7 +634,7 @@ private JoinStatementWithBindingSetIterator( private BindingSet convert(Statement st) { MutableBindingSet made = context.createBindingSet(bindings); - action.accept(made, st); + converter.accept(made, st); return made; } @@ -683,6 +724,60 @@ private static BiConsumer makeConverter(QueryEvalu } + private static BiConsumer makeConvertStatementConverter( + QueryEvaluationContext context, + Var s, Var p, Var o, Var c) { + + if (s != null && !s.isConstant()) { + if (p != null && !p.isConstant()) { + if (o != null && !o.isConstant()) { + if (c != null && !c.isConstant()) { + return StatementConvertorWithoutBindingChecks.spoc(context, s, p, o, c); + } else { + return StatementConvertorWithoutBindingChecks.spo(context, s, p, o); + } + } else if (c != null && !c.isConstant()) { + return StatementConvertorWithoutBindingChecks.spc(context, s, p, c); + } else { + return StatementConvertorWithoutBindingChecks.sp(context, s, p); + } + } else if (o != null && !o.isConstant()) { + if (c != null && !c.isConstant()) { + return StatementConvertorWithoutBindingChecks.soc(context, s, o, c); + } else { + return StatementConvertorWithoutBindingChecks.so(context, s, o); + } + } else if (c != null && !c.isConstant()) { + return StatementConvertorWithoutBindingChecks.sc(context, s, c); + } else { + return StatementConvertorWithoutBindingChecks.s(context, s); + } + } else if (p != null && !p.isConstant()) { + if (o != null && !o.isConstant()) { + if (c != null && !c.isConstant()) { + return StatementConvertorWithoutBindingChecks.poc(context, p, o, c); + } else { + return StatementConvertorWithoutBindingChecks.po(context, p, o); + } + } else if (c != null && !c.isConstant()) { + return StatementConvertorWithoutBindingChecks.pc(context, p, c); + } else { + return StatementConvertorWithoutBindingChecks.p(context, p); + } + } else if (o != null && !o.isConstant()) { + if (c != null && !c.isConstant()) { + return StatementConvertorWithoutBindingChecks.oc(context, o, c); + } else { + return StatementConvertorWithoutBindingChecks.o(context, o); + } + } else if (c != null && !c.isConstant()) { + return StatementConvertorWithoutBindingChecks.c(context, c); + } + + return (a, b) -> { + }; + } + private static Predicate andThen(Predicate pred, Predicate and) { if (pred == null) { return and; diff --git a/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/StatementPatternQueryEvaluationStepTest.java b/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/StatementPatternQueryEvaluationStepTest.java new file mode 100644 index 00000000000..199cd1b5d68 --- /dev/null +++ b/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/StatementPatternQueryEvaluationStepTest.java @@ -0,0 +1,133 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; + +import org.eclipse.rdf4j.common.iteration.CloseableIteration; +import org.eclipse.rdf4j.common.iteration.CloseableIteratorIteration; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Resource; +import org.eclipse.rdf4j.model.Statement; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.Dataset; +import org.eclipse.rdf4j.query.MutableBindingSet; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.algebra.StatementPattern; +import org.eclipse.rdf4j.query.algebra.Var; +import org.eclipse.rdf4j.query.algebra.evaluation.TripleSource; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.StatementPatternQueryEvaluationStep; +import org.junit.jupiter.api.Test; + +class StatementPatternQueryEvaluationStepTest { + + @Test + void convertIterationSkipsBindingChecks() { + InstrumentedQueryEvaluationContext context = new InstrumentedQueryEvaluationContext(); + SingleStatementTripleSource tripleSource = new SingleStatementTripleSource(); + StatementPattern statementPattern = new StatementPattern(new Var("s"), new Var("p"), new Var("o")); + StatementPatternQueryEvaluationStep evaluationStep = new StatementPatternQueryEvaluationStep( + statementPattern, + context, + tripleSource); + + try (CloseableIteration iteration = evaluationStep.evaluate(context.createBindingSet())) { + assertThat(iteration.hasNext()).isTrue(); + BindingSet converted = iteration.next(); + assertThat(converted).isInstanceOf(InstrumentedBindingSet.class); + InstrumentedBindingSet bindingSet = (InstrumentedBindingSet) converted; + assertThat(bindingSet.wasIsEmptyInvoked()).isFalse(); + assertThat(bindingSet.getBindingNames()) + .containsExactlyInAnyOrder("s", "p", "o"); + assertThat(bindingSet.getValue("s")).isEqualTo(tripleSource.statement.getSubject()); + assertThat(bindingSet.getValue("p")).isEqualTo(tripleSource.statement.getPredicate()); + assertThat(bindingSet.getValue("o")).isEqualTo(tripleSource.statement.getObject()); + assertThat(iteration.hasNext()).isFalse(); + } + + assertThat(context.bindingChecks.get()).isZero(); + } + + private static final class InstrumentedQueryEvaluationContext implements QueryEvaluationContext { + + private final AtomicInteger bindingChecks = new AtomicInteger(); + + @Override + public Predicate hasBinding(String variableName) { + return bindings -> { + bindingChecks.incrementAndGet(); + return bindings.hasBinding(variableName); + }; + } + + @Override + public MutableBindingSet createBindingSet() { + return new InstrumentedBindingSet(); + } + + @Override + public Dataset getDataset() { + return null; + } + + @Override + public org.eclipse.rdf4j.model.Literal getNow() { + return null; + } + } + + private static final class InstrumentedBindingSet + extends org.eclipse.rdf4j.query.algebra.evaluation.QueryBindingSet { + + private boolean isEmptyInvoked = false; + + private InstrumentedBindingSet() { + } + + @Override + public boolean isEmpty() { + isEmptyInvoked = true; + return super.isEmpty(); + } + + private boolean wasIsEmptyInvoked() { + return isEmptyInvoked; + } + } + + private static final class SingleStatementTripleSource implements TripleSource { + + private final ValueFactory valueFactory = SimpleValueFactory.getInstance(); + private final Statement statement = valueFactory.createStatement( + valueFactory.createIRI("urn:subj"), + valueFactory.createIRI("urn:pred"), + valueFactory.createLiteral("obj")); + + @Override + public CloseableIteration getStatements(Resource subj, IRI pred, Value obj, + Resource... contexts) throws QueryEvaluationException { + return new CloseableIteratorIteration<>(List.of(statement).iterator()); + } + + @Override + public ValueFactory getValueFactory() { + return valueFactory; + } + } +} From 5ae6ddf903ac97ec4ecb56e39d4d84401cf3cecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Thu, 2 Oct 2025 10:54:25 +0200 Subject: [PATCH 25/46] unrelated test --- .../text/tsv/SPARQLTSVCustomTest.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/core/queryresultio/text/src/test/java/org/eclipse/rdf4j/query/resultio/text/tsv/SPARQLTSVCustomTest.java b/core/queryresultio/text/src/test/java/org/eclipse/rdf4j/query/resultio/text/tsv/SPARQLTSVCustomTest.java index a79915dc81d..a24e07083fb 100644 --- a/core/queryresultio/text/src/test/java/org/eclipse/rdf4j/query/resultio/text/tsv/SPARQLTSVCustomTest.java +++ b/core/queryresultio/text/src/test/java/org/eclipse/rdf4j/query/resultio/text/tsv/SPARQLTSVCustomTest.java @@ -79,6 +79,17 @@ public void testQuotedXSDStringLiteral() throws Exception { assertEquals("?test\n\"example\"\n", result); } + @Test + public void testQuotedXSDStringLiteralLang() throws Exception { + List bindingNames = List.of("test"); + TupleQueryResult tqr = new IteratingTupleQueryResult(bindingNames, + List.of(new ListBindingSet(bindingNames, + SimpleValueFactory.getInstance().createLiteral("example", "en")))); + String result = writeTupleResult(tqr); + assertEquals("?test\n" + + "\"example\"@en\n", result); + } + @Test public void testQuotedXSDStringLiteralWithSpecialCharacters() throws Exception { List bindingNames = List.of("test"); @@ -89,6 +100,17 @@ public void testQuotedXSDStringLiteralWithSpecialCharacters() throws Exception { assertEquals("?test\n\"example\\twith\\nspecial\\\"characters\"\n", result); } + @Test + public void testIRI() throws Exception { + List bindingNames = List.of("test"); + TupleQueryResult tqr = new IteratingTupleQueryResult(bindingNames, + List.of(new ListBindingSet(bindingNames, + SimpleValueFactory.getInstance().createIRI("http://example.org/1")))); + String result = writeTupleResult(tqr); + assertEquals("?test\n" + + "\n", result); + } + private String writeTupleResult(TupleQueryResult tqr) throws IOException, TupleQueryResultHandlerException, QueryEvaluationException { ByteArrayOutputStream output = new ByteArrayOutputStream(); From 295b06f6d2eed504befc3653522b0cd8e9a88654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Thu, 2 Oct 2025 10:57:23 +0200 Subject: [PATCH 26/46] GH-5447 general LMDB optimisation --- .../rdf4j/sail/lmdb/LmdbRecordIterator.java | 44 +- .../rdf4j/sail/lmdb/LmdbSailStore.java | 21 +- .../sail/lmdb/LmdbStatementIterator.java | 55 +- .../eclipse/rdf4j/sail/lmdb/TripleStore.java | 205 ++++-- .../eclipse/rdf4j/sail/lmdb/ValueStore.java | 192 +++++- .../org/eclipse/rdf4j/sail/lmdb/Varint.java | 296 +++++++-- .../eclipse/rdf4j/sail/lmdb/util/Bytes.java | 610 ++++++++++++++++++ .../rdf4j/sail/lmdb/util/GroupMatcher.java | 424 ++++++++++++ .../rdf4j/sail/lmdb/util/IndexKeyWriters.java | 497 ++++++++++++++ .../sail/lmdb/util/SignificantBytesBE.java | 95 +++ .../rdf4j/sail/lmdb/util/package-info.java | 21 + 11 files changed, 2348 insertions(+), 112 deletions(-) create mode 100644 core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/Bytes.java create mode 100644 core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/GroupMatcher.java create mode 100644 core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/IndexKeyWriters.java create mode 100644 core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/SignificantBytesBE.java create mode 100644 core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/package-info.java diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbRecordIterator.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbRecordIterator.java index 197c68deb5f..68c5352a73b 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbRecordIterator.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbRecordIterator.java @@ -29,7 +29,7 @@ import org.eclipse.rdf4j.sail.SailException; import org.eclipse.rdf4j.sail.lmdb.TripleStore.TripleIndex; import org.eclipse.rdf4j.sail.lmdb.TxnManager.Txn; -import org.eclipse.rdf4j.sail.lmdb.Varint.GroupMatcher; +import org.eclipse.rdf4j.sail.lmdb.util.GroupMatcher; import org.lwjgl.PointerBuffer; import org.lwjgl.system.MemoryStack; import org.lwjgl.util.lmdb.MDBVal; @@ -45,11 +45,17 @@ class LmdbRecordIterator implements RecordIterator { private final TripleIndex index; + private final long subj; + private final long pred; + private final long obj; + private final long context; + private final long cursor; private final MDBVal maxKey; - private final GroupMatcher groupMatcher; + private final boolean matchValues; + private GroupMatcher groupMatcher; private final Txn txnRef; @@ -71,7 +77,8 @@ class LmdbRecordIterator implements RecordIterator { private int lastResult; - private final long[] quad = new long[4]; + private final long[] quad; + private final long[] originalQuad; private boolean fetchNext = false; @@ -81,6 +88,12 @@ class LmdbRecordIterator implements RecordIterator { LmdbRecordIterator(TripleIndex index, boolean rangeSearch, long subj, long pred, long obj, long context, boolean explicit, Txn txnRef) throws IOException { + this.subj = subj; + this.pred = pred; + this.obj = obj; + this.context = context; + this.originalQuad = new long[] { subj, pred, obj, context }; + this.quad = new long[] { subj, pred, obj, context }; this.pool = Pool.get(); this.keyData = pool.getVal(); this.valueData = pool.getVal(); @@ -100,12 +113,8 @@ class LmdbRecordIterator implements RecordIterator { this.maxKey = null; } - boolean matchValues = subj > 0 || pred > 0 || obj > 0 || context >= 0; - if (matchValues) { - this.groupMatcher = index.createMatcher(subj, pred, obj, context); - } else { - this.groupMatcher = null; - } + this.matchValues = subj > 0 || pred > 0 || obj > 0 || context >= 0; + this.dbi = index.getDB(explicit); this.txnRef = txnRef; this.txnLockManager = txnRef.lockManager(); @@ -145,6 +154,7 @@ public long[] next() { } if (txnRefVersion != txnRef.version()) { + // TODO: None of the tests in the LMDB Store cover this case! // cursor must be renewed mdb_cursor_renew(txn, cursor); if (fetchNext) { @@ -188,12 +198,12 @@ public long[] next() { // if (maxKey != null && TripleStore.COMPARATOR.compare(keyData.mv_data(), maxKey.mv_data()) > 0) { if (maxKey != null && mdb_cmp(txn, dbi, keyData, maxKey) > 0) { lastResult = MDB_NOTFOUND; - } else if (groupMatcher != null && !groupMatcher.matches(keyData.mv_data())) { + } else if (matches()) { // value doesn't match search key/mask, fetch next value lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_NEXT); } else { // Matching value found - index.keyToQuad(keyData.mv_data(), quad); + index.keyToQuad(keyData.mv_data(), originalQuad, quad); // fetch next value fetchNext = true; return quad; @@ -206,6 +216,18 @@ public long[] next() { } } + private boolean matches() { + + if (groupMatcher != null) { + return !this.groupMatcher.matches(keyData.mv_data()); + } else if (matchValues) { + this.groupMatcher = index.createMatcher(subj, pred, obj, context); + return !this.groupMatcher.matches(keyData.mv_data()); + } else { + return false; + } + } + private void closeInternal(boolean maybeCalledAsync) { if (!closed) { long writeStamp = 0L; diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailStore.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailStore.java index 90212ad598b..6d87d4bc33e 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailStore.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailStore.java @@ -29,7 +29,6 @@ import org.eclipse.rdf4j.common.iteration.CloseableIteration; import org.eclipse.rdf4j.common.iteration.CloseableIteratorIteration; import org.eclipse.rdf4j.common.iteration.ConvertingIteration; -import org.eclipse.rdf4j.common.iteration.EmptyIteration; import org.eclipse.rdf4j.common.iteration.FilterIteration; import org.eclipse.rdf4j.common.iteration.UnionIteration; import org.eclipse.rdf4j.common.order.StatementOrder; @@ -72,6 +71,7 @@ class LmdbSailStore implements SailStore { private boolean multiThreadingActive; private volatile boolean asyncTransactionFinished; private volatile boolean nextTransactionAsync; + private volatile boolean mayHaveInferred; boolean enableMultiThreading = true; @@ -143,6 +143,9 @@ class AddQuadOperation implements Operation { @Override public void execute() throws IOException { + if (!explicit) { + mayHaveInferred = true; + } if (!unusedIds.isEmpty()) { // these ids are used again unusedIds.remove(s); @@ -191,8 +194,10 @@ public LmdbSailStore(File dataDir, LmdbStoreConfig config) throws IOException, S boolean initialized = false; try { namespaceStore = new NamespaceStore(dataDir); - valueStore = new ValueStore(new File(dataDir, "values"), config); - tripleStore = new TripleStore(new File(dataDir, "triples"), config); + var valueStore = new ValueStore(new File(dataDir, "values"), config); + this.valueStore = valueStore; + tripleStore = new TripleStore(new File(dataDir, "triples"), config, valueStore); + mayHaveInferred = tripleStore.hasTriples(false); initialized = true; } finally { if (!initialized) { @@ -348,11 +353,15 @@ protected void handleClose() throws SailException { */ CloseableIteration createStatementIterator( Txn txn, Resource subj, IRI pred, Value obj, boolean explicit, Resource... contexts) throws IOException { + if (!explicit && !mayHaveInferred) { + // there are no inferred statements and the iterator should only return inferred statements + return CloseableIteration.EMPTY_STATEMENT_ITERATION; + } long subjID = LmdbValue.UNKNOWN_ID; if (subj != null) { subjID = valueStore.getId(subj); if (subjID == LmdbValue.UNKNOWN_ID) { - return new EmptyIteration<>(); + return CloseableIteration.EMPTY_STATEMENT_ITERATION; } } @@ -360,7 +369,7 @@ CloseableIteration createStatementIterator( if (pred != null) { predID = valueStore.getId(pred); if (predID == LmdbValue.UNKNOWN_ID) { - return new EmptyIteration<>(); + return CloseableIteration.EMPTY_STATEMENT_ITERATION; } } @@ -369,7 +378,7 @@ CloseableIteration createStatementIterator( objID = valueStore.getId(obj); if (objID == LmdbValue.UNKNOWN_ID) { - return new EmptyIteration<>(); + return CloseableIteration.EMPTY_STATEMENT_ITERATION; } } diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStatementIterator.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStatementIterator.java index 5fccf7113a1..e4b6429afa8 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStatementIterator.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStatementIterator.java @@ -11,8 +11,9 @@ package org.eclipse.rdf4j.sail.lmdb; import java.io.IOException; +import java.util.NoSuchElementException; -import org.eclipse.rdf4j.common.iteration.LookAheadIteration; +import org.eclipse.rdf4j.common.iteration.AbstractCloseableIteration; import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Resource; import org.eclipse.rdf4j.model.Statement; @@ -23,7 +24,7 @@ * A statement iterator that wraps a RecordIterator containing statement records and translates these records to * {@link Statement} objects. */ -class LmdbStatementIterator extends LookAheadIteration { +class LmdbStatementIterator extends AbstractCloseableIteration { /*-----------* * Variables * @@ -32,6 +33,7 @@ class LmdbStatementIterator extends LookAheadIteration { private final RecordIterator recordIt; private final ValueStore valueStore; + private Statement nextElement; /*--------------* * Constructors * @@ -49,7 +51,6 @@ public LmdbStatementIterator(RecordIterator recordIt, ValueStore valueStore) { * Methods * *---------*/ - @Override public Statement getNextElement() throws SailException { try { long[] quad = recordIt.next(); @@ -86,4 +87,52 @@ protected void handleClose() throws SailException { private SailException causeIOException(IOException e) { return new SailException(e); } + + @Override + public final boolean hasNext() { + if (isClosed()) { + return false; + } + + return lookAhead() != null; + } + + @Override + public final Statement next() { + if (isClosed()) { + throw new NoSuchElementException("The iteration has been closed."); + } + Statement result = lookAhead(); + + if (result != null) { + nextElement = null; + return result; + } else { + throw new NoSuchElementException(); + } + } + + /** + * Fetches the next element if it hasn't been fetched yet and stores it in {@link #nextElement}. + * + * @return The next element, or null if there are no more results. + */ + private Statement lookAhead() { + if (nextElement == null) { + nextElement = getNextElement(); + + if (nextElement == null) { + close(); + } + } + return nextElement; + } + + /** + * Throws an {@link UnsupportedOperationException}. + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } } diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/TripleStore.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/TripleStore.java index a39762135f1..7dfd8791a03 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/TripleStore.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/TripleStore.java @@ -14,8 +14,7 @@ import static org.eclipse.rdf4j.sail.lmdb.LmdbUtil.openDatabase; import static org.eclipse.rdf4j.sail.lmdb.LmdbUtil.readTransaction; import static org.eclipse.rdf4j.sail.lmdb.LmdbUtil.transaction; -import static org.eclipse.rdf4j.sail.lmdb.Varint.readListUnsigned; -import static org.eclipse.rdf4j.sail.lmdb.Varint.writeUnsigned; +import static org.eclipse.rdf4j.sail.lmdb.Varint.readQuadUnsigned; import static org.lwjgl.system.MemoryStack.stackPush; import static org.lwjgl.system.MemoryUtil.NULL; import static org.lwjgl.util.lmdb.LMDB.MDB_CREATE; @@ -75,7 +74,8 @@ import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; -import java.util.concurrent.locks.StampedLock; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.LongAdder; import java.util.function.Consumer; import org.eclipse.rdf4j.common.concurrent.locks.StampedLongAdderLockManager; @@ -84,8 +84,9 @@ import org.eclipse.rdf4j.sail.lmdb.TxnManager.Txn; import org.eclipse.rdf4j.sail.lmdb.TxnRecordCache.Record; import org.eclipse.rdf4j.sail.lmdb.TxnRecordCache.RecordCacheIterator; -import org.eclipse.rdf4j.sail.lmdb.Varint.GroupMatcher; import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; +import org.eclipse.rdf4j.sail.lmdb.util.GroupMatcher; +import org.eclipse.rdf4j.sail.lmdb.util.IndexKeyWriters; import org.lwjgl.PointerBuffer; import org.lwjgl.system.MemoryStack; import org.lwjgl.util.lmdb.MDBEnvInfo; @@ -103,6 +104,11 @@ @SuppressWarnings("deprecation") class TripleStore implements Closeable { + static ConcurrentHashMap stats = new ConcurrentHashMap<>(); + static long hit = 0; + static long fullHit = 0; + static long miss = 0; + /*-----------* * Constants * *-----------*/ @@ -154,6 +160,7 @@ class TripleStore implements Closeable { * The list of triple indexes that are used to store and retrieve triples. */ private final List indexes = new ArrayList<>(); + private final ValueStore valueStore; private long env; private int contextsDbi; @@ -187,10 +194,11 @@ public int compareRegion(ByteBuffer array1, int startIdx1, ByteBuffer array2, in } }; - TripleStore(File dir, LmdbStoreConfig config) throws IOException, SailException { + TripleStore(File dir, LmdbStoreConfig config, ValueStore valueStore) throws IOException, SailException { this.dir = dir; this.forceSync = config.getForceSync(); this.autoGrow = config.getAutoGrow(); + this.valueStore = valueStore; // create directory if it not exists this.dir.mkdirs(); @@ -508,6 +516,15 @@ public RecordIterator getTriples(Txn txn, long subj, long pred, long obj, long c return getTriplesUsingIndex(txn, subj, pred, obj, context, explicit, index, doRangeSearch); } + boolean hasTriples(boolean explicit) throws IOException { + TripleIndex mainIndex = indexes.get(0); + return txnManager.doWith((stack, txn) -> { + MDBStat stat = MDBStat.mallocStack(stack); + mdb_stat(txn, mainIndex.getDB(explicit), stat); + return stat.ms_entries() > 0; + }); + } + private RecordIterator getTriplesUsingIndex(Txn txn, long subj, long pred, long obj, long context, boolean explicit, TripleIndex index, boolean rangeSearch) throws IOException { return new LmdbRecordIterator(index, rangeSearch, subj, pred, obj, context, explicit, txn); @@ -1163,11 +1180,15 @@ private void storeProperties(File propFile) throws IOException { class TripleIndex { private final char[] fieldSeq; + private final IndexKeyWriters.KeyWriter keyWriter; + private final IndexKeyWriters.MatcherFactory matcherFactory; private final int dbiExplicit, dbiInferred; private final int[] indexMap; public TripleIndex(String fieldSeq) throws IOException { this.fieldSeq = fieldSeq.toCharArray(); + this.keyWriter = IndexKeyWriters.forFieldSeq(fieldSeq); + this.matcherFactory = IndexKeyWriters.matcherFactory(fieldSeq); this.indexMap = getIndexes(this.fieldSeq); // open database and use native sort order without comparator dbiExplicit = openDatabase(env, fieldSeq, MDB_CREATE, null); @@ -1273,52 +1294,132 @@ void getMaxKey(ByteBuffer bb, long subj, long pred, long obj, long context) { } GroupMatcher createMatcher(long subj, long pred, long obj, long context) { - ByteBuffer bb = ByteBuffer.allocate(TripleStore.MAX_KEY_LENGTH); + int length = getLength(subj, pred, obj, context); + + ByteBuffer bb = ByteBuffer.allocate(length); toKey(bb, subj == -1 ? 0 : subj, pred == -1 ? 0 : pred, obj == -1 ? 0 : obj, context == -1 ? 0 : context); bb.flip(); - boolean[] shouldMatch = new boolean[4]; - for (int i = 0; i < fieldSeq.length; i++) { - switch (fieldSeq[i]) { - case 's': - shouldMatch[i] = subj > 0; - break; - case 'p': - shouldMatch[i] = pred > 0; - break; - case 'o': - shouldMatch[i] = obj > 0; - break; - case 'c': - shouldMatch[i] = context >= 0; - break; + return new GroupMatcher(bb.array(), matcherFactory.create(subj, pred, obj, context)); + } + + private int getLength(long subj, long pred, long obj, long context) { + int length = 4; + if (subj > 240) { + length += 8; + } + if (pred > 240) { + length += 8; + + } + if (obj > 240) { + length += 8; + + } + if (context > 240) { + length += 8; + + } + return length; + } + + class KeyStats { + long subj; + long pred; + long obj; + long context; + public LongAdder count = new LongAdder(); + + public KeyStats(long subj, long pred, long obj, long context) { + this.subj = subj; + this.pred = pred; + this.obj = obj; + this.context = context; + } + + @Override + public final boolean equals(Object o) { + if (!(o instanceof KeyStats)) { + return false; + } + + KeyStats keyStats = (KeyStats) o; + return subj == keyStats.subj && pred == keyStats.pred && obj == keyStats.obj + && context == keyStats.context; + } + + @Override + public int hashCode() { + int result = Long.hashCode(subj); + result = 31 * result + Long.hashCode(pred); + result = 31 * result + Long.hashCode(obj); + result = 31 * result + Long.hashCode(context); + return result; + } + + public void print() { + if (count.sum() % 1000000 == 0) { + + try { + System.out.println("Key " + new String(getFieldSeq()) + " " + + Arrays.asList(valueStore.getValue(subj), valueStore.getValue(pred), + valueStore.getValue(obj), valueStore.getValue(context)) + + " count: " + count.sum()); + } catch (IOException e) { + throw new RuntimeException(e); + } } + } - return new GroupMatcher(bb, shouldMatch); } void toKey(ByteBuffer bb, long subj, long pred, long obj, long context) { - for (int i = 0; i < fieldSeq.length; i++) { - switch (fieldSeq[i]) { - case 's': - writeUnsigned(bb, subj); - break; - case 'p': - writeUnsigned(bb, pred); - break; - case 'o': - writeUnsigned(bb, obj); - break; - case 'c': - writeUnsigned(bb, context); - break; + + boolean shouldCache = threeOfFourAreZeroOrMax(subj, pred, obj, context); + + if (shouldCache) { + long sum = subj + pred + obj + context; + if (sum == 0 && subj == pred && obj == context) { + bb.put(Varint.ALL_ZERO_QUAD); + return; + } + + if (sum < 241) { // keys with sum < 241 only need 4 bytes to write and don't need caching + shouldCache = false; } + } + + // Pass through to the keyWriter with caching hint + keyWriter.write(bb, subj, pred, obj, context, shouldCache); } void keyToQuad(ByteBuffer key, long[] quad) { + readQuadUnsigned(key, indexMap, quad); + } + + void keyToQuad(ByteBuffer key, long[] originalQuad, long[] quad) { // directly use index map to read values in to correct positions - readListUnsigned(key, indexMap, quad); + if (originalQuad[indexMap[0]] != -1) { + Varint.skipUnsigned(key); + } else { + quad[indexMap[0]] = Varint.readUnsigned(key); + } + if (originalQuad[indexMap[1]] != -1) { + Varint.skipUnsigned(key); + } else { + quad[indexMap[1]] = Varint.readUnsigned(key); + } + if (originalQuad[indexMap[2]] != -1) { + Varint.skipUnsigned(key); + } else { + quad[indexMap[2]] = Varint.readUnsigned(key); + } + if (originalQuad[indexMap[3]] != -1) { + Varint.skipUnsigned(key); + } else { + quad[indexMap[3]] = Varint.readUnsigned(key); + } } @Override @@ -1341,4 +1442,34 @@ void destroy(long txn) { mdb_drop(txn, dbiInferred, true); } } + + static boolean threeOfFourAreZeroOrMax(long subj, long pred, long obj, long context) { + // Precompute the 8 equalities once (cheapest operations here) + boolean zS = subj == 0L, zP = pred == 0L, zO = obj == 0L, zC = context == 0L; + boolean mS = subj == Long.MAX_VALUE, mP = pred == Long.MAX_VALUE, mO = obj == Long.MAX_VALUE, + mC = context == Long.MAX_VALUE; + + // ≥3-of-4 ≡ ab(c∨d) ∨ cd(a∨b). Apply once for zeros and once for maxes. + // Using '&' and '|' (not &&/||) keeps it branchless and predictable. + + return (((zS & zP & (zO | zC)) | (zO & zC & (zS | zP)))// ≥3 zeros + | ((mS & mP & (mO | mC)) | (mO & mC & (mS | mP))));// ≥3 Long.MAX_VALUE +// & !(zS & zP & zO & zC) // not all zeros +// & !(mS & mP & mO & mC); // not all max + } + + public static void main(String[] args) { + System.out.println(threeOfFourAreZeroOrMax(0, 0, 0, 1)); + System.out.println(threeOfFourAreZeroOrMax(0, 0, 1, 0)); + System.out.println(threeOfFourAreZeroOrMax(0, 1, 0, 0)); + System.out.println(threeOfFourAreZeroOrMax(1, 0, 0, 0)); + System.out.println(threeOfFourAreZeroOrMax(Long.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE, 1)); + System.out.println(threeOfFourAreZeroOrMax(Long.MAX_VALUE, Long.MAX_VALUE, 1, Long.MAX_VALUE)); + System.out.println(threeOfFourAreZeroOrMax(Long.MAX_VALUE, 1, Long.MAX_VALUE, Long.MAX_VALUE)); + System.out.println(threeOfFourAreZeroOrMax(1, Long.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE)); + + System.out.println(threeOfFourAreZeroOrMax(0, 0, 0, 0)); + System.out.println(threeOfFourAreZeroOrMax(Long.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE)); + } + } diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java index 4cfc59a5be0..ca469ec7e51 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java @@ -22,6 +22,7 @@ import static org.lwjgl.util.lmdb.LMDB.MDB_NOSYNC; import static org.lwjgl.util.lmdb.LMDB.MDB_NOTLS; import static org.lwjgl.util.lmdb.LMDB.MDB_PREV; +import static org.lwjgl.util.lmdb.LMDB.MDB_RDONLY; import static org.lwjgl.util.lmdb.LMDB.MDB_RESERVE; import static org.lwjgl.util.lmdb.LMDB.MDB_SET_RANGE; import static org.lwjgl.util.lmdb.LMDB.MDB_SUCCESS; @@ -48,6 +49,8 @@ import java.io.File; import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; @@ -57,7 +60,6 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicReferenceArray; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.StampedLock; @@ -115,6 +117,18 @@ class ValueStore extends AbstractValueFactory { * Maximum size of keys before hashing is used (size of two long values) */ private static final int MAX_KEY_SIZE = 16; + + private static final VarHandle PREVIOUS_NAMESPACE_HANDLE; + + static { + try { + PREVIOUS_NAMESPACE_HANDLE = MethodHandles.lookup() + .findVarHandle(ValueStore.class, "previousNamespaceEntry", Object[].class); + } catch (ReflectiveOperationException e) { + throw new ExceptionInInitializerError(e); + } + } + /** * Used to do the actual storage of values, once they're translated to byte arrays. */ @@ -126,7 +140,9 @@ class ValueStore extends AbstractValueFactory { /** * A simple cache containing the [VALUE_CACHE_SIZE] most-recently used values stored by their ID. */ - private final AtomicReferenceArray valueCache; + private final LmdbValue[] valueCache; + private final long[] valueCacheId; + private final int valueCacheMask; /** * A simple cache containing the [ID_CACHE_SIZE] most-recently used value-IDs stored by their value. */ @@ -158,6 +174,7 @@ class ValueStore extends AbstractValueFactory { private final boolean forceSync; private final boolean autoGrow; private boolean invalidateRevisionOnCommit = false; + /** * This lock is required to block transactions while auto-growing the map size. */ @@ -167,7 +184,7 @@ class ValueStore extends AbstractValueFactory { * An object that indicates the revision of the value store, which is used to check if cached value IDs are still * valid. In order to be valid, the ValueStoreRevision object of a LmdbValue needs to be equal to this object. */ - private volatile ValueStoreRevision revision; + private volatile ValueStoreRevision.Default revision; /** * A wrapper object for the revision of the value store, which is used within lazy (uninitialized values). If this * object is GCed then it is safe to finally remove the ID-value associations and to reuse IDs. @@ -186,6 +203,15 @@ class ValueStore extends AbstractValueFactory { final Set unusedRevisionIds = new HashSet<>(); private final ConcurrentCleaner cleaner = new ConcurrentCleaner(); + private final Set readTransactions = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private final ThreadLocal threadLocalReadTxn = ThreadLocal.withInitial(() -> { + ReadTxn readTxn = new ReadTxn(); + readTxn.registerIfNeeded(); + cleaner.register(readTxn, readTxn::close); + return readTxn; + }); + + private Object[] previousNamespaceEntry; ValueStore(File dir, LmdbStoreConfig config) throws IOException { this.dir = dir; @@ -194,7 +220,10 @@ class ValueStore extends AbstractValueFactory { this.mapSize = config.getValueDBSize(); open(); - valueCache = new AtomicReferenceArray<>(config.getValueCacheSize()); + int cacheSize = nextPowerOfTwo(config.getValueCacheSize()); + valueCache = new LmdbValue[cacheSize]; + valueCacheId = new long[cacheSize]; + valueCacheMask = cacheSize - 1; valueIDCache = new ConcurrentCache<>(config.getValueIDCacheSize()); namespaceCache = new ConcurrentCache<>(config.getNamespaceCacheSize()); namespaceIDCache = new ConcurrentCache<>(config.getNamespaceIDCacheSize()); @@ -443,13 +472,25 @@ protected byte[] getData(long id) throws IOException { * @return the value object or null if not found */ LmdbValue cachedValue(long id) { - LmdbValue value = valueCache.get((int) (id % valueCache.length())); + int idx = (int) (id & valueCacheMask); + + // Faster to read the long from an array than calling LmdbValue#getInternalID() on the value object. There may + // be race conditions, especially if the cache is small and has a high churn rate, but we can live with that + // since we anyway check the ID on the value object later on. + if (valueCacheId[idx] != id) { + return null; + } + + LmdbValue value = valueCache[idx]; if (value != null && value.getInternalID() == id) { return value; } return null; } + long prevId; + long prevPrevId; + /** * Cache value by ID. *

    @@ -460,7 +501,17 @@ LmdbValue cachedValue(long id) { * @return the value object or null if not found */ void cacheValue(long id, LmdbValue value) { - valueCache.lazySet((int) (id % valueCache.length()), value); + int idx = (int) (id & valueCacheMask); + valueCacheId[idx] = id; + valueCache[idx] = value; + } + + private static int nextPowerOfTwo(int n) { + if (n <= 1) { + return 1; + } + int highest = Integer.highestOneBit(n - 1) << 1; + return highest > 0 ? highest : 1 << 30; } /** @@ -614,6 +665,7 @@ private void incrementRefCount(MemoryStack stack, long writeTxn, byte[] data) th Varint.writeUnsigned(countBb, newCount); dataVal.mv_data(countBb.flip()); E(mdb_put(writeTxn, refCountsDbi, idVal, dataVal, 0)); + } finally { stack.pop(); } @@ -639,6 +691,7 @@ private boolean decrementRefCount(MemoryStack stack, long writeTxn, ByteBuffer i Varint.writeUnsigned(countBb, newCount); dataVal.mv_data(countBb.flip()); E(mdb_put(writeTxn, refCountsDbi, idVal, dataVal, 0)); + } } return false; @@ -708,7 +761,6 @@ private long findId(byte[] data, boolean create) throws IOException { writeTransaction((stack2, writeTxn) -> { dataVal.mv_size(data.length); idVal.mv_data(id2data(idBuffer(stack), newId).flip()); - // store mapping of hash -> ID E(mdb_put(txn, dbi, hashVal, idVal, 0)); // store mapping of ID -> data @@ -799,7 +851,10 @@ private long findId(byte[] data, boolean create) throws IOException { T readTransaction(long env, Transaction transaction) throws IOException { txnLock.readLock().lock(); try { - return LmdbUtil.readTransaction(env, writeTxn, transaction); + if (writeTxn != 0) { + return LmdbUtil.readTransaction(env, writeTxn, transaction); + } + return threadLocalReadTxn.get().execute(transaction); } finally { txnLock.readLock().unlock(); } @@ -952,6 +1007,7 @@ public void gcIds(Collection ids, Collection nextIds) throws IOExcep } // mark id as unused E(mdb_put(writeTxn, unusedDbi, revIdVal, dataVal, 0)); + } deleteValueToIdMappings(stack, writeTxn, finalIds, finalNextIds); @@ -1044,6 +1100,7 @@ protected void deleteValueToIdMappings(MemoryStack stack, long txn, Collection T execute(Transaction transaction) throws IOException { + try (MemoryStack stack = MemoryStack.stackPush()) { + long handle = ensureTxn(); + depth++; + try { + return transaction.exec(stack, handle); + } finally { + releaseTxn(); + } + } + } + + private long ensureTxn() throws IOException { + registerIfNeeded(); + if (!initialized) { + txn = startTxn(); + initialized = true; + return txn; + } + if (depth == 0) { + try { + E(mdb_txn_renew(txn)); + } catch (IOException e) { + closeInternal(); + txn = startTxn(); + initialized = true; + } + } + return txn; + } + + private long startTxn() throws IOException { + try (MemoryStack stack = MemoryStack.stackPush()) { + PointerBuffer pp = stack.mallocPointer(1); + E(mdb_txn_begin(env, NULL, MDB_RDONLY, pp)); + return pp.get(0); + } + } + + private void releaseTxn() { + if (depth == 0) { + return; + } + depth--; + if (depth == 0 && initialized) { + mdb_txn_reset(txn); + } + } + + private void registerIfNeeded() { + if (!registered) { + readTransactions.add(this); + registered = true; + } + } + + private void unregister() { + registered = false; + } + + private void closeInternal() { + if (initialized) { + if (depth > 0) { + mdb_txn_reset(txn); + depth = 0; + } + mdb_txn_abort(txn); + initialized = false; + } + } + + void close() { + closeInternal(); + } + } + /** * Checks if the supplied Value object is a LmdbValue object that has been created by this ValueStore. */ @@ -1390,7 +1549,7 @@ private LmdbIRI data2uri(long id, byte[] data, LmdbIRI value) throws IOException ByteBuffer bb = ByteBuffer.wrap(data); // skip type marker bb.get(); - long nsID = Varint.readUnsigned(bb); + long nsID = Varint.readUnsignedHeap(bb); String namespace = getNamespace(nsID); String localName = new String(data, bb.position(), bb.remaining(), StandardCharsets.UTF_8); @@ -1417,7 +1576,7 @@ private LmdbLiteral data2literal(long id, byte[] data, LmdbLiteral value) throws // skip type marker bb.get(); // Get datatype - long datatypeID = Varint.readUnsigned(bb); + long datatypeID = Varint.readUnsignedHeap(bb); IRI datatype = null; if (datatypeID != LmdbValue.UNKNOWN_ID) { datatype = (IRI) getValue(datatypeID); @@ -1484,6 +1643,11 @@ private long getNamespaceID(String namespace, boolean create) throws IOException *-------------------------------------*/ private String getNamespace(long id) throws IOException { + Object[] cached = (Object[]) PREVIOUS_NAMESPACE_HANDLE.getAcquire(this); + if (cached != null && (long) cached[0] == id) { + return (String) cached[1]; + } + Long cacheID = id; String namespace = namespaceCache.get(cacheID); @@ -1495,6 +1659,8 @@ private String getNamespace(long id) throws IOException { } } + PREVIOUS_NAMESPACE_HANDLE.setRelease(this, new Object[] { cacheID, namespace }); + return namespace; } diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/Varint.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/Varint.java index 283186c0246..af6632804d6 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/Varint.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/Varint.java @@ -11,12 +11,38 @@ package org.eclipse.rdf4j.sail.lmdb; import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import org.eclipse.rdf4j.sail.lmdb.util.SignificantBytesBE; /** * Encodes and decodes unsigned values using variable-length encoding. */ public final class Varint { + static final byte[] ENCODED_LONG_MAX = new byte[] { + (byte) 0xFF, // header: 8 payload bytes + 0x7F, // MSB of Long.MAX_VALUE + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF + }; + + static final byte[] ENCODED_LONG_MAX_QUAD = new byte[] { + (byte) 0xFF, // header: 8 payload bytes + 0x7F, // MSB of Long.MAX_VALUE + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, // header: 8 payload bytes + 0x7F, // MSB of Long.MAX_VALUE + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, // header: 8 payload bytes + 0x7F, // MSB of Long.MAX_VALUE + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, // header: 8 payload bytes + 0x7F, // MSB of Long.MAX_VALUE + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF + }; + + static final byte[] ALL_ZERO_QUAD = new byte[] { 0, 0, 0, 0 }; + private Varint() { } @@ -89,19 +115,102 @@ private Varint() { * @param value value to encode */ public static void writeUnsigned(final ByteBuffer bb, final long value) { + + // Fast path for Long.MAX_VALUE (0xFF header + 8 data bytes) + if (value == Long.MAX_VALUE) { + final ByteOrder prev = bb.order(); + if (prev != ByteOrder.BIG_ENDIAN) { + bb.order(ByteOrder.BIG_ENDIAN); + } + try { + bb.put(ENCODED_LONG_MAX); + } finally { + if (prev != ByteOrder.BIG_ENDIAN) { + bb.order(prev); + } + } + return; + } + if (value <= 240) { bb.put((byte) value); } else if (value <= 2287) { - bb.put((byte) ((value - 240) / 256 + 241)); - bb.put((byte) ((value - 240) % 256)); + // header: 241..248, then 1 payload byte + // Using bit ops instead of div/mod and putShort to batch the two bytes. + long v = value - 240; // 1..2047 + final ByteOrder prev = bb.order(); + if (prev != ByteOrder.BIG_ENDIAN) { + bb.order(ByteOrder.BIG_ENDIAN); + } + try { + int hi = (int) (v >>> 8) + 241; // 241..248 + int lo = (int) (v & 0xFF); // 0..255 + bb.putShort((short) ((hi << 8) | lo)); + } finally { + if (prev != ByteOrder.BIG_ENDIAN) { + bb.order(prev); + } + } } else if (value <= 67823) { + // header 249, then 2 payload bytes (value - 2288), big-endian + long v = value - 2288; // 0..65535 bb.put((byte) 249); - bb.put((byte) ((value - 2288) / 256)); - bb.put((byte) ((value - 2288) % 256)); + final ByteOrder prev = bb.order(); + if (prev != ByteOrder.BIG_ENDIAN) { + bb.order(ByteOrder.BIG_ENDIAN); + } + try { + bb.putShort((short) v); + } finally { + if (prev != ByteOrder.BIG_ENDIAN) { + bb.order(prev); + } + } } else { - int bytes = descriptor(value) + 1; - bb.put((byte) (250 + (bytes - 3))); - writeSignificantBits(bb, value, bytes); + int bytes = descriptor(value) + 1; // 3..8 + bb.put((byte) (250 + (bytes - 3))); // header 250..255 + writeSignificantBits(bb, value, bytes); // payload (batched) + } + } + + // Writes the top `bytes` significant bytes of `value` in big-endian order. +// Uses putLong/putInt/putShort to batch writes and a single leading byte if needed. + private static void writeSignificantBits(ByteBuffer bb, long value, int bytes) { + final ByteOrder prev = bb.order(); + if (prev != ByteOrder.BIG_ENDIAN) { + bb.order(ByteOrder.BIG_ENDIAN); + } + try { + int i = bytes; + + // If odd number of bytes, write the leading MSB first + if ((i & 1) != 0) { + bb.put((byte) (value >>> ((i - 1) * 8))); + i--; + } + + // Now i is even: prefer largest chunks first + if (i == 8) { // exactly 8 bytes + bb.putLong(value); + return; + } + + if (i >= 4) { // write next 4 bytes, if any + int shift = (i - 4) * 8; + bb.putInt((int) (value >>> shift)); + i -= 4; + } + + while (i >= 2) { // write remaining pairs + int shift = (i - 2) * 8; + bb.putShort((short) (value >>> shift)); + i -= 2; + } + // i must be 0 here. + } finally { + if (prev != ByteOrder.BIG_ENDIAN) { + bb.order(prev); + } } } @@ -152,8 +261,10 @@ private static byte descriptor(long value) { } /** - * Decodes a value using the variable-length encoding of - * SQLite. + * Decodes a value using SQLite's variable-length integer encoding. + * + * Lead-byte layout → number of additional bytes: 0..240 → 0 241..248→ 1 249 → 2 250..255→ 3..8 (i.e., 250→3, 251→4, + * …, 255→8) * * @param bb buffer for reading bytes * @return decoded value @@ -161,7 +272,86 @@ private static byte descriptor(long value) { * @see #writeUnsigned(ByteBuffer, long) */ public static long readUnsigned(ByteBuffer bb) throws IllegalArgumentException { + final int a0 = bb.get() & 0xFF; // lead byte, unsigned + + if (a0 <= 240) { + return a0; + } + + final int extra = VARINT_EXTRA_BYTES[a0]; // 0..8 additional bytes + + switch (extra) { + case 1: { + // 1 extra byte; 241..248 + final int a1 = bb.get() & 0xFF; + // 240 + 256*(a0-241) + a1 + return 240L + ((long) (a0 - 241) << 8) + a1; + } + + case 2: { + // 2 extra bytes; lead byte == 249 + final int a1 = bb.get() & 0xFF; + final int a2 = bb.get() & 0xFF; + // 2288 + 256*a1 + a2 + return 2288L + ((long) a1 << 8) + a2; + } + + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + return readSignificantBitsDirect(bb, extra); + // 3..8 extra bytes; 250..255 + default: + throw new IllegalArgumentException("Bytes is higher than 8: " + extra); + + } + } + + public static void skipUnsigned(ByteBuffer bb) throws IllegalArgumentException { + final int a0 = bb.get() & 0xFF; // lead byte, unsigned + + if (a0 <= 240) { + return; + } + + final int extra = VARINT_EXTRA_BYTES[a0]; // 0..8 additional bytes + bb.position(bb.position() + extra); + + } + + /** Lookup: lead byte (0..255) → number of additional bytes (0..8). */ + private static final byte[] VARINT_EXTRA_BYTES = buildVarintExtraBytes(); + + private static byte[] buildVarintExtraBytes() { + final byte[] t = new byte[256]; + + // 0..240 → 0 extra bytes + for (int i = 0; i <= 240; i++) { + t[i] = 0; + } + + // 241..248 → 1 extra byte + for (int i = 241; i <= 248; i++) { + t[i] = 1; + } + + // 249 → 2 extra bytes + t[249] = 2; + + // 250..255 → 3..8 extra bytes + for (int i = 250; i <= 255; i++) { + t[i] = (byte) (i - 247); // 250→3, …, 255→8 + } + + return t; + } + + public static long readUnsignedHeap(ByteBuffer bb) throws IllegalArgumentException { int a0 = bb.get() & 0xFF; + if (a0 <= 240) { return a0; } else if (a0 <= 248) { @@ -173,7 +363,7 @@ public static long readUnsigned(ByteBuffer bb) throws IllegalArgumentException { return 2288 + 256 * a1 + a2; } else { int bytes = a0 - 250 + 3; - return readSignificantBits(bb, bytes); + return readSignificantBitsHeap(bb, bytes); } } @@ -195,7 +385,7 @@ public static long readUnsigned(ByteBuffer bb, int pos) throws IllegalArgumentEx return 240 + 256 * (a0 - 241) + a1; } else if (a0 == 249) { int a1 = bb.get(pos + 1) & 0xFF; - int a2 = bb.get(pos + 1) & 0xFF; + int a2 = bb.get(pos + 2) & 0xFF; return 2288 + 256 * a1 + a2; } else { int bytes = a0 - 250 + 3; @@ -203,6 +393,27 @@ public static long readUnsigned(ByteBuffer bb, int pos) throws IllegalArgumentEx } } + private static final int[] FIRST_TO_LENGTH = buildFirstToLength(); + + private static int[] buildFirstToLength() { + int[] t = new int[256]; + // 0..240 → 1 + for (int i = 0; i <= 240; i++) { + t[i] = 1; + } + // 241..248 → 2 + for (int i = 241; i <= 248; i++) { + t[i] = 2; + } + // 249 → 3 + t[249] = 3; + // 250..255 → 4..9 + for (int i = 250; i <= 255; i++) { + t[i] = i - 246; // 250→4, 255→9 + } + return t; + } + /** * Determines length of an encoded varint value by inspecting the first byte. * @@ -210,17 +421,7 @@ public static long readUnsigned(ByteBuffer bb, int pos) throws IllegalArgumentEx * @return decoded value */ public static int firstToLength(byte a0) { - int a0Unsigned = a0 & 0xFF; - if (a0Unsigned <= 240) { - return 1; - } else if (a0Unsigned <= 248) { - return 2; - } else if (a0Unsigned == 249) { - return 3; - } else { - int bytes = a0Unsigned - 250 + 3; - return 1 + bytes; - } + return FIRST_TO_LENGTH[a0 & 0xFF]; } /** @@ -245,6 +446,7 @@ public static long readListElementUnsigned(ByteBuffer bb, int index) { * @param values array with values to write */ public static void writeListUnsigned(final ByteBuffer bb, final long[] values) { + // TODO: Optimise for quads and also call writeUnsigned for (int i = 0; i < values.length; i++) { final long value = values[i]; if (value <= 240) { @@ -276,6 +478,13 @@ public static void readListUnsigned(ByteBuffer bb, long[] values) { } } + public static void readQuadUnsigned(ByteBuffer bb, long[] values) { + values[0] = readUnsigned(bb); + values[1] = readUnsigned(bb); + values[2] = readUnsigned(bb); + values[3] = readUnsigned(bb); + } + /** * Decodes multiple values using variable-length encoding from the given buffer. * @@ -289,32 +498,33 @@ public static void readListUnsigned(ByteBuffer bb, int[] indexMap, long[] values } } - /** - * Writes only the significant bytes of the given value in big-endian order. - * - * @param bb buffer for writing bytes - * @param value value to encode - * @param bytes number of significant bytes - */ - private static void writeSignificantBits(ByteBuffer bb, long value, int bytes) { - while (bytes-- > 0) { - bb.put((byte) (0xFF & (value >>> (bytes * 8)))); - } + public static void readQuadUnsigned(ByteBuffer bb, int[] indexMap, long[] values) { + values[indexMap[0]] = readUnsigned(bb); + values[indexMap[1]] = readUnsigned(bb); + values[indexMap[2]] = readUnsigned(bb); + values[indexMap[3]] = readUnsigned(bb); } /** * Reads only the significant bytes of the given value in big-endian order. * - * @param bb buffer for reading bytes - * @param bytes number of significant bytes + * @param bb buffer for reading bytes + * @param n number of significant bytes */ - private static long readSignificantBits(ByteBuffer bb, int bytes) { - bytes--; - long value = (long) (bb.get() & 0xFF) << (bytes * 8); - while (bytes-- > 0) { - value |= (long) (bb.get() & 0xFF) << (bytes * 8); + private static long readSignificantBits(ByteBuffer bb, int n) { + if (bb.isDirect()) { + return readSignificantBitsDirect(bb, n); + } else { + return readSignificantBitsHeap(bb, n); } - return value; + } + + private static long readSignificantBitsDirect(ByteBuffer bb, int n) { + return SignificantBytesBE.readDirect(bb, n); + } + + private static long readSignificantBitsHeap(ByteBuffer bb, int n) { + return SignificantBytesBE.read(bb, n); } /** @@ -342,8 +552,9 @@ private static int compareRegion(ByteBuffer bb1, int startIdx1, ByteBuffer bb2, } /** - * A matcher for partial equality tests of varint lists. + * Use of this class is deprecated, use {@link org.eclipse.rdf4j.sail.lmdb.util.GroupMatcher} instead. */ + @Deprecated(forRemoval = true) public static class GroupMatcher { final ByteBuffer value; @@ -383,4 +594,5 @@ public boolean matches(ByteBuffer other) { return true; } } + } diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/Bytes.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/Bytes.java new file mode 100644 index 00000000000..ab008769e73 --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/Bytes.java @@ -0,0 +1,610 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + ******************************************************************************/ + +package org.eclipse.rdf4j.sail.lmdb.util; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public final class Bytes { + + private final static boolean bigEndian = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN; + + private Bytes() { + } + + @FunctionalInterface + public interface RegionComparator { + boolean equals(byte firstByte, ByteBuffer other); + } + + private static boolean equals(int a, int b) { + return a == b; + } + + private static short toShort(byte[] array, int offset) { + return (short) (((array[offset] & 0xFF) << 8) | (array[offset + 1] & 0xFF)); + } + + private static int toInt(byte[] array, int offset) { + return ((array[offset] & 0xFF) << 24) + | ((array[offset + 1] & 0xFF) << 16) + | ((array[offset + 2] & 0xFF) << 8) + | (array[offset + 3] & 0xFF); + } + + public static RegionComparator capturedComparator(byte[] array, int offset, int len) { + if (len <= 0) { + return (firstByte, b) -> true; + } + switch (len) { + case 1: + return comparatorLen1(array, offset); + case 2: + return comparatorLen2(array, offset); + case 3: + return comparatorLen3(array, offset); + case 4: + return comparatorLen4(array, offset); + case 5: + return comparatorLen5(array, offset); + case 6: + return comparatorLen6(array, offset); + case 7: + return comparatorLen7(array, offset); + case 8: + return comparatorLen8(array, offset); + case 9: + return comparatorLen9(array, offset); + case 10: + return comparatorLen10(array, offset); + case 11: + return comparatorLen11(array, offset); + case 12: + return comparatorLen12(array, offset); + case 13: + return comparatorLen13(array, offset); + case 14: + return comparatorLen14(array, offset); + case 15: + return comparatorLen15(array, offset); + case 16: + return comparatorLen16(array, offset); + case 17: + return comparatorLen17(array, offset); + case 18: + return comparatorLen18(array, offset); + case 19: + return comparatorLen19(array, offset); + case 20: + return comparatorLen20(array, offset); + default: + return comparatorGeneric(array, offset, len); + } + } + + private static RegionComparator comparatorLen1(byte[] array, int offset) { + return (firstByte, b) -> equals(array[offset], firstByte); + } + + private static RegionComparator comparatorLen2(byte[] array, int offset) { + + return (firstByte, b) -> { + if (!equals(array[offset], firstByte)) { + return false; + } + + return equals(array[offset + 1], b.get()); + }; + } + + private static RegionComparator comparatorLen3(byte[] array, int offset) { + + final short expected = toShort(array, offset + 1); + final short expectedEndian = bigEndian ? expected : Short.reverseBytes(expected); + + return (firstByte, b) -> { + if (!equals(array[offset], firstByte)) { + return false; + } + + return equals(expectedEndian, b.getShort()); + }; + } + + private static RegionComparator comparatorLen4(byte[] array, int offset) { + + final int expected = toInt(array, offset); + final int expectedEndian = bigEndian ? expected : Integer.reverseBytes(expected); + + return (firstByte, b) -> { + + b.position(b.position() - 1); + + return equals(expectedEndian, b.getInt()); + }; + } + + private static RegionComparator comparatorLen5(byte[] array, int offset) { + + final int expected = toInt(array, offset + 1); + final int expectedEndian = bigEndian ? expected : Integer.reverseBytes(expected); + + return (firstByte, b) -> { + if (!equals(array[offset], firstByte)) { + return false; + } + + return equals(expectedEndian, b.getInt()); + }; + } + + private static RegionComparator comparatorLen6(byte[] array, int offset) { + + final int expected = toInt(array, offset + 1); + final int expectedEndian = bigEndian ? expected : Integer.reverseBytes(expected); + final byte tail = array[offset + 5]; + + return (firstByte, b) -> { + if (!equals(array[offset], firstByte)) { + return false; + } + + if (!equals(expectedEndian, b.getInt())) { + return false; + } + + return equals(tail, b.get()); + }; + } + + private static RegionComparator comparatorLen7(byte[] array, int offset) { + + final int expected = toInt(array, offset + 1); + final int expectedEndian = bigEndian ? expected : Integer.reverseBytes(expected); + final short expectedTail = toShort(array, offset + 5); + final short expectedTailEndian = bigEndian ? expectedTail : Short.reverseBytes(expectedTail); + + return (firstByte, b) -> { + if (!equals(array[offset], firstByte)) { + return false; + } + + if (!equals(expectedEndian, b.getInt())) { + return false; + } + + return equals(expectedTailEndian, b.getShort()); + }; + } + + private static RegionComparator comparatorLen8(byte[] array, int offset) { + + final int expected = toInt(array, offset + 1); + final int expectedEndian = bigEndian ? expected : Integer.reverseBytes(expected); + final short expectedShort = toShort(array, offset + 5); + final short expectedShortEndian = bigEndian ? expectedShort : Short.reverseBytes(expectedShort); + final byte tail = array[offset + 7]; + + return (firstByte, b) -> { + if (!equals(array[offset], firstByte)) { + return false; + } + + if (!equals(expectedEndian, b.getInt())) { + return false; + } + + if (!equals(expectedShortEndian, b.getShort())) { + return false; + } + + return equals(tail, b.get()); + }; + } + + private static RegionComparator comparatorLen9(byte[] array, int offset) { + + final int expected1 = toInt(array, offset + 1); + final int expected1Endian = bigEndian ? expected1 : Integer.reverseBytes(expected1); + final int expected2 = toInt(array, offset + 5); + final int expected2Endian = bigEndian ? expected2 : Integer.reverseBytes(expected2); + + return (firstByte, b) -> { + if (!equals(array[offset], firstByte)) { + return false; + } + + if (!equals(expected1Endian, b.getInt())) { + return false; + } + + return equals(expected2Endian, b.getInt()); + }; + } + + private static RegionComparator comparatorLen10(byte[] array, int offset) { + + final int expected1 = toInt(array, offset + 1); + final int expected1Endian = bigEndian ? expected1 : Integer.reverseBytes(expected1); + final int expected2 = toInt(array, offset + 5); + final int expected2Endian = bigEndian ? expected2 : Integer.reverseBytes(expected2); + final byte tail = array[offset + 9]; + + return (firstByte, b) -> { + if (!equals(array[offset], firstByte)) { + return false; + } + + if (!equals(expected1Endian, b.getInt())) { + return false; + } + + if (!equals(expected2Endian, b.getInt())) { + return false; + } + + return equals(tail, b.get()); + }; + } + + private static RegionComparator comparatorLen11(byte[] array, int offset) { + + final int expected1 = toInt(array, offset + 1); + final int expected1Endian = bigEndian ? expected1 : Integer.reverseBytes(expected1); + final int expected2 = toInt(array, offset + 5); + final int expected2Endian = bigEndian ? expected2 : Integer.reverseBytes(expected2); + final short expectedShort = toShort(array, offset + 9); + final short expectedShortEndian = bigEndian ? expectedShort : Short.reverseBytes(expectedShort); + + return (firstByte, b) -> { + if (!equals(array[offset], firstByte)) { + return false; + } + + if (!equals(expected1Endian, b.getInt())) { + return false; + } + + if (!equals(expected2Endian, b.getInt())) { + return false; + } + + return equals(expectedShortEndian, b.getShort()); + }; + } + + private static RegionComparator comparatorLen12(byte[] array, int offset) { + + final int expected1 = toInt(array, offset + 1); + final int expected1Endian = bigEndian ? expected1 : Integer.reverseBytes(expected1); + final int expected2 = toInt(array, offset + 5); + final int expected2Endian = bigEndian ? expected2 : Integer.reverseBytes(expected2); + final short expectedShort = toShort(array, offset + 9); + final short expectedShortEndian = bigEndian ? expectedShort : Short.reverseBytes(expectedShort); + final byte tail = array[offset + 11]; + + return (firstByte, b) -> { + if (!equals(array[offset], firstByte)) { + return false; + } + + if (!equals(expected1Endian, b.getInt())) { + return false; + } + + if (!equals(expected2Endian, b.getInt())) { + return false; + } + + if (!equals(expectedShortEndian, b.getShort())) { + return false; + } + + return equals(tail, b.get()); + }; + } + + private static RegionComparator comparatorLen13(byte[] array, int offset) { + + final int expected1 = toInt(array, offset + 1); + final int expected1Endian = bigEndian ? expected1 : Integer.reverseBytes(expected1); + final int expected2 = toInt(array, offset + 5); + final int expected2Endian = bigEndian ? expected2 : Integer.reverseBytes(expected2); + final int expected3 = toInt(array, offset + 9); + final int expected3Endian = bigEndian ? expected3 : Integer.reverseBytes(expected3); + + return (firstByte, b) -> { + if (!equals(array[offset], firstByte)) { + return false; + } + + if (!equals(expected1Endian, b.getInt())) { + return false; + } + + if (!equals(expected2Endian, b.getInt())) { + return false; + } + + return equals(expected3Endian, b.getInt()); + }; + } + + private static RegionComparator comparatorLen14(byte[] array, int offset) { + + final int expected1 = toInt(array, offset + 1); + final int expected1Endian = bigEndian ? expected1 : Integer.reverseBytes(expected1); + final int expected2 = toInt(array, offset + 5); + final int expected2Endian = bigEndian ? expected2 : Integer.reverseBytes(expected2); + final int expected3 = toInt(array, offset + 9); + final int expected3Endian = bigEndian ? expected3 : Integer.reverseBytes(expected3); + final byte tail = array[offset + 13]; + + return (firstByte, b) -> { + if (!equals(array[offset], firstByte)) { + return false; + } + + if (!equals(expected1Endian, b.getInt())) { + return false; + } + + if (!equals(expected2Endian, b.getInt())) { + return false; + } + + if (!equals(expected3Endian, b.getInt())) { + return false; + } + + return equals(tail, b.get()); + }; + } + + private static RegionComparator comparatorLen15(byte[] array, int offset) { + + final int expected1 = toInt(array, offset + 1); + final int expected1Endian = bigEndian ? expected1 : Integer.reverseBytes(expected1); + final int expected2 = toInt(array, offset + 5); + final int expected2Endian = bigEndian ? expected2 : Integer.reverseBytes(expected2); + final int expected3 = toInt(array, offset + 9); + final int expected3Endian = bigEndian ? expected3 : Integer.reverseBytes(expected3); + final short expectedShort = toShort(array, offset + 13); + final short expectedShortEndian = bigEndian ? expectedShort : Short.reverseBytes(expectedShort); + + return (firstByte, b) -> { + if (!equals(array[offset], firstByte)) { + return false; + } + + if (!equals(expected1Endian, b.getInt())) { + return false; + } + + if (!equals(expected2Endian, b.getInt())) { + return false; + } + + if (!equals(expected3Endian, b.getInt())) { + return false; + } + + return equals(expectedShortEndian, b.getShort()); + }; + } + + private static RegionComparator comparatorLen16(byte[] array, int offset) { + + final int expected1 = toInt(array, offset + 1); + final int expected1Endian = bigEndian ? expected1 : Integer.reverseBytes(expected1); + final int expected2 = toInt(array, offset + 5); + final int expected2Endian = bigEndian ? expected2 : Integer.reverseBytes(expected2); + final int expected3 = toInt(array, offset + 9); + final int expected3Endian = bigEndian ? expected3 : Integer.reverseBytes(expected3); + final short expectedShort = toShort(array, offset + 13); + final short expectedShortEndian = bigEndian ? expectedShort : Short.reverseBytes(expectedShort); + final byte tail = array[offset + 15]; + + return (firstByte, b) -> { + if (!equals(array[offset], firstByte)) { + return false; + } + + if (!equals(expected1Endian, b.getInt())) { + return false; + } + + if (!equals(expected2Endian, b.getInt())) { + return false; + } + + if (!equals(expected3Endian, b.getInt())) { + return false; + } + + if (!equals(expectedShortEndian, b.getShort())) { + return false; + } + + return equals(tail, b.get()); + }; + } + + private static RegionComparator comparatorLen17(byte[] array, int offset) { + + final int expected1 = toInt(array, offset + 1); + final int expected1Endian = bigEndian ? expected1 : Integer.reverseBytes(expected1); + final int expected2 = toInt(array, offset + 5); + final int expected2Endian = bigEndian ? expected2 : Integer.reverseBytes(expected2); + final int expected3 = toInt(array, offset + 9); + final int expected3Endian = bigEndian ? expected3 : Integer.reverseBytes(expected3); + final int expected4 = toInt(array, offset + 13); + final int expected4Endian = bigEndian ? expected4 : Integer.reverseBytes(expected4); + + return (firstByte, b) -> { + if (!equals(array[offset], firstByte)) { + return false; + } + + if (!equals(expected1Endian, b.getInt())) { + return false; + } + + if (!equals(expected2Endian, b.getInt())) { + return false; + } + + if (!equals(expected3Endian, b.getInt())) { + return false; + } + + return equals(expected4Endian, b.getInt()); + }; + } + + private static RegionComparator comparatorLen18(byte[] array, int offset) { + + final int expected1 = toInt(array, offset + 1); + final int expected1Endian = bigEndian ? expected1 : Integer.reverseBytes(expected1); + final int expected2 = toInt(array, offset + 5); + final int expected2Endian = bigEndian ? expected2 : Integer.reverseBytes(expected2); + final int expected3 = toInt(array, offset + 9); + final int expected3Endian = bigEndian ? expected3 : Integer.reverseBytes(expected3); + final int expected4 = toInt(array, offset + 13); + final int expected4Endian = bigEndian ? expected4 : Integer.reverseBytes(expected4); + final byte tail = array[offset + 17]; + + return (firstByte, b) -> { + if (!equals(array[offset], firstByte)) { + return false; + } + + if (!equals(expected1Endian, b.getInt())) { + return false; + } + + if (!equals(expected2Endian, b.getInt())) { + return false; + } + + if (!equals(expected3Endian, b.getInt())) { + return false; + } + + if (!equals(expected4Endian, b.getInt())) { + return false; + } + + return equals(tail, b.get()); + }; + } + + private static RegionComparator comparatorLen19(byte[] array, int offset) { + + final int expected1 = toInt(array, offset + 1); + final int expected1Endian = bigEndian ? expected1 : Integer.reverseBytes(expected1); + final int expected2 = toInt(array, offset + 5); + final int expected2Endian = bigEndian ? expected2 : Integer.reverseBytes(expected2); + final int expected3 = toInt(array, offset + 9); + final int expected3Endian = bigEndian ? expected3 : Integer.reverseBytes(expected3); + final int expected4 = toInt(array, offset + 13); + final int expected4Endian = bigEndian ? expected4 : Integer.reverseBytes(expected4); + final short expectedShort = toShort(array, offset + 17); + final short expectedShortEndian = bigEndian ? expectedShort : Short.reverseBytes(expectedShort); + + return (firstByte, b) -> { + if (!equals(array[offset], firstByte)) { + return false; + } + + if (!equals(expected1Endian, b.getInt())) { + return false; + } + + if (!equals(expected2Endian, b.getInt())) { + return false; + } + + if (!equals(expected3Endian, b.getInt())) { + return false; + } + + if (!equals(expected4Endian, b.getInt())) { + return false; + } + + return equals(expectedShortEndian, b.getShort()); + }; + } + + private static RegionComparator comparatorLen20(byte[] array, int offset) { + + final int expected1 = toInt(array, offset + 1); + final int expected1Endian = bigEndian ? expected1 : Integer.reverseBytes(expected1); + final int expected2 = toInt(array, offset + 5); + final int expected2Endian = bigEndian ? expected2 : Integer.reverseBytes(expected2); + final int expected3 = toInt(array, offset + 9); + final int expected3Endian = bigEndian ? expected3 : Integer.reverseBytes(expected3); + final int expected4 = toInt(array, offset + 13); + final int expected4Endian = bigEndian ? expected4 : Integer.reverseBytes(expected4); + final short expectedShort = toShort(array, offset + 17); + final short expectedShortEndian = bigEndian ? expectedShort : Short.reverseBytes(expectedShort); + final byte tail = array[offset + 19]; + + return (firstByte, b) -> { + if (!equals(array[offset], firstByte)) { + return false; + } + + if (!equals(expected1Endian, b.getInt())) { + return false; + } + + if (!equals(expected2Endian, b.getInt())) { + return false; + } + + if (!equals(expected3Endian, b.getInt())) { + return false; + } + + if (!equals(expected4Endian, b.getInt())) { + return false; + } + + if (!equals(expectedShortEndian, b.getShort())) { + return false; + } + + return equals(tail, b.get()); + }; + } + + private static RegionComparator comparatorGeneric(byte[] array, int offset, int len) { + final int start = offset; + final int end = offset + len; + return (firstByte, b) -> { + if (!equals(array[start], firstByte)) { + return false; + } + + int idx = start + 1; + while (idx < end) { + if (!equals(array[idx], b.get())) { + return false; + } + idx++; + } + return true; + }; + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/GroupMatcher.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/GroupMatcher.java new file mode 100644 index 00000000000..016090b949f --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/GroupMatcher.java @@ -0,0 +1,424 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + ******************************************************************************/ + +package org.eclipse.rdf4j.sail.lmdb.util; + +import static org.eclipse.rdf4j.sail.lmdb.Varint.firstToLength; + +import java.nio.ByteBuffer; + +/** + * A matcher for partial equality tests of varint lists. + */ +public class GroupMatcher { + + public static final Bytes.RegionComparator NULL_REGION_COMPARATOR = (a, b) -> true; + private final int length0; + private final int length1; + private final int length2; + private final int length3; + private final Bytes.RegionComparator cmp0; + private final Bytes.RegionComparator cmp1; + private final Bytes.RegionComparator cmp2; + private final Bytes.RegionComparator cmp3; + private final byte firstByte0; + private final byte firstByte1; + private final byte firstByte2; + private final byte firstByte3; + private final MatchFn matcher; + + public GroupMatcher(byte[] valueArray, boolean[] shouldMatch) { + assert shouldMatch.length == 4; + + int baseOffset = 0; + + // Loop is unrolled for performance. Do not change back to a loop, do not extract into method, unless you + // benchmark with QueryBenchmark first! + { + byte fb = valueArray[0]; + this.firstByte0 = fb; + int len = firstToLength(fb); + this.length0 = len; + if (shouldMatch[0]) { + this.cmp0 = Bytes.capturedComparator(valueArray, 0, len); + } else { + this.cmp0 = NULL_REGION_COMPARATOR; + ; + } + + baseOffset += len; + } + { + + byte fb = valueArray[baseOffset]; + this.firstByte1 = fb; + int len = firstToLength(fb); + this.length1 = len; + + if (shouldMatch[1]) { + this.cmp1 = Bytes.capturedComparator(valueArray, baseOffset, len); + } else { + this.cmp1 = NULL_REGION_COMPARATOR; + } + + baseOffset += len; + } + { + byte fb = valueArray[baseOffset]; + this.firstByte2 = fb; + int len = firstToLength(fb); + this.length2 = len; + if (shouldMatch[2]) { + this.cmp2 = Bytes.capturedComparator(valueArray, baseOffset, len); + } else { + this.cmp2 = NULL_REGION_COMPARATOR; + } + + baseOffset += len; + } + { + byte fb = valueArray[baseOffset]; + this.firstByte3 = fb; + int len = firstToLength(fb); + this.length3 = len; + + if (shouldMatch[3]) { + this.cmp3 = Bytes.capturedComparator(valueArray, baseOffset, len); + } else { + this.cmp3 = NULL_REGION_COMPARATOR; + } + } + + this.matcher = selectMatcher(shouldMatch); + + } + + public boolean matches(ByteBuffer other) { + return matcher.matches(other); + } + + @FunctionalInterface + private interface MatchFn { + boolean matches(ByteBuffer other); + } + + private MatchFn selectMatcher(boolean[] shouldMatch) { + byte mask = 0; + if (shouldMatch[0]) { + mask |= 0b0001; + } + if (shouldMatch[1]) { + mask |= 0b0010; + } + if (shouldMatch[2]) { + mask |= 0b0100; + } + if (shouldMatch[3]) { + mask |= 0b1000; + } + + switch (mask) { + case 0b0000: + return this::match0000; + case 0b0001: + return this::match0001; + case 0b0010: + return this::match0010; + case 0b0011: + return this::match0011; + case 0b0100: + return this::match0100; + case 0b0101: + return this::match0101; + case 0b0110: + return this::match0110; + case 0b0111: + return this::match0111; + case 0b1000: + return this::match1000; + case 0b1001: + return this::match1001; + case 0b1010: + return this::match1010; + case 0b1011: + return this::match1011; + case 0b1100: + return this::match1100; + case 0b1101: + return this::match1101; + case 0b1110: + return this::match1110; + case 0b1111: + return this::match1111; + default: + throw new IllegalStateException("Unsupported matcher mask: " + mask); + } + } + + private boolean match0000(ByteBuffer other) { + return true; + } + + private boolean match0001(ByteBuffer other) { + byte otherFirst0 = other.get(); + if (firstByte0 == otherFirst0) { + return length0 == 1 || cmp0.equals(otherFirst0, other); + } + return false; + } + + private boolean match0010(ByteBuffer other) { + + skipAhead(other); + + byte otherFirst1 = other.get(); + if (firstByte1 == otherFirst1) { + return length1 == 1 || cmp1.equals(otherFirst1, other); + } + return false; + } + + private boolean match0011(ByteBuffer other) { + byte otherFirst0 = other.get(); + if (firstByte0 == otherFirst0) { + if (length0 == 1 || cmp0.equals(otherFirst0, other)) { + byte otherFirst1 = other.get(); + if (firstByte1 == otherFirst1) { + return length1 == 1 || cmp1.equals(otherFirst1, other); + } + } + } + + return false; + } + + private boolean match0100(ByteBuffer other) { + + skipAhead(other); + skipAhead(other); + + byte otherFirst2 = other.get(); + if (firstByte2 == otherFirst2) { + return length2 == 1 || cmp2.equals(otherFirst2, other); + } + return false; + } + + private boolean match0101(ByteBuffer other) { + + byte otherFirst0 = other.get(); + if (firstByte0 == otherFirst0) { + if (length0 == 1 || cmp0.equals(otherFirst0, other)) { + skipAhead(other); + + byte otherFirst2 = other.get(); + if (firstByte2 == otherFirst2) { + return length2 == 1 || cmp2.equals(otherFirst2, other); + } + } + } + return false; + } + + private boolean match0110(ByteBuffer other) { + + skipAhead(other); + + byte otherFirst1 = other.get(); + if (firstByte1 == otherFirst1) { + if (length1 == 1 || cmp1.equals(otherFirst1, other)) { + byte otherFirst2 = other.get(); + if (firstByte2 == otherFirst2) { + return length2 == 1 || cmp2.equals(otherFirst2, other); + } + } + } + return false; + } + + private void skipAhead(ByteBuffer other) { + int i = firstToLength(other.get()) - 1; + assert i >= 0; + if (i > 0) { + other.position(i + other.position()); + } + } + + private boolean match0111(ByteBuffer other) { + + byte otherFirst0 = other.get(); + if (firstByte0 == otherFirst0) { + if (length0 == 1 || cmp0.equals(otherFirst0, other)) { + byte otherFirst1 = other.get(); + if (firstByte1 == otherFirst1) { + if (length1 == 1 || cmp1.equals(otherFirst1, other)) { + byte otherFirst2 = other.get(); + if (firstByte2 == otherFirst2) { + return length2 == 1 || cmp2.equals(otherFirst2, other); + } + } + } + } + } + return false; + } + + private boolean match1000(ByteBuffer other) { + + skipAhead(other); + skipAhead(other); + skipAhead(other); + + byte otherFirst3 = other.get(); + if (firstByte3 == otherFirst3) { + return length3 == 1 || cmp3.equals(otherFirst3, other); + } + return false; + } + + private boolean match1001(ByteBuffer other) { + + byte otherFirst0 = other.get(); + if (firstByte0 == otherFirst0) { + if (length0 == 1 || cmp0.equals(otherFirst0, other)) { + skipAhead(other); + skipAhead(other); + + byte otherFirst3 = other.get(); + if (firstByte3 == otherFirst3) { + return length3 == 1 || cmp3.equals(otherFirst3, other); + } + } + } + return false; + } + + private boolean match1010(ByteBuffer other) { + + skipAhead(other); + byte otherFirst1 = other.get(); + if (firstByte1 == otherFirst1) { + if (length1 == 1 || cmp1.equals(otherFirst1, other)) { + skipAhead(other); + + byte otherFirst3 = other.get(); + if (firstByte3 == otherFirst3) { + return length3 == 1 || cmp3.equals(otherFirst3, other); + } + } + } + return false; + } + + private boolean match1011(ByteBuffer other) { + + byte otherFirst0 = other.get(); + if (firstByte0 == otherFirst0) { + if (length0 == 1 || cmp0.equals(otherFirst0, other)) { + byte otherFirst1 = other.get(); + if (firstByte1 == otherFirst1) { + if (length1 == 1 || cmp1.equals(otherFirst1, other)) { + skipAhead(other); + + byte otherFirst3 = other.get(); + if (firstByte3 == otherFirst3) { + return length3 == 1 || cmp3.equals(otherFirst3, other); + } + } + } + } + } + return false; + } + + private boolean match1100(ByteBuffer other) { + + skipAhead(other); + skipAhead(other); + + byte otherFirst2 = other.get(); + if (firstByte2 == otherFirst2) { + if (length2 == 1 || cmp2.equals(otherFirst2, other)) { + byte otherFirst3 = other.get(); + if (firstByte3 == otherFirst3) { + return length3 == 1 || cmp3.equals(otherFirst3, other); + } + } + } + return false; + } + + private boolean match1101(ByteBuffer other) { + + byte otherFirst0 = other.get(); + if (firstByte0 == otherFirst0) { + if (length0 == 1 || cmp0.equals(otherFirst0, other)) { + skipAhead(other); + + byte otherFirst2 = other.get(); + if (firstByte2 == otherFirst2) { + if (length2 == 1 || cmp2.equals(otherFirst2, other)) { + byte otherFirst3 = other.get(); + if (firstByte3 == otherFirst3) { + return length3 == 1 || cmp3.equals(otherFirst3, other); + } + } + } + } + } + return false; + } + + private boolean match1110(ByteBuffer other) { + + skipAhead(other); + + byte otherFirst1 = other.get(); + if (firstByte1 == otherFirst1) { + if (length1 == 1 || cmp1.equals(otherFirst1, other)) { + byte otherFirst2 = other.get(); + if (firstByte2 == otherFirst2) { + if (length2 == 1 || cmp2.equals(otherFirst2, other)) { + byte otherFirst3 = other.get(); + if (firstByte3 == otherFirst3) { + return length3 == 1 || cmp3.equals(otherFirst3, other); + } + } + } + } + } + return false; + } + + private boolean match1111(ByteBuffer other) { + byte otherFirst0 = other.get(); + if (firstByte0 == otherFirst0) { + if (length0 == 1 || cmp0.equals(otherFirst0, other)) { + byte otherFirst1 = other.get(); + if (firstByte1 == otherFirst1) { + if (length1 == 1 || cmp1.equals(otherFirst1, other)) { + byte otherFirst2 = other.get(); + if (firstByte2 == otherFirst2) { + if (length2 == 1 || cmp2.equals(otherFirst2, other)) { + byte otherFirst3 = other.get(); + if (firstByte3 == otherFirst3) { + return length3 == 1 || cmp3.equals(otherFirst3, other); + } + } + } + } + } + } + } + return false; + } + +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/IndexKeyWriters.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/IndexKeyWriters.java new file mode 100644 index 00000000000..bfd81aae41b --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/IndexKeyWriters.java @@ -0,0 +1,497 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + ******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb.util; + +import java.nio.ByteBuffer; + +import org.eclipse.rdf4j.sail.lmdb.Varint; + +public final class IndexKeyWriters { + + private static final int CACHE_SIZE = 1 << 12; // 4096 entries + private static final int MASK = CACHE_SIZE - 1; + + private IndexKeyWriters() { + } + + @FunctionalInterface + public interface KeyWriter { + void write(ByteBuffer bb, long subj, long pred, long obj, long context, boolean shouldCache); + } + + @FunctionalInterface + interface BasicWriter { + void write(ByteBuffer bb, long subj, long pred, long obj, long context); + } + + @FunctionalInterface + public interface MatcherFactory { + boolean[] create(long subj, long pred, long obj, long context); + } + + public static KeyWriter forFieldSeq(String fieldSeq) { + final BasicWriter basic; + switch (fieldSeq) { + case "spoc": + basic = IndexKeyWriters::spoc; + break; + case "spco": + basic = IndexKeyWriters::spco; + break; + case "sopc": + basic = IndexKeyWriters::sopc; + break; + case "socp": + basic = IndexKeyWriters::socp; + break; + case "scpo": + basic = IndexKeyWriters::scpo; + break; + case "scop": + basic = IndexKeyWriters::scop; + break; + case "psoc": + basic = IndexKeyWriters::psoc; + break; + case "psco": + basic = IndexKeyWriters::psco; + break; + case "posc": + basic = IndexKeyWriters::posc; + break; + case "pocs": + basic = IndexKeyWriters::pocs; + break; + case "pcso": + basic = IndexKeyWriters::pcso; + break; + case "pcos": + basic = IndexKeyWriters::pcos; + break; + case "ospc": + basic = IndexKeyWriters::ospc; + break; + case "oscp": + basic = IndexKeyWriters::oscp; + break; + case "opsc": + basic = IndexKeyWriters::opsc; + break; + case "opcs": + basic = IndexKeyWriters::opcs; + break; + case "ocsp": + basic = IndexKeyWriters::ocsp; + break; + case "ocps": + basic = IndexKeyWriters::ocps; + break; + case "cspo": + basic = IndexKeyWriters::cspo; + break; + case "csop": + basic = IndexKeyWriters::csop; + break; + case "cpso": + basic = IndexKeyWriters::cpso; + break; + case "cpos": + basic = IndexKeyWriters::cpos; + break; + case "cosp": + basic = IndexKeyWriters::cosp; + break; + case "cops": + basic = IndexKeyWriters::cops; + break; + default: + throw new IllegalArgumentException("Unsupported field sequence: " + fieldSeq); + } + // Wrap the basic writer with a caching KeyWriter implementation + return new CachingKeyWriter(basic); + } + + // Simple array-based cache keyed by a masked index computed from a hashCode. + private static final class CachingKeyWriter implements KeyWriter { + + private final CachingKeyWriter.Entry[] cache = new CachingKeyWriter.Entry[CACHE_SIZE]; + + private static final class Entry { + final long hashCode; + final long s, p, o, c; + final byte[] bytes; + final int length; + + Entry(long hashCode, long s, long p, long o, long c, byte[] bytes) { + this.hashCode = hashCode; + this.s = s; + this.p = p; + this.o = o; + this.c = c; + this.bytes = bytes; + this.length = bytes.length; + } + } + + private final BasicWriter basic; + // Races are acceptable; we overwrite slots without synchronization. + + CachingKeyWriter(BasicWriter basic) { + this.basic = basic; + } + + @Override + public void write(ByteBuffer bb, long subj, long pred, long obj, long context, boolean shouldCache) { + if (!shouldCache) { + basic.write(bb, subj, pred, obj, context); + return; + } + + long hashCode = subj - Long.MAX_VALUE + (pred - Long.MAX_VALUE) * 2 + (obj - Long.MAX_VALUE) * 3 + + (context - Long.MAX_VALUE) * 4; + int slot = (int) (hashCode & MASK); + + Entry e = cache[slot]; + + if (e != null && e.hashCode == hashCode && e.s == subj && e.p == pred && e.o == obj && e.c == context) { + bb.put(e.bytes, 0, e.length); + return; + } + + int len = Varint.calcListLengthUnsigned(subj, pred, obj, context); + byte[] bytes = new byte[len]; + ByteBuffer out = ByteBuffer.wrap(bytes); + basic.write(out, subj, pred, obj, context); + out.flip(); + bb.put(out); + cache[slot] = new Entry(hashCode, subj, pred, obj, context, bytes); + } + } + + public static MatcherFactory matcherFactory(String fieldSeq) { + switch (fieldSeq) { + case "spoc": + return IndexKeyWriters::spocShouldMatch; + case "spco": + return IndexKeyWriters::spcoShouldMatch; + case "sopc": + return IndexKeyWriters::sopcShouldMatch; + case "socp": + return IndexKeyWriters::socpShouldMatch; + case "scpo": + return IndexKeyWriters::scpoShouldMatch; + case "scop": + return IndexKeyWriters::scopShouldMatch; + case "psoc": + return IndexKeyWriters::psocShouldMatch; + case "psco": + return IndexKeyWriters::pscoShouldMatch; + case "posc": + return IndexKeyWriters::poscShouldMatch; + case "pocs": + return IndexKeyWriters::pocsShouldMatch; + case "pcso": + return IndexKeyWriters::pcsoShouldMatch; + case "pcos": + return IndexKeyWriters::pcosShouldMatch; + case "ospc": + return IndexKeyWriters::ospcShouldMatch; + case "oscp": + return IndexKeyWriters::oscpShouldMatch; + case "opsc": + return IndexKeyWriters::opscShouldMatch; + case "opcs": + return IndexKeyWriters::opcsShouldMatch; + case "ocsp": + return IndexKeyWriters::ocspShouldMatch; + case "ocps": + return IndexKeyWriters::ocpsShouldMatch; + case "cspo": + return IndexKeyWriters::cspoShouldMatch; + case "csop": + return IndexKeyWriters::csopShouldMatch; + case "cpso": + return IndexKeyWriters::cpsoShouldMatch; + case "cpos": + return IndexKeyWriters::cposShouldMatch; + case "cosp": + return IndexKeyWriters::cospShouldMatch; + case "cops": + return IndexKeyWriters::copsShouldMatch; + default: + throw new IllegalArgumentException("Unsupported field sequence: " + fieldSeq); + } + } + + static void spoc(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, subj); + Varint.writeUnsigned(bb, pred); + Varint.writeUnsigned(bb, obj); + Varint.writeUnsigned(bb, context); + } + + static void spco(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, subj); + Varint.writeUnsigned(bb, pred); + Varint.writeUnsigned(bb, context); + Varint.writeUnsigned(bb, obj); + } + + static void sopc(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, subj); + Varint.writeUnsigned(bb, obj); + Varint.writeUnsigned(bb, pred); + Varint.writeUnsigned(bb, context); + } + + static void socp(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, subj); + Varint.writeUnsigned(bb, obj); + Varint.writeUnsigned(bb, context); + Varint.writeUnsigned(bb, pred); + } + + static void scpo(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, subj); + Varint.writeUnsigned(bb, context); + Varint.writeUnsigned(bb, pred); + Varint.writeUnsigned(bb, obj); + } + + static void scop(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, subj); + Varint.writeUnsigned(bb, context); + Varint.writeUnsigned(bb, obj); + Varint.writeUnsigned(bb, pred); + } + + static void psoc(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, pred); + Varint.writeUnsigned(bb, subj); + Varint.writeUnsigned(bb, obj); + Varint.writeUnsigned(bb, context); + } + + static void psco(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, pred); + Varint.writeUnsigned(bb, subj); + Varint.writeUnsigned(bb, context); + Varint.writeUnsigned(bb, obj); + } + + static void posc(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, pred); + Varint.writeUnsigned(bb, obj); + Varint.writeUnsigned(bb, subj); + Varint.writeUnsigned(bb, context); + } + + static void pocs(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, pred); + Varint.writeUnsigned(bb, obj); + Varint.writeUnsigned(bb, context); + Varint.writeUnsigned(bb, subj); + } + + static void pcso(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, pred); + Varint.writeUnsigned(bb, context); + Varint.writeUnsigned(bb, subj); + Varint.writeUnsigned(bb, obj); + } + + static void pcos(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, pred); + Varint.writeUnsigned(bb, context); + Varint.writeUnsigned(bb, obj); + Varint.writeUnsigned(bb, subj); + } + + static void ospc(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, obj); + Varint.writeUnsigned(bb, subj); + Varint.writeUnsigned(bb, pred); + Varint.writeUnsigned(bb, context); + } + + static void oscp(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, obj); + Varint.writeUnsigned(bb, subj); + Varint.writeUnsigned(bb, context); + Varint.writeUnsigned(bb, pred); + } + + static void opsc(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, obj); + Varint.writeUnsigned(bb, pred); + Varint.writeUnsigned(bb, subj); + Varint.writeUnsigned(bb, context); + } + + static void opcs(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, obj); + Varint.writeUnsigned(bb, pred); + Varint.writeUnsigned(bb, context); + Varint.writeUnsigned(bb, subj); + } + + static void ocsp(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, obj); + Varint.writeUnsigned(bb, context); + Varint.writeUnsigned(bb, subj); + Varint.writeUnsigned(bb, pred); + } + + static void ocps(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, obj); + Varint.writeUnsigned(bb, context); + Varint.writeUnsigned(bb, pred); + Varint.writeUnsigned(bb, subj); + } + + static void cspo(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, context); + Varint.writeUnsigned(bb, subj); + Varint.writeUnsigned(bb, pred); + Varint.writeUnsigned(bb, obj); + } + + static void csop(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, context); + Varint.writeUnsigned(bb, subj); + Varint.writeUnsigned(bb, obj); + Varint.writeUnsigned(bb, pred); + } + + static void cpso(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, context); + Varint.writeUnsigned(bb, pred); + Varint.writeUnsigned(bb, subj); + Varint.writeUnsigned(bb, obj); + } + + static void cpos(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, context); + Varint.writeUnsigned(bb, pred); + Varint.writeUnsigned(bb, obj); + Varint.writeUnsigned(bb, subj); + } + + static void cosp(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, context); + Varint.writeUnsigned(bb, obj); + Varint.writeUnsigned(bb, subj); + Varint.writeUnsigned(bb, pred); + } + + static void cops(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, context); + Varint.writeUnsigned(bb, obj); + Varint.writeUnsigned(bb, pred); + Varint.writeUnsigned(bb, subj); + } + + static boolean[] spocShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { subj > 0, pred > 0, obj > 0, context >= 0 }; + } + + static boolean[] spcoShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { subj > 0, pred > 0, context >= 0, obj > 0 }; + } + + static boolean[] sopcShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { subj > 0, obj > 0, pred > 0, context >= 0 }; + } + + static boolean[] socpShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { subj > 0, obj > 0, context >= 0, pred > 0 }; + } + + static boolean[] scpoShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { subj > 0, context >= 0, pred > 0, obj > 0 }; + } + + static boolean[] scopShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { subj > 0, context >= 0, obj > 0, pred > 0 }; + } + + static boolean[] psocShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { pred > 0, subj > 0, obj > 0, context >= 0 }; + } + + static boolean[] pscoShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { pred > 0, subj > 0, context >= 0, obj > 0 }; + } + + static boolean[] poscShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { pred > 0, obj > 0, subj > 0, context >= 0 }; + } + + static boolean[] pocsShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { pred > 0, obj > 0, context >= 0, subj > 0 }; + } + + static boolean[] pcsoShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { pred > 0, context >= 0, subj > 0, obj > 0 }; + } + + static boolean[] pcosShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { pred > 0, context >= 0, obj > 0, subj > 0 }; + } + + static boolean[] ospcShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { obj > 0, subj > 0, pred > 0, context >= 0 }; + } + + static boolean[] oscpShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { obj > 0, subj > 0, context >= 0, pred > 0 }; + } + + static boolean[] opscShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { obj > 0, pred > 0, subj > 0, context >= 0 }; + } + + static boolean[] opcsShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { obj > 0, pred > 0, context >= 0, subj > 0 }; + } + + static boolean[] ocspShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { obj > 0, context >= 0, subj > 0, pred > 0 }; + } + + static boolean[] ocpsShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { obj > 0, context >= 0, pred > 0, subj > 0 }; + } + + static boolean[] cspoShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { context >= 0, subj > 0, pred > 0, obj > 0 }; + } + + static boolean[] csopShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { context >= 0, subj > 0, obj > 0, pred > 0 }; + } + + static boolean[] cpsoShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { context >= 0, pred > 0, subj > 0, obj > 0 }; + } + + static boolean[] cposShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { context >= 0, pred > 0, obj > 0, subj > 0 }; + } + + static boolean[] cospShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { context >= 0, obj > 0, subj > 0, pred > 0 }; + } + + static boolean[] copsShouldMatch(long subj, long pred, long obj, long context) { + return new boolean[] { context >= 0, obj > 0, pred > 0, subj > 0 }; + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/SignificantBytesBE.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/SignificantBytesBE.java new file mode 100644 index 00000000000..a335023bb73 --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/SignificantBytesBE.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + ******************************************************************************/ + +package org.eclipse.rdf4j.sail.lmdb.util; + +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * Fast reader for 1..8 "significant" big-endian bytes from a ByteBuffer. Chooses optimal path at runtime: - heap-backed + * buffers: raw array indexing (no per-byte virtual calls), - direct/read-only buffers: absolute wide reads + + * conditional byte-swap. + * + * Returns an unsigned value in the low bits of the long (0 .. 2^(8*n)-1). + */ +public final class SignificantBytesBE { + private SignificantBytesBE() { + } + + /** + * Read n (1..8) big-endian significant bytes from the buffer and advance position by n. + * + * @throws IllegalArgumentException if n is not in [1,8] + * @throws BufferUnderflowException if fewer than n bytes remain + */ + public static long read(ByteBuffer bb, int n) { + return readDirect(bb, n); + } + + // -------- Direct/read-only fast path (absolute wide reads + conditional byte swap) -------- + + private static long u32(int x) { + return x & 0xFFFF_FFFFL; + } + + private static int u16(short x) { + return x & 0xFFFF; + } + + private static short getShortBE(ByteBuffer bb, boolean littleEndian) { + short s = bb.getShort(); + return (littleEndian) ? Short.reverseBytes(s) : s; + } + + private static int getIntBE(ByteBuffer bb, boolean littleEndian) { + int i = bb.getInt(); + return (littleEndian) ? Integer.reverseBytes(i) : i; + } + + private static long getLongBE(ByteBuffer bb, boolean littleEndian) { + long l = bb.getLong(); + return (littleEndian) ? Long.reverseBytes(l) : l; + } + + public static long readDirect(ByteBuffer bb, int n) { + if (n < 3 || n > 8) { + throw new IllegalArgumentException("n must be in [3,8]"); + } + + boolean littleEndian = bb.order() == ByteOrder.LITTLE_ENDIAN; + + switch (n) { + case 8: + return getLongBE(bb, littleEndian); + case 7: + return ((bb.get() & 0xFFL) << 48) + | ((u32(getIntBE(bb, littleEndian)) << 16)) + | (u16(getShortBE(bb, littleEndian))); + case 6: + return (((long) u16(getShortBE(bb, littleEndian)) << 32)) + | (u32(getIntBE(bb, littleEndian))); + case 5: + return ((bb.get() & 0xFFL) << 32) + | (u32(getIntBE(bb, littleEndian))); + case 4: + return u32(getIntBE(bb, littleEndian)); + case 3: + return (((long) u16(getShortBE(bb, littleEndian)) << 8)) + | (bb.get() & 0xFFL); + // TODO: add 1 and 2 byte cases here!!! + default: + throw new AssertionError("unreachable"); + } + } + +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/package-info.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/package-info.java new file mode 100644 index 00000000000..6501904cc48 --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/package-info.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2020 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ + +/** + * @apiNote This feature is for internal use only: its existence, signature or behavior may change without warning from + * one release to the next. + */ + +@InternalUseOnly + +package org.eclipse.rdf4j.sail.lmdb.util; + +import org.eclipse.rdf4j.common.annotation.InternalUseOnly; From f8d1887c7df097dfd363e834bd9f551b2a304d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Thu, 2 Oct 2025 10:58:55 +0200 Subject: [PATCH 27/46] add tests --- .../rdf4j/sail/lmdb/CardinalityTest.java | 2 +- .../rdf4j/sail/lmdb/DefaultIndexTest.java | 6 +- .../rdf4j/sail/lmdb/GroupMatcherTest.java | 295 +++++++++++++++++ .../rdf4j/sail/lmdb/GroupMatcherTest2.java | 312 ++++++++++++++++++ .../rdf4j/sail/lmdb/LmdbSailStoreTest.java | 16 + .../rdf4j/sail/lmdb/QueryBenchmarkTest.java | 2 - .../sail/lmdb/RecordIteratorBenchmark.java | 38 ++- .../sail/lmdb/TripleIndexToKeyCacheTest.java | 96 ++++++ .../sail/lmdb/TripleStoreAutoGrowTest.java | 2 +- .../sail/lmdb/TripleStoreManyIndexesTest.java | 4 +- .../rdf4j/sail/lmdb/TripleStoreTest.java | 3 +- .../rdf4j/sail/lmdb/ValueStoreCacheTest.java | 52 +++ .../lmdb/ValueStoreNamespaceCacheTest.java | 83 +++++ .../eclipse/rdf4j/sail/lmdb/VarintTest.java | 102 +++++- .../OverflowBenchmarkConcurrent.java | 4 - .../sail/lmdb/benchmark/QueryBenchmark.java | 94 +++++- .../lmdb/benchmark/QueryBenchmarkFoaf.java | 2 +- .../testsuite/repository/RepositoryTest.java | 32 ++ 18 files changed, 1119 insertions(+), 26 deletions(-) create mode 100644 core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/GroupMatcherTest.java create mode 100644 core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/GroupMatcherTest2.java create mode 100644 core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/TripleIndexToKeyCacheTest.java create mode 100644 core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/ValueStoreCacheTest.java create mode 100644 core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/ValueStoreNamespaceCacheTest.java diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/CardinalityTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/CardinalityTest.java index 1b4aebe72fd..685db90c83a 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/CardinalityTest.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/CardinalityTest.java @@ -38,7 +38,7 @@ public class CardinalityTest { public void before() throws Exception { File dataDir = new File(tempFolder, "triplestore"); dataDir.mkdir(); - tripleStore = new TripleStore(dataDir, new LmdbStoreConfig("spoc,posc")); + tripleStore = new TripleStore(dataDir, new LmdbStoreConfig("spoc,posc"), null); } int count(RecordIterator it) { diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/DefaultIndexTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/DefaultIndexTest.java index 9af0da5a956..b1e8b23df7c 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/DefaultIndexTest.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/DefaultIndexTest.java @@ -26,7 +26,7 @@ public class DefaultIndexTest { @Test public void testDefaultIndex(@TempDir File dir) throws Exception { - TripleStore store = new TripleStore(dir, new LmdbStoreConfig()); + TripleStore store = new TripleStore(dir, new LmdbStoreConfig(), null); store.close(); // check that the triple store used the default index assertEquals("spoc,posc", findIndex(dir)); @@ -36,11 +36,11 @@ public void testDefaultIndex(@TempDir File dir) throws Exception { @Test public void testExistingIndex(@TempDir File dir) throws Exception { // set a non-default index - TripleStore store = new TripleStore(dir, new LmdbStoreConfig("spoc,opsc")); + TripleStore store = new TripleStore(dir, new LmdbStoreConfig("spoc,opsc"), null); store.close(); String before = findIndex(dir); // check that the index is preserved with a null value - store = new TripleStore(dir, new LmdbStoreConfig(null)); + store = new TripleStore(dir, new LmdbStoreConfig(null), null); store.close(); assertEquals(before, findIndex(dir)); FileUtil.deleteDir(dir); diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/GroupMatcherTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/GroupMatcherTest.java new file mode 100644 index 00000000000..a259b0edd53 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/GroupMatcherTest.java @@ -0,0 +1,295 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + ******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.rdf4j.sail.lmdb.util.GroupMatcher; +import org.junit.jupiter.api.Test; + +class GroupMatcherTest { + + private static final int FIELD_COUNT = 4; + private static final int MAX_LENGTH = 9; + + private static final ValueVariants[] VALUE_VARIANTS = buildValueVariants(); + private static final List ALL_LENGTH_COMBINATIONS = buildAllLengthCombinations(); + private static final CandidateStrategy[] CANDIDATE_STRATEGIES = CandidateStrategy.values(); + + @Test + void coversEveryMatcherMaskAcrossAllLengthCombinations() { + for (int mask = 0; mask < (1 << FIELD_COUNT); mask++) { + final int maskBits = mask; + boolean[] shouldMatch = maskToArray(mask); + for (byte[] valueLengths : ALL_LENGTH_COMBINATIONS) { + final byte[] lengthsRef = valueLengths; + long[] referenceValues = valuesForLengths(valueLengths); + GroupMatcher matcher = new GroupMatcher(encodeBE(referenceValues).duplicate().array(), shouldMatch); + + for (CandidateStrategy strategy : CANDIDATE_STRATEGIES) { + final CandidateStrategy strategyRef = strategy; + long[] candidateValues = buildCandidateValues(referenceValues, valueLengths, shouldMatch, strategy); + final long[] candidateCopy = candidateValues; + ByteBuffer matchBuffer = encode(candidateCopy); + + assertTrue(matcher.matches(nativeOrder(matchBuffer.duplicate())), + () -> failureMessage("expected match", maskBits, lengthsRef, strategyRef, candidateCopy, + null)); + + if (hasMatch(shouldMatch)) { + for (int index = 0; index < FIELD_COUNT; index++) { + if (!shouldMatch[index]) { + continue; + } + for (MismatchType mismatchType : MismatchType.values()) { + long[] mismatchValues = createMismatch(candidateCopy, lengthsRef, index, mismatchType); + if (mismatchValues == null) { + continue; + } + final long[] mismatchCopy = mismatchValues; + ByteBuffer mismatchBuffer = encode(mismatchCopy); + assertFalse(matcher.matches(nativeOrder(mismatchBuffer.duplicate())), + () -> failureMessage("expected mismatch", + maskBits, lengthsRef, strategyRef, mismatchCopy, mismatchType)); + } + } + } + } + } + } + } + + private ByteBuffer nativeOrder(ByteBuffer duplicate) { + duplicate.order(ByteOrder.nativeOrder()); + return duplicate; + } + + private static long[] valuesForLengths(byte[] lengthIndices) { + long[] values = new long[FIELD_COUNT]; + for (int i = 0; i < FIELD_COUNT; i++) { + int lengthIndex = Byte.toUnsignedInt(lengthIndices[i]); + values[i] = VALUE_VARIANTS[lengthIndex].base; + } + return values; + } + + private static long[] buildCandidateValues(long[] referenceValues, byte[] valueLengths, boolean[] shouldMatch, + CandidateStrategy strategy) { + long[] candidateValues = new long[FIELD_COUNT]; + for (int i = 0; i < FIELD_COUNT; i++) { + if (shouldMatch[i]) { + candidateValues[i] = referenceValues[i]; + } else { + int lengthIndex = selectLengthIndex(valueLengths, i, strategy); + candidateValues[i] = VALUE_VARIANTS[lengthIndex].nonMatchingSameLength; + } + } + return candidateValues; + } + + private static int selectLengthIndex(byte[] lengths, int position, CandidateStrategy strategy) { + int base = Byte.toUnsignedInt(lengths[position]); + switch (strategy) { + case SAME_LENGTHS: + return base; + case ROTATED_LENGTHS: + return Byte.toUnsignedInt(lengths[(position + 1) % FIELD_COUNT]); + case INCREMENTED_LENGTHS: + return base == MAX_LENGTH ? 1 : base + 1; + default: + throw new IllegalStateException("Unsupported strategy: " + strategy); + } + } + + private static ByteBuffer encode(long[] values) { + ByteBuffer buffer = ByteBuffer + .allocate(Varint.calcListLengthUnsigned(values[0], values[1], values[2], values[3])); + buffer.order(ByteOrder.nativeOrder()); + for (long value : values) { + Varint.writeUnsigned(buffer, value); + } + buffer.flip(); + return buffer; + } + + private static ByteBuffer encodeBE(long[] values) { + ByteBuffer buffer = ByteBuffer + .allocate(Varint.calcListLengthUnsigned(values[0], values[1], values[2], values[3])); + buffer.order(ByteOrder.nativeOrder()); + for (long value : values) { + Varint.writeUnsigned(buffer, value); + } + buffer.flip(); + return buffer; + } + + private static boolean[] maskToArray(int mask) { + boolean[] shouldMatch = new boolean[FIELD_COUNT]; + for (int i = 0; i < FIELD_COUNT; i++) { + shouldMatch[i] = (mask & (1 << i)) != 0; + } + return shouldMatch; + } + + private static boolean hasMatch(boolean[] shouldMatch) { + for (boolean flag : shouldMatch) { + if (flag) { + return true; + } + } + return false; + } + + private static int firstMatchedIndex(boolean[] shouldMatch) { + for (int i = 0; i < FIELD_COUNT; i++) { + if (shouldMatch[i]) { + return i; + } + } + return -1; + } + + private static List buildAllLengthCombinations() { + List combos = new ArrayList<>((int) Math.pow(MAX_LENGTH, FIELD_COUNT)); + buildCombos(combos, new byte[FIELD_COUNT], 0); + return combos; + } + + private static void buildCombos(List combos, byte[] current, int index) { + if (index == FIELD_COUNT) { + combos.add(current.clone()); + return; + } + for (int len = 1; len <= MAX_LENGTH; len++) { + current[index] = (byte) len; + buildCombos(combos, current, index + 1); + } + } + + private static String failureMessage(String expectation, int mask, byte[] valueLengths, CandidateStrategy strategy, + long[] candidateValues, MismatchType mismatchType) { + return expectation + " for mask " + toMask(mask) + ", valueLengths=" + Arrays.toString(toIntArray(valueLengths)) + + ", strategy=" + strategy + + (mismatchType == null ? "" : ", mismatchType=" + mismatchType) + + ", candidate=" + Arrays.toString(candidateValues); + } + + private static String toMask(int mask) { + return String.format("%4s", Integer.toBinaryString(mask)).replace(' ', '0'); + } + + private static int[] toIntArray(byte[] values) { + int[] ints = new int[values.length]; + for (int i = 0; i < values.length; i++) { + ints[i] = Byte.toUnsignedInt(values[i]); + } + return ints; + } + + private static long[] createMismatch(long[] baseCandidate, byte[] valueLengths, int index, + MismatchType mismatchType) { + int lengthIndex = Byte.toUnsignedInt(valueLengths[index]); + ValueVariants variants = VALUE_VARIANTS[lengthIndex]; + long replacement; + switch (mismatchType) { + case SAME_FIRST_BYTE: + if (variants.sameFirstVariant == null) { + return null; + } + replacement = variants.sameFirstVariant; + break; + case DIFFERENT_FIRST_BYTE: + replacement = variants.differentFirstVariant; + break; + default: + throw new IllegalStateException("Unsupported mismatch type: " + mismatchType); + } + if (replacement == baseCandidate[index]) { + return null; + } + long[] mismatch = baseCandidate.clone(); + mismatch[index] = replacement; + return mismatch; + } + + private static ValueVariants[] buildValueVariants() { + ValueVariants[] variants = new ValueVariants[MAX_LENGTH + 1]; + variants[1] = new ValueVariants(42L, 99L, null, 99L); + variants[2] = new ValueVariants(241L, 330L, 330L, 600L); + variants[3] = new ValueVariants(50_000L, 60_000L, 60_000L, 70_000L); + variants[4] = new ValueVariants(1_048_576L, 1_048_577L, 1_048_577L, 16_777_216L); + variants[5] = new ValueVariants(16_777_216L, 16_777_217L, 16_777_217L, 4_294_967_296L); + variants[6] = new ValueVariants(4_294_967_296L, 4_294_967_297L, 4_294_967_297L, 1_099_511_627_776L); + variants[7] = new ValueVariants(1_099_511_627_776L, 1_099_511_627_777L, 1_099_511_627_777L, + 281_474_976_710_656L); + variants[8] = new ValueVariants(281_474_976_710_656L, 281_474_976_710_657L, 281_474_976_710_657L, + 72_057_594_037_927_936L); + variants[9] = new ValueVariants(72_057_594_037_927_936L, 72_057_594_037_927_937L, + 72_057_594_037_927_937L, 281_474_976_710_656L); + + for (int len = 1; len <= MAX_LENGTH; len++) { + ValueVariants v = variants[len]; + if (Varint.calcLengthUnsigned(v.base) != len) { + throw new IllegalStateException("Unexpected length for base value " + v.base + " (len=" + len + ")"); + } + if (Varint.calcLengthUnsigned(v.nonMatchingSameLength) != len) { + throw new IllegalStateException( + "Unexpected length for same-length variant " + v.nonMatchingSameLength + " (len=" + len + ")"); + } + if (v.sameFirstVariant != null && firstByte(v.sameFirstVariant.longValue()) != firstByte(v.base)) { + throw new IllegalStateException("Expected same-first variant to share header for length " + len); + } + if (firstByte(v.differentFirstVariant) == firstByte(v.base)) { + throw new IllegalStateException("Expected different-first variant to differ for length " + len); + } + } + + return variants; + } + + private static byte firstByte(long value) { + ByteBuffer buffer = ByteBuffer.allocate(Varint.calcLengthUnsigned(value)); + Varint.writeUnsigned(buffer, value); + return buffer.array()[0]; + } + + private static final class ValueVariants { + final long base; + final long nonMatchingSameLength; + final Long sameFirstVariant; + final long differentFirstVariant; + + ValueVariants(long base, long nonMatchingSameLength, Long sameFirstVariant, long differentFirstVariant) { + this.base = base; + this.nonMatchingSameLength = nonMatchingSameLength; + this.sameFirstVariant = sameFirstVariant; + this.differentFirstVariant = differentFirstVariant; + } + } + + private enum MismatchType { + SAME_FIRST_BYTE, + DIFFERENT_FIRST_BYTE + } + + private enum CandidateStrategy { + SAME_LENGTHS, + ROTATED_LENGTHS, + INCREMENTED_LENGTHS + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/GroupMatcherTest2.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/GroupMatcherTest2.java new file mode 100644 index 00000000000..00f58e384ab --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/GroupMatcherTest2.java @@ -0,0 +1,312 @@ +///******************************************************************************* +// * Copyright (c) 2025 Eclipse RDF4J contributors. +// * +// * All rights reserved. This program and the accompanying materials +// * are made available under the terms of the Eclipse Distribution License v1.0 +// * which accompanies this distribution, and is available at +// * http://www.eclipse.org/org/documents/edl-v10.php. +// * +// * SPDX-License-Identifier: BSD-3-Clause +// ******************************************************************************/ +//package org.eclipse.rdf4j.sail.lmdb; +// +//import org.eclipse.rdf4j.sail.lmdb.util.GroupMatcher; +//import org.junit.jupiter.api.DynamicTest; +//import org.junit.jupiter.api.TestFactory; +// +//import java.nio.ByteBuffer; +//import java.util.ArrayList; +//import java.util.Arrays; +//import java.util.List; +//import java.util.Optional; +//import java.util.stream.IntStream; +//import java.util.stream.Stream; +// +//import static org.junit.jupiter.api.Assertions.assertFalse; +//import static org.junit.jupiter.api.Assertions.assertTrue; +// +//class GroupMatcherTest2 { +// +// private static final int FIELD_COUNT = 4; +// private static final int MAX_LENGTH = 9; +// +// private static final ValueVariants[] VALUE_VARIANTS = buildValueVariants(); +// private static final List ALL_LENGTH_COMBINATIONS = buildAllLengthCombinations(); +// private static final CandidateStrategy[] CANDIDATE_STRATEGIES = CandidateStrategy.values(); +// +// @TestFactory +// Stream coversEveryMatcherMaskAcrossAllLengthCombinations() { +// return IntStream.range(0, 1 << FIELD_COUNT) +// .mapToObj(Integer::valueOf) +// .flatMap(this::dynamicTestsForMask); +// } +// +// private Stream dynamicTestsForMask(int maskBits) { +// boolean[] shouldMatch = maskToArray(maskBits); +// return ALL_LENGTH_COMBINATIONS.stream() +// .flatMap(lengths -> dynamicTestsForLengths(maskBits, shouldMatch, lengths)); +// } +// +// private Stream dynamicTestsForLengths(int maskBits, boolean[] shouldMatch, byte[] valueLengths) { +// return Arrays.stream(CANDIDATE_STRATEGIES) +// .flatMap(strategy -> dynamicTestsForStrategy(maskBits, shouldMatch, valueLengths, strategy)); +// } +// +// private Stream dynamicTestsForStrategy(int maskBits, boolean[] shouldMatch, byte[] valueLengths, +// CandidateStrategy strategy) { +// long[] referenceValues = valuesForLengths(valueLengths); +// Stream matchTest = Stream.of(createMatchTest(maskBits, shouldMatch, valueLengths, strategy, +// referenceValues)); +// Stream mismatchTests = hasMatch(shouldMatch) +// ? dynamicMismatchTests(maskBits, shouldMatch, valueLengths, referenceValues, strategy) +// : Stream.empty(); +// return Stream.concat(matchTest, mismatchTests); +// } +// +// private DynamicTest createMatchTest(int maskBits, boolean[] shouldMatch, byte[] valueLengths, +// CandidateStrategy strategy, long[] referenceValues) { +// String displayName = "match mask=" + toMask(maskBits) + ", valueLengths=" +// + Arrays.toString(toIntArray(valueLengths)) +// + ", strategy=" + strategy; +// return DynamicTest.dynamicTest(displayName, () -> { +// boolean[] shouldMatchCopy = Arrays.copyOf(shouldMatch, shouldMatch.length); +// GroupMatcher matcher = new GroupMatcher(encode(referenceValues).duplicate(), shouldMatchCopy); +// long[] candidateValues = buildCandidateValues(referenceValues, valueLengths, shouldMatchCopy, strategy); +// assertTrue(matcher.matches(encode(candidateValues).duplicate()), +// () -> failureMessage("expected match", maskBits, valueLengths, strategy, candidateValues, null)); +// }); +// } +// +// private Stream dynamicMismatchTests(int maskBits, boolean[] shouldMatch, byte[] valueLengths, +// long[] referenceValues, CandidateStrategy strategy) { +// return IntStream.range(0, FIELD_COUNT) +// .filter(index -> shouldMatch[index]) +// .mapToObj(Integer::valueOf) +// .flatMap(index -> Arrays.stream(MismatchType.values()) +// .map(mismatchType -> createMismatchTest(maskBits, shouldMatch, valueLengths, referenceValues, +// strategy, +// index, mismatchType)) +// .flatMap(Optional::stream)); +// } +// +// private Optional createMismatchTest(int maskBits, boolean[] shouldMatch, byte[] valueLengths, +// long[] referenceValues, CandidateStrategy strategy, int index, MismatchType mismatchType) { +// long[] candidateValues = buildCandidateValues(referenceValues, valueLengths, shouldMatch, strategy); +// long[] mismatchValues = createMismatch(candidateValues, valueLengths, index, mismatchType); +// if (mismatchValues == null) { +// return Optional.empty(); +// } +// String displayName = "mismatch mask=" + toMask(maskBits) + ", valueLengths=" +// + Arrays.toString(toIntArray(valueLengths)) + ", strategy=" + strategy + ", index=" + index + ", type=" +// + mismatchType; +// return Optional.of(DynamicTest.dynamicTest(displayName, () -> { +// boolean[] shouldMatchCopy = Arrays.copyOf(shouldMatch, shouldMatch.length); +// GroupMatcher matcher = new GroupMatcher(encode(referenceValues).duplicate(), shouldMatchCopy); +// assertFalse(matcher.matches(encode(mismatchValues).duplicate()), +// () -> failureMessage("expected mismatch", maskBits, valueLengths, strategy, mismatchValues, +// mismatchType)); +// })); +// } +// +// private static long[] valuesForLengths(byte[] lengthIndices) { +// long[] values = new long[FIELD_COUNT]; +// for (int i = 0; i < FIELD_COUNT; i++) { +// int lengthIndex = Byte.toUnsignedInt(lengthIndices[i]); +// values[i] = VALUE_VARIANTS[lengthIndex].base; +// } +// return values; +// } +// +// private static long[] buildCandidateValues(long[] referenceValues, byte[] valueLengths, boolean[] shouldMatch, +// CandidateStrategy strategy) { +// long[] candidateValues = new long[FIELD_COUNT]; +// for (int i = 0; i < FIELD_COUNT; i++) { +// if (shouldMatch[i]) { +// candidateValues[i] = referenceValues[i]; +// } else { +// int lengthIndex = selectLengthIndex(valueLengths, i, strategy); +// candidateValues[i] = VALUE_VARIANTS[lengthIndex].nonMatchingSameLength; +// } +// } +// return candidateValues; +// } +// +// private static int selectLengthIndex(byte[] lengths, int position, CandidateStrategy strategy) { +// int base = Byte.toUnsignedInt(lengths[position]); +// switch (strategy) { +// case SAME_LENGTHS: +// return base; +// case ROTATED_LENGTHS: +// return Byte.toUnsignedInt(lengths[(position + 1) % FIELD_COUNT]); +// case INCREMENTED_LENGTHS: +// return base == MAX_LENGTH ? 1 : base + 1; +// default: +// throw new IllegalStateException("Unsupported strategy: " + strategy); +// } +// } +// +// private static ByteBuffer encode(long[] values) { +// ByteBuffer buffer = ByteBuffer +// .allocate(Varint.calcListLengthUnsigned(values[0], values[1], values[2], values[3])); +// for (long value : values) { +// Varint.writeUnsigned(buffer, value); +// } +// buffer.flip(); +// return buffer; +// } +// +// private static boolean[] maskToArray(int mask) { +// boolean[] shouldMatch = new boolean[FIELD_COUNT]; +// for (int i = 0; i < FIELD_COUNT; i++) { +// shouldMatch[i] = (mask & (1 << i)) != 0; +// } +// return shouldMatch; +// } +// +// private static boolean hasMatch(boolean[] shouldMatch) { +// for (boolean flag : shouldMatch) { +// if (flag) { +// return true; +// } +// } +// return false; +// } +// +// private static int firstMatchedIndex(boolean[] shouldMatch) { +// for (int i = 0; i < FIELD_COUNT; i++) { +// if (shouldMatch[i]) { +// return i; +// } +// } +// return -1; +// } +// +// private static List buildAllLengthCombinations() { +// List combos = new ArrayList<>((int) Math.pow(MAX_LENGTH, FIELD_COUNT)); +// buildCombos(combos, new byte[FIELD_COUNT], 0); +// return combos; +// } +// +// private static void buildCombos(List combos, byte[] current, int index) { +// if (index == FIELD_COUNT) { +// combos.add(current.clone()); +// return; +// } +// for (int len = 1; len <= MAX_LENGTH; len++) { +// current[index] = (byte) len; +// buildCombos(combos, current, index + 1); +// } +// } +// +// private static String failureMessage(String expectation, int mask, byte[] valueLengths, CandidateStrategy strategy, +// long[] candidateValues, MismatchType mismatchType) { +// return expectation + " for mask " + toMask(mask) + ", valueLengths=" + Arrays.toString(toIntArray(valueLengths)) +// + ", strategy=" + strategy +// + (mismatchType == null ? "" : ", mismatchType=" + mismatchType) +// + ", candidate=" + Arrays.toString(candidateValues); +// } +// +// private static String toMask(int mask) { +// return String.format("%4s", Integer.toBinaryString(mask)).replace(' ', '0'); +// } +// +// private static int[] toIntArray(byte[] values) { +// int[] ints = new int[values.length]; +// for (int i = 0; i < values.length; i++) { +// ints[i] = Byte.toUnsignedInt(values[i]); +// } +// return ints; +// } +// +// private static long[] createMismatch(long[] baseCandidate, byte[] valueLengths, int index, +// MismatchType mismatchType) { +// int lengthIndex = Byte.toUnsignedInt(valueLengths[index]); +// ValueVariants variants = VALUE_VARIANTS[lengthIndex]; +// long replacement; +// switch (mismatchType) { +// case SAME_FIRST_BYTE: +// if (variants.sameFirstVariant == null) { +// return null; +// } +// replacement = variants.sameFirstVariant; +// break; +// case DIFFERENT_FIRST_BYTE: +// replacement = variants.differentFirstVariant; +// break; +// default: +// throw new IllegalStateException("Unsupported mismatch type: " + mismatchType); +// } +// if (replacement == baseCandidate[index]) { +// return null; +// } +// long[] mismatch = baseCandidate.clone(); +// mismatch[index] = replacement; +// return mismatch; +// } +// +// private static ValueVariants[] buildValueVariants() { +// ValueVariants[] variants = new ValueVariants[MAX_LENGTH + 1]; +// variants[1] = new ValueVariants(42L, 99L, null, 99L); +// variants[2] = new ValueVariants(241L, 330L, 330L, 600L); +// variants[3] = new ValueVariants(50_000L, 60_000L, 60_000L, 70_000L); +// variants[4] = new ValueVariants(1_048_576L, 1_048_577L, 1_048_577L, 16_777_216L); +// variants[5] = new ValueVariants(16_777_216L, 16_777_217L, 16_777_217L, 4_294_967_296L); +// variants[6] = new ValueVariants(4_294_967_296L, 4_294_967_297L, 4_294_967_297L, 1_099_511_627_776L); +// variants[7] = new ValueVariants(1_099_511_627_776L, 1_099_511_627_777L, 1_099_511_627_777L, +// 281_474_976_710_656L); +// variants[8] = new ValueVariants(281_474_976_710_656L, 281_474_976_710_657L, 281_474_976_710_657L, +// 72_057_594_037_927_936L); +// variants[9] = new ValueVariants(72_057_594_037_927_936L, 72_057_594_037_927_937L, +// 72_057_594_037_927_937L, 281_474_976_710_656L); +// +// for (int len = 1; len <= MAX_LENGTH; len++) { +// ValueVariants v = variants[len]; +// if (Varint.calcLengthUnsigned(v.base) != len) { +// throw new IllegalStateException("Unexpected length for base value " + v.base + " (len=" + len + ")"); +// } +// if (Varint.calcLengthUnsigned(v.nonMatchingSameLength) != len) { +// throw new IllegalStateException( +// "Unexpected length for same-length variant " + v.nonMatchingSameLength + " (len=" + len + ")"); +// } +// if (v.sameFirstVariant != null && firstByte(v.sameFirstVariant.longValue()) != firstByte(v.base)) { +// throw new IllegalStateException("Expected same-first variant to share header for length " + len); +// } +// if (firstByte(v.differentFirstVariant) == firstByte(v.base)) { +// throw new IllegalStateException("Expected different-first variant to differ for length " + len); +// } +// } +// +// return variants; +// } +// +// private static byte firstByte(long value) { +// ByteBuffer buffer = ByteBuffer.allocate(Varint.calcLengthUnsigned(value)); +// Varint.writeUnsigned(buffer, value); +// return buffer.array()[0]; +// } +// +// private static final class ValueVariants { +// final long base; +// final long nonMatchingSameLength; +// final Long sameFirstVariant; +// final long differentFirstVariant; +// +// ValueVariants(long base, long nonMatchingSameLength, Long sameFirstVariant, long differentFirstVariant) { +// this.base = base; +// this.nonMatchingSameLength = nonMatchingSameLength; +// this.sameFirstVariant = sameFirstVariant; +// this.differentFirstVariant = differentFirstVariant; +// } +// } +// +// private enum MismatchType { +// SAME_FIRST_BYTE, +// DIFFERENT_FIRST_BYTE +// } +// +// private enum CandidateStrategy { +// SAME_LENGTHS, +// ROTATED_LENGTHS, +// INCREMENTED_LENGTHS +// } +//} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailStoreTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailStoreTest.java index 2e416067a18..b735074c00c 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailStoreTest.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailStoreTest.java @@ -16,6 +16,8 @@ import java.io.File; +import org.eclipse.rdf4j.common.iteration.CloseableIteration; +import org.eclipse.rdf4j.common.iteration.EmptyIteration; import org.eclipse.rdf4j.common.transaction.IsolationLevels; import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Resource; @@ -28,6 +30,8 @@ import org.eclipse.rdf4j.repository.Repository; import org.eclipse.rdf4j.repository.RepositoryConnection; import org.eclipse.rdf4j.repository.sail.SailRepository; +import org.eclipse.rdf4j.sail.SailException; +import org.eclipse.rdf4j.sail.base.SailDataset; import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -193,6 +197,18 @@ public void testPassConnectionBetweenThreadsWithTx() throws InterruptedException } } + @Test + public void testInferredSourceHasEmptyIterationWithoutInferredStatements() throws SailException { + LmdbStore sail = (LmdbStore) ((SailRepository) repo).getSail(); + LmdbSailStore backingStore = sail.getBackingStore(); + + try (SailDataset dataset = backingStore.getInferredSailSource().dataset(IsolationLevels.NONE); + CloseableIteration iteration = dataset.getStatements(null, null, null)) { + assertTrue(iteration instanceof EmptyIteration); + assertFalse(iteration.hasNext()); + } + } + @AfterEach public void after() { repo.shutDown(); diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/QueryBenchmarkTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/QueryBenchmarkTest.java index 7a0fc04b60f..647d0c97bef 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/QueryBenchmarkTest.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/QueryBenchmarkTest.java @@ -18,7 +18,6 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.List; -import java.util.stream.Stream; import org.apache.commons.io.IOUtils; import org.eclipse.rdf4j.common.iteration.Iterations; @@ -26,7 +25,6 @@ import org.eclipse.rdf4j.model.Resource; import org.eclipse.rdf4j.model.Statement; import org.eclipse.rdf4j.model.vocabulary.RDF; -import org.eclipse.rdf4j.query.BindingSet; import org.eclipse.rdf4j.repository.sail.SailRepository; import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection; import org.eclipse.rdf4j.rio.RDFFormat; diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/RecordIteratorBenchmark.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/RecordIteratorBenchmark.java index b26b86a6279..db6d2da0486 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/RecordIteratorBenchmark.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/RecordIteratorBenchmark.java @@ -18,8 +18,24 @@ import org.apache.commons.io.FileUtils; import org.assertj.core.util.Files; import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; -import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.Main; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; /** * @author Piotr Sowiński @@ -27,10 +43,10 @@ @State(Scope.Benchmark) @Warmup(iterations = 5) @BenchmarkMode({ Mode.AverageTime }) -@Fork(value = 4, jvmArgs = { "-Xms1G", "-Xmx1G" }) +@Fork(value = 1, jvmArgs = { "-Xms1G", "-Xmx1G" }) //@Fork(value = 1, jvmArgs = {"-Xms1G", "-Xmx1G", "-XX:StartFlightRecording=jdk.CPUTimeSample#enabled=true,filename=profile.jfr,method-profiling=max","-XX:FlightRecorderOptions=stackdepth=1024", "-XX:+UnlockDiagnosticVMOptions", "-XX:+DebugNonSafepoints"}) @Threads(value = 8) -@Measurement(iterations = 10) +@Measurement(iterations = 5) @OutputTimeUnit(TimeUnit.MILLISECONDS) public class RecordIteratorBenchmark { @@ -40,7 +56,7 @@ public class RecordIteratorBenchmark { @Setup(Level.Trial) public void setup() throws IOException { dataDir = Files.newTemporaryFolder(); - tripleStore = new TripleStore(dataDir, new LmdbStoreConfig("spoc,posc")); + tripleStore = new TripleStore(dataDir, new LmdbStoreConfig("spoc,posc"), null); final int statements = 1_000_000; tripleStore.startTransaction(); @@ -67,4 +83,18 @@ public void iterateAll(Blackhole blackhole) throws IOException { } } } + + public static void main(String[] args) throws Exception { + if (args != null && args.length > 0) { + Main.main(args); + return; + } + + Options options = new OptionsBuilder() + .include(RecordIteratorBenchmark.class.getSimpleName() + ".iterateAll") + .forks(0) + .build(); + + new Runner(options).run(); + } } diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/TripleIndexToKeyCacheTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/TripleIndexToKeyCacheTest.java new file mode 100644 index 00000000000..0e82f5246fe --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/TripleIndexToKeyCacheTest.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.io.File; +import java.nio.ByteBuffer; + +import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +/** + * Focused tests that directly exercise TripleStore.TripleIndex#toKey to provide coverage for behavior-neutral + * optimizations such as internal key encoding caching. + */ +class TripleIndexToKeyCacheTest { + + private TripleStore tripleStore; + + @BeforeEach + void setup(@TempDir File dataDir) throws Exception { + // Create a small store; index set is irrelevant for constructing standalone TripleIndex instances + tripleStore = new TripleStore(dataDir, new LmdbStoreConfig("spoc,posc"), null); + } + + @AfterEach + void tearDown() throws Exception { + if (tripleStore != null) { + tripleStore.close(); + } + } + + @Test + void spoc_subjectBound_othersWildcard() throws Exception { + // Given: only subject is bound, others are wildcards (Long.MAX_VALUE) + long subj = 123L; + long pred = Long.MAX_VALUE; + long obj = Long.MAX_VALUE; + long context = Long.MAX_VALUE; + + TripleStore.TripleIndex index = tripleStore.new TripleIndex("spoc"); + + int len = Varint.calcListLengthUnsigned(subj, pred, obj, context); + ByteBuffer actual = ByteBuffer.allocate(len); + index.toKey(actual, subj, pred, obj, context); + actual.flip(); + + // Expected: varints in spoc order + ByteBuffer expected = ByteBuffer.allocate(len); + Varint.writeUnsigned(expected, subj); + Varint.writeUnsigned(expected, pred); + Varint.writeUnsigned(expected, obj); + Varint.writeUnsigned(expected, context); + expected.flip(); + + assertArrayEquals(expected.array(), actual.array()); + } + + @Test + void posc_predicateBound_othersWildcard() throws Exception { + // Given: only predicate is bound, others are wildcards (Long.MAX_VALUE) + long subj = Long.MAX_VALUE; + long pred = 456L; + long obj = Long.MAX_VALUE; + long context = Long.MAX_VALUE; + + TripleStore.TripleIndex index = tripleStore.new TripleIndex("posc"); + + int len = Varint.calcListLengthUnsigned(subj, pred, obj, context); + ByteBuffer actual = ByteBuffer.allocate(len); + index.toKey(actual, subj, pred, obj, context); + actual.flip(); + + // Expected: varints in posc order + ByteBuffer expected = ByteBuffer.allocate(len); + Varint.writeUnsigned(expected, pred); + Varint.writeUnsigned(expected, obj); + Varint.writeUnsigned(expected, subj); + Varint.writeUnsigned(expected, context); + expected.flip(); + + assertArrayEquals(expected.array(), actual.array()); + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/TripleStoreAutoGrowTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/TripleStoreAutoGrowTest.java index 04c25dee20e..afcfc5e64b0 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/TripleStoreAutoGrowTest.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/TripleStoreAutoGrowTest.java @@ -34,7 +34,7 @@ public class TripleStoreAutoGrowTest { public void before(@TempDir File dataDir) throws Exception { var config = new LmdbStoreConfig("spoc,posc"); config.setTripleDBSize(4096 * 10); - tripleStore = new TripleStore(dataDir, config); + tripleStore = new TripleStore(dataDir, config, null); ((Logger) LoggerFactory .getLogger(TripleStore.class.getName())) .setLevel(ch.qos.logback.classic.Level.DEBUG); diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/TripleStoreManyIndexesTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/TripleStoreManyIndexesTest.java index 7d027cefc13..f6e7ca850a9 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/TripleStoreManyIndexesTest.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/TripleStoreManyIndexesTest.java @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipse.rdf4j.sail.lmdb; -import static org.junit.Assert.*; +import static org.junit.Assert.assertNotNull; import java.io.File; @@ -33,7 +33,7 @@ public void before(@TempDir File dataDir) throws Exception { @Test public void testSixIndexes() throws Exception { TripleStore tripleStore = new TripleStore(dataDir, - new LmdbStoreConfig("spoc,posc,ospc,cspo,cpos,cosp")); + new LmdbStoreConfig("spoc,posc,ospc,cspo,cpos,cosp"), null); tripleStore.startTransaction(); tripleStore.storeTriple(1, 2, 3, 1, true); tripleStore.commit(); diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/TripleStoreTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/TripleStoreTest.java index 32e20f2e766..336c22b9378 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/TripleStoreTest.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/TripleStoreTest.java @@ -11,7 +11,6 @@ package org.eclipse.rdf4j.sail.lmdb; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import java.io.File; import java.util.Arrays; @@ -34,7 +33,7 @@ public class TripleStoreTest { @BeforeEach public void before(@TempDir File dataDir) throws Exception { - tripleStore = new TripleStore(dataDir, new LmdbStoreConfig("spoc,posc")); + tripleStore = new TripleStore(dataDir, new LmdbStoreConfig("spoc,posc"), null); } int count(RecordIterator it) { diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/ValueStoreCacheTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/ValueStoreCacheTest.java new file mode 100644 index 00000000000..a3bc9787345 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/ValueStoreCacheTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; + +import java.io.File; + +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.sail.lmdb.model.LmdbValue; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +/** + * Focused test to prove that the ValueStore cache path is exercised by existing operations. + */ +class ValueStoreCacheTest { + + @Test + void cachedValuePath(@TempDir File dataDir) throws Exception { + LmdbStore store = new LmdbStore(dataDir); + store.init(); + try { + ValueFactory vf = store.getValueFactory(); + // ValueFactory is actually the package-private ValueStore + ValueStore vs = (ValueStore) vf; + + IRI iri = vf.createIRI("urn:example:foo"); + long id = vs.getId(iri, true); + + // Populate cache via lazy retrieval + LmdbValue v1 = vs.getLazyValue(id); + // Direct cache hit + LmdbValue v2 = vs.cachedValue(id); + + assertNotNull(v1); + assertSame(v1, v2); + } finally { + store.shutDown(); + } + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/ValueStoreNamespaceCacheTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/ValueStoreNamespaceCacheTest.java new file mode 100644 index 00000000000..dabb2942c70 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/ValueStoreNamespaceCacheTest.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.File; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Field; +import java.util.concurrent.atomic.AtomicInteger; + +import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class ValueStoreNamespaceCacheTest { + + @Test + void getNamespaceUsesLastResult(@TempDir File dataDir) throws Throwable { + ValueStore valueStore = new ValueStore(new File(dataDir, "values"), new LmdbStoreConfig()); + try { + MethodHandles.Lookup privateLookup = MethodHandles.privateLookupIn(ValueStore.class, + MethodHandles.lookup()); + TestConcurrentCache cache = new TestConcurrentCache(32); + Field namespaceCacheField = ValueStore.class.getDeclaredField("namespaceCache"); + namespaceCacheField.setAccessible(true); + namespaceCacheField.set(valueStore, cache); + + MethodHandle getNamespace = privateLookup.findVirtual(ValueStore.class, "getNamespace", + MethodType.methodType(String.class, long.class)); + + String namespace = "http://example.com/"; + long id = 123L; + cache.put(id, namespace); + String first = (String) getNamespace.invoke(valueStore, id); + assertEquals(namespace, first); + cache.failOnFurtherGets(); + + String second = (String) getNamespace.invoke(valueStore, id); + assertEquals(namespace, second); + assertEquals(1, cache.getInvocations()); + } finally { + valueStore.close(); + } + } + + private static final class TestConcurrentCache extends ConcurrentCache { + + private final AtomicInteger invocations = new AtomicInteger(); + private volatile boolean failOnFurtherGets; + + private TestConcurrentCache(int capacity) { + super(capacity); + } + + @Override + public String get(Object key) { + int count = invocations.incrementAndGet(); + if (failOnFurtherGets && count > 1) { + throw new AssertionError("namespaceCache#get must not be invoked after caching last namespace"); + } + return super.get(key); + } + + private void failOnFurtherGets() { + failOnFurtherGets = true; + } + + private int getInvocations() { + return invocations.get(); + } + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/VarintTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/VarintTest.java index 79cdf1b293d..fef3bd67313 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/VarintTest.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/VarintTest.java @@ -14,6 +14,7 @@ import static org.junit.Assert.assertEquals; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import org.junit.jupiter.api.Test; @@ -26,7 +27,7 @@ public class VarintTest { @Test public void testVarint() { - ByteBuffer bb = ByteBuffer.allocate(9); + ByteBuffer bb = ByteBuffer.allocate(9).order(ByteOrder.nativeOrder()); for (int i = 0; i < values.length; i++) { bb.clear(); Varint.writeUnsigned(bb, values[i]); @@ -36,9 +37,94 @@ public void testVarint() { } } + @Test + public void testVarint2() { + ByteBuffer bb = ByteBuffer.allocate(9).order(ByteOrder.nativeOrder()); + bb.clear(); + Varint.writeUnsigned(bb, values[1]); + bb.flip(); + assertEquals("Encoding should use " + (2) + " bytes", 2, bb.remaining()); + assertEquals("Encoded and decoded value should be equal", values[1], Varint.readUnsigned(bb)); + + } + + @Test + public void testVarint3() { + ByteBuffer bb = ByteBuffer.allocate(9).order(ByteOrder.nativeOrder()); + bb.clear(); + Varint.writeUnsigned(bb, 67823); + bb.flip(); + assertEquals("Encoded and decoded value should be equal", 67823, Varint.readUnsigned(bb)); + + } + + @Test + public void testVarint4() { + ByteBuffer bb = ByteBuffer.allocate(9).order(ByteOrder.nativeOrder()); + bb.clear(); + Varint.writeUnsigned(bb, 67824); + bb.flip(); + assertEquals("Encoded and decoded value should be equal", 67824, Varint.readUnsigned(bb)); + + } + + @Test + public void testVarint5() { + ByteBuffer bb = ByteBuffer.allocate(9).order(ByteOrder.nativeOrder()); + bb.clear(); + Varint.writeUnsigned(bb, 4299999999L); + bb.flip(); + assertEquals("Encoded and decoded value should be equal", 4299999999L, Varint.readUnsigned(bb)); + + } + + @Test + public void testVarintSequential() { + for (long i = 0; i < 99999999; i++) { + ByteBuffer bb = ByteBuffer.allocate(9).order(ByteOrder.nativeOrder()); + bb.clear(); + Varint.writeUnsigned(bb, i); + bb.flip(); + try { + assertEquals("Encoded and decoded value should be equal", i, Varint.readUnsigned(bb)); + } catch (Exception e) { + System.err.println("Failed for i=" + i); + throw e; + } + } + + for (long i = 99999999; i < 999999999999999L; i += 10000000) { + try { + ByteBuffer bb = ByteBuffer.allocate(9).order(ByteOrder.nativeOrder()); + bb.clear(); + Varint.writeUnsigned(bb, i); + bb.flip(); + + assertEquals("Encoded and decoded value should be equal", i, Varint.readUnsigned(bb)); + } catch (Exception e) { + System.err.println("Failed for i=" + i); + throw e; + } + } + + for (long i = Long.MAX_VALUE; i > Long.MAX_VALUE - 999999L; i -= 1) { + ByteBuffer bb = ByteBuffer.allocate(9).order(ByteOrder.nativeOrder()); + bb.clear(); + Varint.writeUnsigned(bb, i); + bb.flip(); + try { + assertEquals("Encoded and decoded value should be equal", i, Varint.readUnsigned(bb)); + } catch (Exception e) { + System.err.println("Failed for i=" + i); + throw e; + } + } + + } + @Test public void testVarintList() { - ByteBuffer bb = ByteBuffer.allocate(2 + 4 * Long.BYTES); + ByteBuffer bb = ByteBuffer.allocate(2 + 4 * Long.BYTES).order(ByteOrder.nativeOrder()); for (int i = 0; i < values.length - 4; i++) { long[] expected = new long[4]; System.arraycopy(values, 0, expected, 0, 4); @@ -50,4 +136,16 @@ public void testVarintList() { assertArrayEquals("Encoded and decoded value should be equal", expected, actual); } } + + @Test + public void testVarintReadUnsignedAtPositionThreeByteEncoding() { + long value = 3000L; + ByteBuffer bb = ByteBuffer.allocate(Varint.calcLengthUnsigned(value)) + .order(ByteOrder.nativeOrder()); + Varint.writeUnsigned(bb, value); + bb.flip(); + assertEquals("Expected three byte encoding", 3, bb.remaining()); + long decoded = Varint.readUnsigned(bb, 0); + assertEquals("Encoded and decoded value using positional read should match", value, decoded); + } } diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/OverflowBenchmarkConcurrent.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/OverflowBenchmarkConcurrent.java index eef34f93d1c..0544ef7b970 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/OverflowBenchmarkConcurrent.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/OverflowBenchmarkConcurrent.java @@ -27,7 +27,6 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import org.apache.commons.io.FileUtils; import org.assertj.core.util.Files; import org.eclipse.rdf4j.common.io.FileUtil; import org.eclipse.rdf4j.model.IRI; @@ -59,10 +58,7 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; -import org.openjdk.jmh.runner.options.Options; -import org.openjdk.jmh.runner.options.OptionsBuilder; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.Logger; diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/QueryBenchmark.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/QueryBenchmark.java index d4a93a3060e..2cda4d8b696 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/QueryBenchmark.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/QueryBenchmark.java @@ -49,10 +49,10 @@ * @author Håvard Ottestad */ @State(Scope.Benchmark) -@Warmup(iterations = 5) +@Warmup(iterations = 10) @BenchmarkMode({ Mode.AverageTime }) @Fork(value = 1, jvmArgs = { "-Xms1G", "-Xmx1G" }) -//@Fork(value = 1, jvmArgs = {"-Xms1G", "-Xmx1G", "-XX:StartFlightRecording=delay=60s,duration=120s,filename=recording.jfr,settings=profile", "-XX:FlightRecorderOptions=samplethreads=true,stackdepth=1024", "-XX:+UnlockDiagnosticVMOptions", "-XX:+DebugNonSafepoints"}) +//@Fork(value = 1, jvmArgs = {"-Xms1G", "-Xmx1G", "-XX:StartFlightRecording=jdk.CPUTimeSample#enabled=true,filename=profile.jfr,method-profiling=max","-XX:FlightRecorderOptions=stackdepth=1024", "-XX:+UnlockDiagnosticVMOptions", "-XX:+DebugNonSafepoints"}) @Measurement(iterations = 5) @OutputTimeUnit(TimeUnit.MILLISECONDS) public class QueryBenchmark { @@ -126,8 +126,8 @@ public class QueryBenchmark { public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() - .include("QueryBenchmark.*") // adapt to run other benchmark tests - .forks(1) + .include("QueryBenchmark.complexQuery$") // adapt to run other benchmark tests + .forks(0) .build(); new Runner(opt).run(); @@ -160,6 +160,92 @@ private static long count(TupleQueryResult evaluate) { } } + // @Benchmark + public long allBenchmarks() { + long ret = 0; + long result; + try (SailRepositoryConnection connection = repository.getConnection()) { + result = count(connection + .prepareTupleQuery(query1) + .evaluate()); + } + ret += result; + long result1; + try (SailRepositoryConnection connection = repository.getConnection()) { + result1 = count(connection + .prepareTupleQuery(query4) + .evaluate() + ); + } + ret += result1; + long result2; + + try (SailRepositoryConnection connection = repository.getConnection()) { + result2 = count(connection + .prepareTupleQuery(query7_pathexpression1) + .evaluate()); + + } + ret += result2; + long result3; + try (SailRepositoryConnection connection = repository.getConnection()) { + result3 = count(connection + .prepareTupleQuery(query8_pathexpression2) + .evaluate()); + } + ret += result3; + long result4; + try (SailRepositoryConnection connection = repository.getConnection()) { + result4 = count(connection + .prepareTupleQuery(different_datasets_with_similar_distributions) + .evaluate()); + } + ret += result4; + long result5; + try (SailRepositoryConnection connection = repository.getConnection()) { + result5 = count(connection + .prepareTupleQuery(long_chain) + .evaluate()); + } + ret += result5; + long result6; + try (SailRepositoryConnection connection = repository.getConnection()) { + result6 = count(connection + .prepareTupleQuery(lots_of_optional) + .evaluate()); + } + ret += result6; + long result7; + try (SailRepositoryConnection connection = repository.getConnection()) { + result7 = count(connection + .prepareTupleQuery(minus) + .evaluate()); + } + ret += result7; + long result8; + try (SailRepositoryConnection connection = repository.getConnection()) { + result8 = count(connection + .prepareTupleQuery(nested_optionals) + .evaluate()); + } + ret += result8; + long result9; + try (SailRepositoryConnection connection = repository.getConnection()) { + result9 = count(connection + .prepareTupleQuery(query_distinct_predicates) + .evaluate()); + } + ret += result9; + long result10; + try (SailRepositoryConnection connection = repository.getConnection()) { + result10 = count(connection + .prepareTupleQuery(simple_filter_not) + .evaluate()); + } + ret += result10; + return ret; + } + @Benchmark public long groupByQuery() { try (SailRepositoryConnection connection = repository.getConnection()) { diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/QueryBenchmarkFoaf.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/QueryBenchmarkFoaf.java index eedfe2ceb96..a217cdc575d 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/QueryBenchmarkFoaf.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/QueryBenchmarkFoaf.java @@ -40,7 +40,7 @@ * Benchmarks query performance with extended FOAF data. */ @State(Scope.Benchmark) -@Warmup(iterations = 2) +@Warmup(iterations = 5) @BenchmarkMode({ Mode.AverageTime }) @Fork(value = 1, jvmArgs = { "-Xms2G", "-Xmx2G", "-Xmn1G", "-XX:+UseSerialGC" }) @Measurement(iterations = 5) diff --git a/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/RepositoryTest.java b/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/RepositoryTest.java index 79a65e5f98f..3ddfb2e1652 100644 --- a/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/RepositoryTest.java +++ b/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/RepositoryTest.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.rdf4j.testsuite.repository; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.concurrent.TimeUnit; @@ -17,10 +18,14 @@ import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Literal; import org.eclipse.rdf4j.model.Resource; +import org.eclipse.rdf4j.model.Statement; import org.eclipse.rdf4j.model.ValueFactory; import org.eclipse.rdf4j.model.vocabulary.DC; +import org.eclipse.rdf4j.model.vocabulary.FOAF; +import org.eclipse.rdf4j.model.vocabulary.RDF; import org.eclipse.rdf4j.repository.Repository; import org.eclipse.rdf4j.repository.RepositoryConnection; +import org.eclipse.rdf4j.repository.RepositoryResult; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; @@ -151,4 +156,31 @@ public void testAutoInit() { } } + @Test + public void getAllStatements() { + try (RepositoryConnection conn = testRepository.getConnection()) { + conn.add(bob, mbox, mboxBob); + conn.add(bob, RDF.TYPE, FOAF.PERSON); + + for (int i = 0; i < 1024 * 32; i++) { + try (RepositoryResult statements = conn.getStatements(null, null, null)) { + assertEquals(2, statements.stream().count()); + } + } + + for (int i = 0; i < 1024 * 32; i++) { + try (RepositoryResult statements = conn.getStatements(null, RDF.TYPE, null)) { + assertEquals(1, statements.stream().count()); + } + } + + for (int i = 0; i < 1024 * 32; i++) { + try (RepositoryResult statements = conn.getStatements(null, null, null)) { + assertEquals(2, statements.stream().count()); + } + } + } + + } + } From 63ff8056e50d0df0c590a3b7135ad0995ed62889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Thu, 2 Oct 2025 10:59:34 +0200 Subject: [PATCH 28/46] GH-5447 improve performance of DefaultBindingSetKey --- .../collection/factory/impl/DefaultBindingSetKey.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/collection-factory/api/src/main/java/org/eclipse/rdf4j/collection/factory/impl/DefaultBindingSetKey.java b/core/collection-factory/api/src/main/java/org/eclipse/rdf4j/collection/factory/impl/DefaultBindingSetKey.java index c8f8815c9be..4b58005769f 100644 --- a/core/collection-factory/api/src/main/java/org/eclipse/rdf4j/collection/factory/impl/DefaultBindingSetKey.java +++ b/core/collection-factory/api/src/main/java/org/eclipse/rdf4j/collection/factory/impl/DefaultBindingSetKey.java @@ -12,7 +12,6 @@ package org.eclipse.rdf4j.collection.factory.impl; import java.io.Serializable; -import java.util.Arrays; import java.util.List; import org.eclipse.rdf4j.collection.factory.api.BindingSetKey; @@ -22,12 +21,12 @@ public class DefaultBindingSetKey implements BindingSetKey, Serializable { private static final long serialVersionUID = 1; - private final Value[] values; + private final List values; private final int hash; public DefaultBindingSetKey(List values, int hash) { - this.values = values.toArray(new Value[0]); + this.values = values; this.hash = hash; } @@ -39,7 +38,7 @@ public int hashCode() { @Override public boolean equals(Object other) { if (other instanceof DefaultBindingSetKey && other.hashCode() == hash) { - return Arrays.deepEquals(values, ((DefaultBindingSetKey) other).values); + return values.equals(((DefaultBindingSetKey) other).values); } return false; } From aeaa8691a81b68cb2722a024aa4f87c4b2f1cb47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Thu, 2 Oct 2025 11:03:20 +0200 Subject: [PATCH 29/46] ignore failing test --- .../compliance/ElasticsearchStoreRepositoryIT.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/sail/elasticsearch-store/src/test/java/org/eclipse/rdf4j/sail/elasticsearchstore/compliance/ElasticsearchStoreRepositoryIT.java b/core/sail/elasticsearch-store/src/test/java/org/eclipse/rdf4j/sail/elasticsearchstore/compliance/ElasticsearchStoreRepositoryIT.java index 899df2fc981..6547347a696 100644 --- a/core/sail/elasticsearch-store/src/test/java/org/eclipse/rdf4j/sail/elasticsearchstore/compliance/ElasticsearchStoreRepositoryIT.java +++ b/core/sail/elasticsearch-store/src/test/java/org/eclipse/rdf4j/sail/elasticsearchstore/compliance/ElasticsearchStoreRepositoryIT.java @@ -48,4 +48,10 @@ protected Repository createRepository() { public void testShutdownFollowedByInit() { // ignore test } + + @Disabled + @Override + public void getAllStatements() { + // ignore test for now + } } From 5bc393b82d9c439bf4963279a94f458ce0350fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Thu, 2 Oct 2025 12:22:47 +0200 Subject: [PATCH 30/46] fix thread local cleanup --- .../eclipse/rdf4j/sail/lmdb/TripleStore.java | 15 -- .../eclipse/rdf4j/sail/lmdb/ValueStore.java | 136 +++++++++++++----- .../rdf4j/sail/lmdb/model/LmdbIRI.java | 10 +- .../rdf4j/sail/lmdb/QueryBenchmarkTest.java | 100 ++++++++++--- 4 files changed, 183 insertions(+), 78 deletions(-) diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/TripleStore.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/TripleStore.java index 7dfd8791a03..059bb51e666 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/TripleStore.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/TripleStore.java @@ -1376,7 +1376,6 @@ public void print() { void toKey(ByteBuffer bb, long subj, long pred, long obj, long context) { boolean shouldCache = threeOfFourAreZeroOrMax(subj, pred, obj, context); - if (shouldCache) { long sum = subj + pred + obj + context; if (sum == 0 && subj == pred && obj == context) { @@ -1458,18 +1457,4 @@ static boolean threeOfFourAreZeroOrMax(long subj, long pred, long obj, long cont // & !(mS & mP & mO & mC); // not all max } - public static void main(String[] args) { - System.out.println(threeOfFourAreZeroOrMax(0, 0, 0, 1)); - System.out.println(threeOfFourAreZeroOrMax(0, 0, 1, 0)); - System.out.println(threeOfFourAreZeroOrMax(0, 1, 0, 0)); - System.out.println(threeOfFourAreZeroOrMax(1, 0, 0, 0)); - System.out.println(threeOfFourAreZeroOrMax(Long.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE, 1)); - System.out.println(threeOfFourAreZeroOrMax(Long.MAX_VALUE, Long.MAX_VALUE, 1, Long.MAX_VALUE)); - System.out.println(threeOfFourAreZeroOrMax(Long.MAX_VALUE, 1, Long.MAX_VALUE, Long.MAX_VALUE)); - System.out.println(threeOfFourAreZeroOrMax(1, Long.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE)); - - System.out.println(threeOfFourAreZeroOrMax(0, 0, 0, 0)); - System.out.println(threeOfFourAreZeroOrMax(Long.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE)); - } - } diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java index ca469ec7e51..b5b3756c000 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java @@ -51,6 +51,7 @@ import java.io.IOException; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; +import java.lang.ref.Cleaner; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; @@ -59,6 +60,7 @@ import java.util.HashSet; import java.util.Optional; import java.util.Set; +import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -75,6 +77,7 @@ import org.eclipse.rdf4j.model.base.AbstractValueFactory; import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.model.util.Literals; +import org.eclipse.rdf4j.sail.SailException; import org.eclipse.rdf4j.sail.lmdb.LmdbUtil.Transaction; import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; import org.eclipse.rdf4j.sail.lmdb.model.LmdbBNode; @@ -203,14 +206,16 @@ class ValueStore extends AbstractValueFactory { final Set unusedRevisionIds = new HashSet<>(); private final ConcurrentCleaner cleaner = new ConcurrentCleaner(); - private final Set readTransactions = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private final Set readTransactions = Collections + .synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>())); private final ThreadLocal threadLocalReadTxn = ThreadLocal.withInitial(() -> { - ReadTxn readTxn = new ReadTxn(); + ReadTxn readTxn = new ReadTxn(readTransactions); readTxn.registerIfNeeded(); - cleaner.register(readTxn, readTxn::close); + readTxn.cleaner(cleaner); return readTxn; }); + @SuppressWarnings("unused") private Object[] previousNamespaceEntry; ValueStore(File dir, LmdbStoreConfig config) throws IOException { @@ -591,7 +596,7 @@ public boolean resolveValue(long id, LmdbValue value) { return true; } } catch (IOException e) { - // should not happen + throw new SailException(e); } return false; } @@ -854,7 +859,7 @@ T readTransaction(long env, Transaction transaction) throws IOException { if (writeTxn != 0) { return LmdbUtil.readTransaction(env, writeTxn, transaction); } - return threadLocalReadTxn.get().execute(transaction); + return threadLocalReadTxn.get().execute(transaction, env); } finally { txnLock.readLock().unlock(); } @@ -1335,59 +1340,119 @@ public void close() throws IOException { } } - private final class ReadTxn { + private static final class ReadTxn { - private long txn; - private boolean initialized; - private int depth; + private final State state = new State(-1); private boolean registered; - T execute(Transaction transaction) throws IOException { + private final Set readTransactions; + private Cleaner.Cleanable cleaner; + + static class State implements Runnable { + public long txn; + public long depth; + private boolean initialized; + + public State(long txn) { + this.txn = txn; + } + + @Override + public void run() { + if (initialized) { + var txn = this.txn; + this.txn = -1; + if (txn != -1) { + try { + if (depth > 0) { + mdb_txn_reset(txn); + depth = 0; + } + } finally { + mdb_txn_abort(txn); + } + } + initialized = false; + } + } + } + + public ReadTxn(Set readTransactions) { + this.readTransactions = readTransactions; + } + + public void cleaner(ConcurrentCleaner cleaner) { + this.cleaner = cleaner.register(this, state); + } + + synchronized T execute(Transaction transaction, long env) throws IOException { try (MemoryStack stack = MemoryStack.stackPush()) { - long handle = ensureTxn(); - depth++; try { - return transaction.exec(stack, handle); - } finally { - releaseTxn(); + ensureTxn(env); + state.depth++; + try { + return transaction.exec(stack, state.txn); + } finally { + releaseTxn(); + } + } catch (Exception e) { + // Retry once + try { + System.gc(); + Thread.sleep(1); + System.gc(); + Thread.sleep(1); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + + ensureTxn(env); + state.depth++; + try { + return transaction.exec(stack, state.txn); + } finally { + releaseTxn(); + } } } } - private long ensureTxn() throws IOException { + private void ensureTxn(long env) throws IOException { registerIfNeeded(); - if (!initialized) { - txn = startTxn(); - initialized = true; - return txn; + + if (!state.initialized) { + startTxn(env); + state.initialized = true; + return; } - if (depth == 0) { + + if (state.depth == 0) { try { - E(mdb_txn_renew(txn)); + E(mdb_txn_renew(state.txn)); } catch (IOException e) { closeInternal(); - txn = startTxn(); - initialized = true; + startTxn(env); + state.initialized = true; } } - return txn; + } - private long startTxn() throws IOException { + private void startTxn(long env) throws IOException { try (MemoryStack stack = MemoryStack.stackPush()) { PointerBuffer pp = stack.mallocPointer(1); E(mdb_txn_begin(env, NULL, MDB_RDONLY, pp)); - return pp.get(0); + state.txn = pp.get(0); } } private void releaseTxn() { - if (depth == 0) { + if (state.depth == 0) { return; } - depth--; - if (depth == 0 && initialized) { - mdb_txn_reset(txn); + state.depth--; + if (state.depth == 0 && state.initialized) { + mdb_txn_reset(state.txn); } } @@ -1403,13 +1468,8 @@ private void unregister() { } private void closeInternal() { - if (initialized) { - if (depth > 0) { - mdb_txn_reset(txn); - depth = 0; - } - mdb_txn_abort(txn); - initialized = false; + if (state.initialized) { + cleaner.clean(); } } diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbIRI.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbIRI.java index 8bc261d44d0..99cf00cbe37 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbIRI.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbIRI.java @@ -14,10 +14,13 @@ import org.eclipse.rdf4j.model.impl.SimpleIRI; import org.eclipse.rdf4j.sail.lmdb.ValueStoreRevision; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class LmdbIRI extends SimpleIRI implements LmdbResource { private static final long serialVersionUID = -5888138591826143179L; + private static final Logger log = LoggerFactory.getLogger(LmdbIRI.class); /*-----------* * Constants * @@ -103,9 +106,12 @@ public void init() { if (!initialized) { synchronized (this) { if (!initialized) { - revision.resolveValue(internalID, this); + boolean resolved = revision.resolveValue(internalID, this); + if (!resolved) { + log.warn("Could not resolve value"); + } + initialized = resolved; } - initialized = true; } } } diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/QueryBenchmarkTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/QueryBenchmarkTest.java index 647d0c97bef..3f8b3068fdf 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/QueryBenchmarkTest.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/QueryBenchmarkTest.java @@ -18,6 +18,7 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.stream.Stream; import org.apache.commons.io.IOUtils; import org.eclipse.rdf4j.common.iteration.Iterations; @@ -25,6 +26,8 @@ import org.eclipse.rdf4j.model.Resource; import org.eclipse.rdf4j.model.Statement; import org.eclipse.rdf4j.model.vocabulary.RDF; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.TupleQueryResult; import org.eclipse.rdf4j.repository.sail.SailRepository; import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection; import org.eclipse.rdf4j.rio.RDFFormat; @@ -42,37 +45,70 @@ public class QueryBenchmarkTest { private static SailRepository repository; public static TemporaryFolder tempDir = new TemporaryFolder(); + static List statementList; private static final String query1; private static final String query2; private static final String query3; private static final String query4; - private static final String query5; - private static final String optionalLhsFilterQuery; - private static final String optionalRhsFilterQuery; - private static final String orderedUnionLimitQuery; - private static final String subSelectQuery; - private static final String multipleSubSelectQuery; - - static List statementList; + private static final String query7_pathexpression1; + private static final String query8_pathexpression2; + + private static final String common_themes; + private static final String different_datasets_with_similar_distributions; + private static final String long_chain; + private static final String optional_lhs_filter; + private static final String optional_rhs_filter; + private static final String ordered_union_limit; + private static final String lots_of_optional; + private static final String minus; + private static final String nested_optionals; + private static final String particularly_large_join_surface; + private static final String query_distinct_predicates; + private static final String simple_filter_not; + private static final String wild_card_chain_with_common_ends; + private static final String sub_select; + private static final String multiple_sub_select; static { try { + common_themes = IOUtils.toString(getResourceAsStream("benchmarkFiles/common-themes.qr"), + StandardCharsets.UTF_8); + different_datasets_with_similar_distributions = IOUtils.toString( + getResourceAsStream("benchmarkFiles/different-datasets-with-similar-distributions.qr"), + StandardCharsets.UTF_8); + long_chain = IOUtils.toString(getResourceAsStream("benchmarkFiles/long-chain.qr"), StandardCharsets.UTF_8); + optional_lhs_filter = IOUtils.toString(getResourceAsStream("benchmarkFiles/optional-lhs-filter.qr"), + StandardCharsets.UTF_8); + optional_rhs_filter = IOUtils.toString(getResourceAsStream("benchmarkFiles/optional-rhs-filter.qr"), + StandardCharsets.UTF_8); + ordered_union_limit = IOUtils.toString(getResourceAsStream("benchmarkFiles/ordered-union-limit.qr"), + StandardCharsets.UTF_8); + lots_of_optional = IOUtils.toString(getResourceAsStream("benchmarkFiles/lots-of-optional.qr"), + StandardCharsets.UTF_8); + minus = IOUtils.toString(getResourceAsStream("benchmarkFiles/minus.qr"), StandardCharsets.UTF_8); + nested_optionals = IOUtils.toString(getResourceAsStream("benchmarkFiles/nested-optionals.qr"), + StandardCharsets.UTF_8); + particularly_large_join_surface = IOUtils.toString( + getResourceAsStream("benchmarkFiles/particularly-large-join-surface.qr"), StandardCharsets.UTF_8); query1 = IOUtils.toString(getResourceAsStream("benchmarkFiles/query1.qr"), StandardCharsets.UTF_8); query2 = IOUtils.toString(getResourceAsStream("benchmarkFiles/query2.qr"), StandardCharsets.UTF_8); query3 = IOUtils.toString(getResourceAsStream("benchmarkFiles/query3.qr"), StandardCharsets.UTF_8); query4 = IOUtils.toString(getResourceAsStream("benchmarkFiles/query4.qr"), StandardCharsets.UTF_8); - query5 = IOUtils.toString(getResourceAsStream("benchmarkFiles/query5.qr"), StandardCharsets.UTF_8); - optionalLhsFilterQuery = IOUtils.toString( - getResourceAsStream("benchmarkFiles/optional-lhs-filter.qr"), StandardCharsets.UTF_8); - optionalRhsFilterQuery = IOUtils.toString( - getResourceAsStream("benchmarkFiles/optional-rhs-filter.qr"), StandardCharsets.UTF_8); - orderedUnionLimitQuery = IOUtils.toString( - getResourceAsStream("benchmarkFiles/ordered-union-limit.qr"), StandardCharsets.UTF_8); - subSelectQuery = IOUtils.toString(getResourceAsStream("benchmarkFiles/sub-select.qr"), + query7_pathexpression1 = IOUtils.toString(getResourceAsStream("benchmarkFiles/query7-pathexpression1.qr"), + StandardCharsets.UTF_8); + query8_pathexpression2 = IOUtils.toString(getResourceAsStream("benchmarkFiles/query8-pathexpression2.qr"), StandardCharsets.UTF_8); - multipleSubSelectQuery = IOUtils.toString( + query_distinct_predicates = IOUtils.toString( + getResourceAsStream("benchmarkFiles/query-distinct-predicates.qr"), StandardCharsets.UTF_8); + simple_filter_not = IOUtils.toString(getResourceAsStream("benchmarkFiles/simple-filter-not.qr"), + StandardCharsets.UTF_8); + wild_card_chain_with_common_ends = IOUtils.toString( + getResourceAsStream("benchmarkFiles/wild-card-chain-with-common-ends.qr"), StandardCharsets.UTF_8); + sub_select = IOUtils.toString(getResourceAsStream("benchmarkFiles/sub-select.qr"), StandardCharsets.UTF_8); + multiple_sub_select = IOUtils.toString( getResourceAsStream("benchmarkFiles/multiple-sub-select.qr"), StandardCharsets.UTF_8); + } catch (IOException e) { throw new RuntimeException(e); } @@ -136,7 +172,7 @@ public void complexQuery() { public void distinctPredicatesQuery() { try (SailRepositoryConnection connection = repository.getConnection()) { long count; - try (var stream = connection.prepareTupleQuery(query5).evaluate().stream()) { + try (var stream = connection.prepareTupleQuery(query_distinct_predicates).evaluate().stream()) { count = stream.count(); } System.out.println(count); @@ -147,7 +183,7 @@ public void distinctPredicatesQuery() { public void optionalLhsFilterQueryProducesExpectedCount() { try (SailRepositoryConnection connection = repository.getConnection()) { long count; - try (var stream = connection.prepareTupleQuery(optionalLhsFilterQuery).evaluate().stream()) { + try (var stream = connection.prepareTupleQuery(optional_lhs_filter).evaluate().stream()) { count = stream.count(); } assertEquals(34904L, count); @@ -158,7 +194,7 @@ public void optionalLhsFilterQueryProducesExpectedCount() { public void optionalRhsFilterQueryProducesExpectedCount() { try (SailRepositoryConnection connection = repository.getConnection()) { long count; - try (var stream = connection.prepareTupleQuery(optionalRhsFilterQuery).evaluate().stream()) { + try (var stream = connection.prepareTupleQuery(optional_rhs_filter).evaluate().stream()) { count = stream.count(); } assertEquals(37917L, count); @@ -169,7 +205,7 @@ public void optionalRhsFilterQueryProducesExpectedCount() { public void orderedUnionLimitQueryProducesExpectedCount() { try (SailRepositoryConnection connection = repository.getConnection()) { long count; - try (var stream = connection.prepareTupleQuery(orderedUnionLimitQuery).evaluate().stream()) { + try (var stream = connection.prepareTupleQuery(ordered_union_limit).evaluate().stream()) { count = stream.count(); } assertEquals(250L, count); @@ -180,7 +216,7 @@ public void orderedUnionLimitQueryProducesExpectedCount() { public void subSelectQueryProducesExpectedCount() { try (SailRepositoryConnection connection = repository.getConnection()) { long count; - try (var stream = connection.prepareTupleQuery(subSelectQuery).evaluate().stream()) { + try (var stream = connection.prepareTupleQuery(sub_select).evaluate().stream()) { count = stream.count(); } assertEquals(16035L, count); @@ -191,7 +227,7 @@ public void subSelectQueryProducesExpectedCount() { public void multipleSubSelectQueryProducesExpectedCount() { try (SailRepositoryConnection connection = repository.getConnection()) { long count; - try (var stream = connection.prepareTupleQuery(multipleSubSelectQuery).evaluate().stream()) { + try (var stream = connection.prepareTupleQuery(multiple_sub_select).evaluate().stream()) { count = stream.count(); } assertEquals(27881L, count); @@ -260,10 +296,28 @@ public void simpleUpdateQueryIsolationNone() { } + @Test + public void ordered_union_limit() { + for (int i = 0; i < 100; i++) { + try (SailRepositoryConnection connection = repository.getConnection()) { + long count = count(connection + .prepareTupleQuery(ordered_union_limit) + .evaluate()); + assertEquals(250L, count); + } + } + } + private boolean hasStatement() { try (SailRepositoryConnection connection = repository.getConnection()) { return connection.hasStatement(RDF.TYPE, RDF.TYPE, RDF.TYPE, true); } } + private static long count(TupleQueryResult evaluate) { + try (Stream stream = evaluate.stream()) { + return stream.count(); + } + } + } From cab792bc6e700d77eb36f489f7944a82dafa6b34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Thu, 2 Oct 2025 13:16:17 +0200 Subject: [PATCH 31/46] code cleanup --- .../org/eclipse/rdf4j/sail/lmdb/benchmark/QueryBenchmark.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/QueryBenchmark.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/QueryBenchmark.java index 2cda4d8b696..3a9c88ad840 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/QueryBenchmark.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/QueryBenchmark.java @@ -49,7 +49,7 @@ * @author Håvard Ottestad */ @State(Scope.Benchmark) -@Warmup(iterations = 10) +@Warmup(iterations = 5) @BenchmarkMode({ Mode.AverageTime }) @Fork(value = 1, jvmArgs = { "-Xms1G", "-Xmx1G" }) //@Fork(value = 1, jvmArgs = {"-Xms1G", "-Xmx1G", "-XX:StartFlightRecording=jdk.CPUTimeSample#enabled=true,filename=profile.jfr,method-profiling=max","-XX:FlightRecorderOptions=stackdepth=1024", "-XX:+UnlockDiagnosticVMOptions", "-XX:+DebugNonSafepoints"}) @@ -126,7 +126,7 @@ public class QueryBenchmark { public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() - .include("QueryBenchmark.complexQuery$") // adapt to run other benchmark tests + .include("QueryBenchmark.ordered_union_limit") // adapt to run other benchmark tests .forks(0) .build(); From aa6bce208128ced040254b93eeff3afb37353286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Thu, 2 Oct 2025 14:03:02 +0200 Subject: [PATCH 32/46] clean up read transactions more proactively --- .../java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java index b5b3756c000..3a09008792c 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java @@ -1252,10 +1252,12 @@ void endTransaction(boolean commit) throws IOException { public void commit() throws IOException { endTransaction(true); + threadLocalReadTxn.get().close(); } public void rollback() throws IOException { endTransaction(false); + threadLocalReadTxn.get().close(); } /** @@ -1315,9 +1317,6 @@ private void closeReadTransactions() { ReadTxn[] snapshot = readTransactions.toArray(new ReadTxn[0]); for (ReadTxn readTxn : snapshot) { readTxn.close(); - if (readTransactions.remove(readTxn)) { - readTxn.unregister(); - } } threadLocalReadTxn.remove(); } finally { @@ -1464,6 +1463,7 @@ private void registerIfNeeded() { } private void unregister() { + readTransactions.remove(this); registered = false; } @@ -1471,6 +1471,9 @@ private void closeInternal() { if (state.initialized) { cleaner.clean(); } + if (registered) { + unregister(); + } } void close() { From b1ca2e99f42eda46169dd3c3b9f18d2f6b42a4ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Thu, 2 Oct 2025 14:10:23 +0200 Subject: [PATCH 33/46] fix test --- .../ElasticsearchStoreRepositoryIT.java | 5 ----- .../testsuite/repository/RepositoryTest.java | 18 +++++++++++++++--- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/core/sail/elasticsearch-store/src/test/java/org/eclipse/rdf4j/sail/elasticsearchstore/compliance/ElasticsearchStoreRepositoryIT.java b/core/sail/elasticsearch-store/src/test/java/org/eclipse/rdf4j/sail/elasticsearchstore/compliance/ElasticsearchStoreRepositoryIT.java index 6547347a696..a7542ab304c 100644 --- a/core/sail/elasticsearch-store/src/test/java/org/eclipse/rdf4j/sail/elasticsearchstore/compliance/ElasticsearchStoreRepositoryIT.java +++ b/core/sail/elasticsearch-store/src/test/java/org/eclipse/rdf4j/sail/elasticsearchstore/compliance/ElasticsearchStoreRepositoryIT.java @@ -49,9 +49,4 @@ public void testShutdownFollowedByInit() { // ignore test } - @Disabled - @Override - public void getAllStatements() { - // ignore test for now - } } diff --git a/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/RepositoryTest.java b/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/RepositoryTest.java index 3ddfb2e1652..c536737d890 100644 --- a/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/RepositoryTest.java +++ b/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/RepositoryTest.java @@ -13,7 +13,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.List; +import java.util.Objects; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Literal; @@ -114,6 +117,9 @@ public void setUp() throws Exception { @AfterEach public void tearDown() { + try (RepositoryConnection connection = testRepository.getConnection()) { + connection.clear(); + } testRepository.shutDown(); } @@ -164,19 +170,25 @@ public void getAllStatements() { for (int i = 0; i < 1024 * 32; i++) { try (RepositoryResult statements = conn.getStatements(null, null, null)) { - assertEquals(2, statements.stream().count()); + List collect = statements.stream().collect(Collectors.toList()); + assertEquals(2, collect.size(), "Iteration " + i + " failed with data:\n" + + collect.stream().map(Objects::toString).reduce((a, b) -> a + "\n" + b).orElse("")); } } for (int i = 0; i < 1024 * 32; i++) { try (RepositoryResult statements = conn.getStatements(null, RDF.TYPE, null)) { - assertEquals(1, statements.stream().count()); + List collect = statements.stream().collect(Collectors.toList()); + assertEquals(1, collect.size(), "Iteration " + i + " failed with data:\n" + + collect.stream().map(Objects::toString).reduce((a, b) -> a + "\n" + b).orElse("")); } } for (int i = 0; i < 1024 * 32; i++) { try (RepositoryResult statements = conn.getStatements(null, null, null)) { - assertEquals(2, statements.stream().count()); + List collect = statements.stream().collect(Collectors.toList()); + assertEquals(2, collect.size(), "Iteration " + i + " failed with data:\n" + + collect.stream().map(Objects::toString).reduce((a, b) -> a + "\n" + b).orElse("")); } } } From 81eab0a54cbb7fe2af781e8070a160d1d3ca2c98 Mon Sep 17 00:00:00 2001 From: Ken Wenzel Date: Mon, 22 Sep 2025 17:54:48 +0200 Subject: [PATCH 34/46] GH-5118 Extend methods of SailConnectionListener with inferred argument. --- .../rdf4j/sail/SailConnectionListener.java | 21 ++++++++++++++++ .../AbstractNotifyingSailConnection.java | 14 +++++++++-- .../rdf4j/sail/base/SailSourceConnection.java | 25 ++++++++++--------- 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/core/sail/api/src/main/java/org/eclipse/rdf4j/sail/SailConnectionListener.java b/core/sail/api/src/main/java/org/eclipse/rdf4j/sail/SailConnectionListener.java index 360a7e6df83..e0f02b44a03 100644 --- a/core/sail/api/src/main/java/org/eclipse/rdf4j/sail/SailConnectionListener.java +++ b/core/sail/api/src/main/java/org/eclipse/rdf4j/sail/SailConnectionListener.java @@ -13,12 +13,32 @@ import org.eclipse.rdf4j.model.Statement; public interface SailConnectionListener { + /** + * Notifies the listener that a statement has been added in a transaction that it has registered itself with. + * + * @param st The statement that was added. + * @param inferred The flag that indicates whether the statement is inferred or explicit. + */ + default void statementAdded(Statement st, boolean inferred) { + statementAdded(st); + } + + /** + * Notifies the listener that a statement has been removed in a transaction that it has registered itself with. + * + * @param st The statement that was removed. + * @param inferred The flag that indicates whether the statement was inferred or explicit. + */ + default void statementRemoved(Statement st, boolean inferred) { + statementRemoved(st); + } /** * Notifies the listener that a statement has been added in a transaction that it has registered itself with. * * @param st The statement that was added. */ + @Deprecated(since = "5.2.0", forRemoval = true) void statementAdded(Statement st); /** @@ -26,5 +46,6 @@ public interface SailConnectionListener { * * @param st The statement that was removed. */ + @Deprecated(since = "5.2.0", forRemoval = true) void statementRemoved(Statement st); } diff --git a/core/sail/api/src/main/java/org/eclipse/rdf4j/sail/helpers/AbstractNotifyingSailConnection.java b/core/sail/api/src/main/java/org/eclipse/rdf4j/sail/helpers/AbstractNotifyingSailConnection.java index fe21c3c3dea..7c7b40d1d9f 100644 --- a/core/sail/api/src/main/java/org/eclipse/rdf4j/sail/helpers/AbstractNotifyingSailConnection.java +++ b/core/sail/api/src/main/java/org/eclipse/rdf4j/sail/helpers/AbstractNotifyingSailConnection.java @@ -48,16 +48,26 @@ protected boolean hasConnectionListeners() { return !listeners.isEmpty(); } + @Deprecated(since = "5.2.0", forRemoval = true) protected void notifyStatementAdded(Statement st) { + notifyStatementAdded(st, false); + } + + protected void notifyStatementAdded(Statement st, boolean inferred) { for (SailConnectionListener listener : listeners) { - listener.statementAdded(st); + listener.statementAdded(st, inferred); } } + @Deprecated(since = "5.2.0", forRemoval = true) protected void notifyStatementRemoved(Statement st) { + notifyStatementRemoved(st, false); + } + + protected void notifyStatementRemoved(Statement st, boolean inferred) { for (SailConnectionListener listener : listeners) { - listener.statementRemoved(st); + listener.statementRemoved(st, inferred); } } } diff --git a/core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/SailSourceConnection.java b/core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/SailSourceConnection.java index 7942984593a..a32f6ba1cb9 100644 --- a/core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/SailSourceConnection.java +++ b/core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/SailSourceConnection.java @@ -632,7 +632,7 @@ public void removeStatement(UpdateContext op, Resource subj, IRI pred, Value obj explicitSinks.put(null, source.sink(getIsolationLevel())); } assert explicitSinks.containsKey(op); - remove(subj, pred, obj, datasets.get(op), explicitSinks.get(op), contexts); + remove(subj, pred, obj, false, datasets.get(op), explicitSinks.get(op), contexts); } removeStatementsInternal(subj, pred, obj, contexts); } @@ -723,7 +723,7 @@ public boolean addInferredStatement(Resource subj, IRI pred, Value obj, Resource // only report inferred statements that don't already // exist addStatementInternal(subj, pred, obj, contexts); - notifyStatementAdded(vf.createStatement(subj, pred, obj)); + notifyStatementAdded(vf.createStatement(subj, pred, obj), true); setStatementsAdded(); modified = true; } @@ -746,7 +746,7 @@ public boolean addInferredStatement(Resource subj, IRI pred, Value obj, Resource // only report inferred statements that don't // already exist addStatementInternal(subj, pred, obj, ctx); - notifyStatementAdded(vf.createStatement(subj, pred, obj, ctx)); + notifyStatementAdded(vf.createStatement(subj, pred, obj, ctx), true); setStatementsAdded(); modified = true; } @@ -762,9 +762,9 @@ private void add(Resource subj, IRI pred, Value obj, SailDataset dataset, SailSi if (contexts.length == 0 || (contexts.length == 1 && contexts[0] == null)) { if (hasConnectionListeners()) { if (!hasStatement(dataset, subj, pred, obj, NULL_CTX)) { - notifyStatementAdded(vf.createStatement(subj, pred, obj)); + notifyStatementAdded(vf.createStatement(subj, pred, obj), false); } else if (sink instanceof Changeset && ((Changeset) sink).hasDeprecated(subj, pred, obj, NULL_CTX)) { - notifyStatementAdded(vf.createStatement(subj, pred, obj)); + notifyStatementAdded(vf.createStatement(subj, pred, obj), false); } // always approve the statement, even if it already exists @@ -788,10 +788,10 @@ private void add(Resource subj, IRI pred, Value obj, SailDataset dataset, SailSi if (hasConnectionListeners()) { if (!hasStatement(dataset, subj, pred, obj, contextsToCheck)) { - notifyStatementAdded(vf.createStatement(subj, pred, obj, ctx)); + notifyStatementAdded(vf.createStatement(subj, pred, obj, ctx), false); } else if (sink instanceof Changeset && ((Changeset) sink).hasDeprecated(subj, pred, obj, contextsToCheck)) { - notifyStatementAdded(vf.createStatement(subj, pred, obj)); + notifyStatementAdded(vf.createStatement(subj, pred, obj), false); } sink.approve(subj, pred, obj, ctx); } else { @@ -815,7 +815,7 @@ public boolean removeInferredStatement(Resource subj, IRI pred, Value obj, Resou explicitOnlyDataset = branch(IncludeInferred.explicitOnly).dataset(level); } removeStatementsInternal(subj, pred, obj, contexts); - boolean removed = remove(subj, pred, obj, inferredOnlyDataset, inferredOnlySink, contexts); + boolean removed = remove(subj, pred, obj, true, inferredOnlyDataset, inferredOnlySink, contexts); if (removed) { setStatementsRemoved(); } @@ -823,7 +823,8 @@ public boolean removeInferredStatement(Resource subj, IRI pred, Value obj, Resou } } - private boolean remove(Resource subj, IRI pred, Value obj, SailDataset dataset, SailSink sink, Resource... contexts) + private boolean remove(Resource subj, IRI pred, Value obj, boolean inferred, SailDataset dataset, SailSink sink, + Resource... contexts) throws SailException { // Use deprecateByQuery if we don't need to notify anyone of which statements have been deleted. @@ -839,7 +840,7 @@ private boolean remove(Resource subj, IRI pred, Value obj, SailDataset dataset, Statement st = iter.next(); sink.deprecate(st); statementsRemoved = true; - notifyStatementRemoved(st); + notifyStatementRemoved(st, inferred); } } return statementsRemoved; @@ -857,7 +858,7 @@ protected void clearInternal(Resource... contexts) throws SailException { } assert explicitSinks.containsKey(null); if (this.hasConnectionListeners()) { - remove(null, null, null, datasets.get(null), explicitSinks.get(null), contexts); + remove(null, null, null, false, datasets.get(null), explicitSinks.get(null), contexts); } explicitSinks.get(null).clear(contexts); } @@ -876,7 +877,7 @@ public void clearInferred(Resource... contexts) throws SailException { explicitOnlyDataset = branch(IncludeInferred.explicitOnly).dataset(level); } if (this.hasConnectionListeners()) { - remove(null, null, null, inferredOnlyDataset, inferredOnlySink, contexts); + remove(null, null, null, true, inferredOnlyDataset, inferredOnlySink, contexts); } inferredOnlySink.clear(contexts); setStatementsRemoved(); From 1640d93833622b83f94ab0cd2c21f32e6d629ab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Fri, 3 Oct 2025 11:24:01 +0200 Subject: [PATCH 35/46] add failing test --- .../sparql/tests/SparqlMinusScopingTests.java | 516 ++++++++++++++++++ 1 file changed, 516 insertions(+) create mode 100644 testsuites/sparql/src/main/java/org/eclipse/rdf4j/testsuite/sparql/tests/SparqlMinusScopingTests.java diff --git a/testsuites/sparql/src/main/java/org/eclipse/rdf4j/testsuite/sparql/tests/SparqlMinusScopingTests.java b/testsuites/sparql/src/main/java/org/eclipse/rdf4j/testsuite/sparql/tests/SparqlMinusScopingTests.java new file mode 100644 index 00000000000..e722be7aea1 --- /dev/null +++ b/testsuites/sparql/src/main/java/org/eclipse/rdf4j/testsuite/sparql/tests/SparqlMinusScopingTests.java @@ -0,0 +1,516 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + ******************************************************************************/ + +package org.eclipse.rdf4j.testsuite.sparql.tests; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.MalformedQueryException; +import org.eclipse.rdf4j.query.QueryResults; +import org.eclipse.rdf4j.query.TupleQuery; +import org.eclipse.rdf4j.query.TupleQueryResult; +import org.eclipse.rdf4j.repository.Repository; +import org.eclipse.rdf4j.repository.RepositoryConnection; +import org.eclipse.rdf4j.rio.RDFFormat; +import org.eclipse.rdf4j.testsuite.sparql.AbstractComplianceTest; +import org.junit.jupiter.api.DynamicTest; + +public class SparqlMinusScopingTests extends AbstractComplianceTest { + + public SparqlMinusScopingTests(Supplier repo) { + super(repo); + } + + private static final String NS = "http://ex/"; + private static final String PREFIX = "PREFIX : \n"; + + private static final String TTL = String.join("\n", + "@prefix : .", + ":a :p 1 . :a :q 10 . :a :r 100 .", + ":b :p 2 . :b :q 20 . :b :r 200 .", + ":c :p 3 . :c :r 300 .", + ":d :q 40 . :d :r 400 .", + ":e :p 5 . :e :q 50 ." + ); + + // ---------- Helpers + + private static List select(RepositoryConnection conn, String body) throws IOException { + String sparql = PREFIX + body; + + conn.add(new StringReader(TTL), "", RDFFormat.TURTLE); + + TupleQuery q = conn.prepareTupleQuery(sparql); + try (TupleQueryResult r = q.evaluate()) { + return QueryResults.asList(r); + } + + } + + /** + * Run a SELECT with a custom dataset (clears the connection first). + */ + private static List selectWithData(RepositoryConnection conn, String data, RDFFormat fmt, String body) + throws IOException { + String sparql = PREFIX + body; + + conn.clear(); + conn.add(new StringReader(data), NS, fmt); + + TupleQuery q = conn.prepareTupleQuery(sparql); + try (TupleQueryResult r = q.evaluate()) { + return QueryResults.asList(r); + } + } + + private static Set names(List rows, String var) { + return rows.stream() + .map(bs -> bs.getValue(var)) + .filter(Objects::nonNull) + .map(SparqlMinusScopingTests::name) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + + private static Set pairs(List rows, String var1, String var2) { + return rows.stream() + .map(bs -> { + Value v1 = bs.getValue(var1); + Value v2 = bs.getValue(var2); + return (v1 != null && v2 != null) ? name(v1) + "|" + name(v2) : null; + }) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + + private static Set triples(List rows, String v1, String v2, String v3) { + return rows.stream() + .map(bs -> { + Value a = bs.getValue(v1), b = bs.getValue(v2), c = bs.getValue(v3); + return (a != null && b != null && c != null) ? name(a) + "|" + name(b) + "|" + name(c) : null; + }) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + + private static String name(Value v) { + if (v instanceof IRI) { + IRI iri = (IRI) v; + return iri.getLocalName(); // ex:a -> "a" + } + return v.stringValue(); + } + + private static Set setOf(String... items) { + return new LinkedHashSet<>(Arrays.asList(items)); + } + + void T1_bindCreatesFreshVarInRight_NoOverlap_NoEffect(RepositoryConnection conn) throws IOException { + List rows = select(conn, + "SELECT ?s ?pVal WHERE {\n" + + " ?s :p ?pVal .\n" + + " MINUS { ?x :q ?qVal . BIND(?qVal*2 AS ?fresh) }\n" + + "}" + ); + assertEquals(4, rows.size()); + assertEquals(setOf("a", "b", "c", "e"), names(rows, "s")); + } + + void T3_bindBeforeUseIntroducesOverlap_EverythingRemoved(RepositoryConnection conn) throws IOException { + List rows = select(conn, + "SELECT ?s ?qVal WHERE {\n" + + " ?s :q ?qVal .\n" + + " MINUS { ?t :q ?x . BIND(?x AS ?qVal) }\n" + + "}" + ); + assertTrue(rows.isEmpty(), "All ?qVal values appear on the right after BIND, so MINUS removes all left rows"); + } + + void T4_renamedVarsInsideRight_NoTrueOverlap_NoEffect(RepositoryConnection conn) throws IOException { + List rows = select(conn, + "SELECT ?s ?pVal WHERE {\n" + + " ?s :p ?pVal .\n" + + " MINUS { ?s2 :p ?pVal2 . BIND(?pVal2 AS ?pVal_tmp) }\n" + + "}" + ); + assertEquals(4, rows.size()); + assertEquals(setOf("a", "b", "c", "e"), names(rows, "s")); + } + + void T5_randInsideDisjointRight_MinusHasNoEffect(RepositoryConnection conn) throws IOException { + List rows = select(conn, + "SELECT ?s WHERE {\n" + + " ?s :p ?v .\n" + + " MINUS { ?x :q ?w . FILTER(RAND() < 2) }\n" + // disjoint vars -> MINUS no effect by spec + "}" + ); + assertEquals(4, rows.size(), "Disjoint-variable MINUS must not remove any rows, regardless of RAND()"); + assertEquals(setOf("a", "b", "c", "e"), names(rows, "s")); + } + + void T8_projectionExprOnLeftDoesNotAffectMinusOverlap_NoEffect(RepositoryConnection conn) throws IOException { + List rows = select(conn, + "SELECT ?s ?pVal (STR(?s) AS ?z) WHERE {\n" + + " ?s :p ?pVal .\n" + + " MINUS { ?x :q ?q . BIND(STR(?x) AS ?z) }\n" + // ?z only exists on the right (left ?z is + // projection-time) + "}" + ); + assertEquals(4, rows.size()); + assertEquals(setOf("a", "b", "c", "e"), names(rows, "s")); + } + + void T9_projectionBeforeMinus_NoSharedVarsAfterSubselect_NoEffect(RepositoryConnection conn) throws IOException { + List rows = select(conn, + "SELECT ?s WHERE {\n" + + " { SELECT ?s WHERE { ?s :p ?v } }\n" + + " MINUS { ?x :p ?v }\n" + // ?v not projected to the outer level; disjoint wrt left (?s) + "}" + ); + assertEquals(4, rows.size()); + assertEquals(setOf("a", "b", "c", "e"), names(rows, "s")); + } + + void T10_minusVsNotExists_WithThisDataTheyCoincide(RepositoryConnection conn) throws IOException { + List minusRows = select(conn, + "SELECT ?s WHERE {\n" + + " ?s :p ?v .\n" + + " MINUS { ?s :q ?w }\n" + + "}" + ); + assertEquals(setOf("c"), names(minusRows, "s")); + + List notExistsRows = select(conn, + "SELECT ?s WHERE {\n" + + " ?s :p ?v .\n" + + " FILTER NOT EXISTS { ?s :q ?w }\n" + + "}" + ); + assertEquals(setOf("c"), names(notExistsRows, "s")); + } + + void T11_multipleMinus_sharedThenIndependent_onlyFirstMatters(RepositoryConnection conn) throws IOException { + List rows = select(conn, + "SELECT ?s WHERE {\n" + + " ?s :p ?v .\n" + + " MINUS { ?s :q ?w } # removes a, b, e\n" + + " MINUS { ?x :r ?r } # no shared vars -> no further effect\n" + + "}" + ); + assertEquals(setOf("c"), names(rows, "s")); + } + + void T12_minusInsideOptional_affectsOnlyOptionalGroup(RepositoryConnection conn) throws IOException { + List rows = select(conn, + "SELECT ?s ?maybe WHERE {\n" + + " ?s :p ?v .\n" + + " OPTIONAL {\n" + + " BIND(1 AS ?maybe)\n" + + " MINUS { ?s :q ?w }\n" + + " }\n" + + "}" + ); + + // Build subject -> hasMaybe mapping + Map hasMaybe = new LinkedHashMap<>(); + for (BindingSet bs : rows) { + String s = name(bs.getValue("s")); + boolean bound = bs.hasBinding("maybe"); + hasMaybe.put(s, bound); + } + + // With the dataset, only :c lacks :q, so OPTIONAL survives only for c. + assertEquals(4, rows.size()); + assertEquals(Boolean.FALSE, hasMaybe.get("a")); + assertEquals(Boolean.FALSE, hasMaybe.get("b")); + assertEquals(Boolean.TRUE, hasMaybe.get("c")); + assertEquals(Boolean.FALSE, hasMaybe.get("e")); + } + + void T13_minus_no_shared_vars_is_noop_select(RepositoryConnection conn) throws IOException { + String ttl = "@prefix : .\n" + + ":a :p 1 . :a :q 1 . :b :p 1 ."; + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?s WHERE { ?s :p 1 MINUS { ?x :q 1 } }"); + assertEquals(setOf("a", "b"), names(rows, "s"), + "MINUS with disjoint var-sets must keep the LHS intact (§8.3)."); + } + + void T14_not_exists_contrast_to_minus_no_shared_vars(RepositoryConnection conn) throws IOException { + String ttl = "@prefix : .\n" + + ":a :p 1 . :a :q 1 . :b :p 1 ."; + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?s WHERE { ?s :p 1 FILTER NOT EXISTS { ?x :q 1 } }"); + assertTrue(rows.isEmpty(), "NOT EXISTS is correlated and removes all rows when { ?x :q 1 } exists (§8.3)."); + } + + void T15_rhs_filter_referencing_outer_var_is_unbound_and_ignored(RepositoryConnection conn) throws IOException { + String ttl = "@prefix : .\n" + + ":a :p 1 ; :q 1, 2 .\n" + + ":b :p 3 ; :q 4 ."; + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?x ?n WHERE {\n" + + " ?x :p ?n .\n" + + " MINUS { ?x :q ?m . FILTER(?m = ?n) } # ?n unbound on RHS → filter errors → RHS empty\n" + + "} ORDER BY ?x"); + assertEquals(setOf("a|1", "b|3"), pairs(rows, "x", "n"), + "RHS filter sees no outer vars under MINUS; subtract nothing (§8.3)."); + } + + void T16_rhs_bind_of_outer_var_produces_unbound_then_overremoves_on_shared_subset(RepositoryConnection conn) + throws IOException { + String ttl = "@prefix : .\n" + + ":a :p 1 ; :q 1, 2 .\n" + + ":b :p 3 ; :q 4 .\n" + + ":c :p 7 ."; + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?x WHERE {\n" + + " ?x :p ?n .\n" + + " MINUS { BIND(?n AS ?k) ?x :q ?k }\n" + + "} ORDER BY ?x"); + assertEquals(setOf("c"), names(rows, "x"), + "RHS BIND on unbound outer var must not correlate; shared-vars logic should remove :a,:b only."); + } + + void T17_rhs_bind_creates_intentional_shared_var_equalities(RepositoryConnection conn) throws IOException { + String ttl = "@prefix : .\n" + + ":e :p 10 ; :q 42 .\n" + + ":f :p 20 ; :q 20 .\n" + + ":g :p 30 ; :q 99 ."; + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?x WHERE {\n" + + " ?x :p ?v .\n" + + " MINUS { ?x :q ?m . BIND(?m AS ?v) } # removes only when q==p\n" + + "} ORDER BY ?x"); + assertEquals(setOf("e", "g"), names(rows, "x"), + "Only :f should be removed (q==p). Early projection must NOT change shared vars."); + } + + void T18_early_projection_should_not_change_minus_semantics(RepositoryConnection conn) throws IOException { + String ttl = "@prefix : .\n" + + ":e :p 10 ; :q 42 .\n" + + ":f :p 20 ; :q 20 .\n" + + ":g :p 30 ; :q 99 ."; + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?x WHERE { ?x :p ?v MINUS { ?x :q ?v } } ORDER BY ?x"); + assertEquals(setOf("e", "g"), names(rows, "x"), + "Pushing projection before MINUS would wrongly remove :e and :g; don't do that."); + } + + void T19_subquery_pins_shared_var_against_optimizer(RepositoryConnection conn) throws IOException { + String ttl = "@prefix : .\n" + + ":e :p 10 ; :q 42 .\n" + + ":f :p 20 ; :q 20 .\n" + + ":g :p 30 ; :q 99 ."; + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?x WHERE {\n" + + " { SELECT ?x ?v WHERE { ?x :p ?v } } # box the LHS\n" + + " MINUS { ?x :q ?v }\n" + + "} ORDER BY ?x"); + assertEquals(setOf("e", "g"), names(rows, "x"), "Subquery must preserve shared vars until MINUS."); + } + + void T20_optional_inside_minus_only_removes_when_optional_matches(RepositoryConnection conn) throws IOException { + String ttl = "@prefix : .\n" + + ":e :name \"Alice\" ; :formerName \"Alice\" .\n" + + ":f :name \"Carol\" ."; + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?x WHERE {\n" + + " ?x :name ?n .\n" + + " MINUS { OPTIONAL { ?x :formerName ?n } }\n" + + "} ORDER BY ?x"); + assertEquals(setOf("f"), names(rows, "x"), + "OPTIONAL inside MINUS: only rows for which the OPTIONAL binds compatibly are removed."); + } + + void T21_not_exists_over_optional_is_always_false_here(RepositoryConnection conn) throws IOException { + String ttl = "@prefix : .\n" + + ":e :name \"Alice\" ; :formerName \"Alice\" .\n" + + ":f :name \"Carol\" ."; + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?x WHERE {\n" + + " ?x :name ?n .\n" + + " FILTER NOT EXISTS { OPTIONAL { ?x :formerName ?n } }\n" + + "}"); + assertEquals(List.of(), rows, + "Rewriting MINUS{OPTIONAL{…}} to NOT EXISTS { OPTIONAL{…} } is wrong: the inner group always yields at least the empty mapping."); + } + + void T22_graph_isolation_same_g_on_both_sides_no_removal_when_values_differ(RepositoryConnection conn) + throws IOException { + String trig = "@prefix : .\n" + + "GRAPH :g1 { :a :p 1 . }\n" + + "GRAPH :g2 { :a :q 1 . :a :p 2 . }"; + List rows = selectWithData(conn, trig, RDFFormat.TRIG, + "SELECT ?g ?x ?n WHERE {\n" + + " GRAPH ?g { ?x :p ?n }\n" + + " MINUS { GRAPH ?g { ?x :q ?n } }\n" + + "} ORDER BY ?g ?x ?n"); + assertEquals(setOf("g1|a|1", "g2|a|2"), triples(rows, "g", "x", "n"), + "Active graph must be respected on the RHS as well (§13.3)."); + } + + void T23_graph_isolation_removes_only_in_graph_where_match_exists(RepositoryConnection conn) throws IOException { + String trig = "@prefix : .\n" + + "GRAPH :g1 { :a :p 1 . }\n" + + "GRAPH :g2 { :a :q 1 . :a :p 2 . :a :q 2 . }"; + List rows = selectWithData(conn, trig, RDFFormat.TRIG, + "SELECT ?g ?x ?n WHERE {\n" + + " GRAPH ?g { ?x :p ?n }\n" + + " MINUS { GRAPH ?g { ?x :q ?n } }\n" + + "} ORDER BY ?g"); + assertEquals(setOf("g1|a|1"), triples(rows, "g", "x", "n"), + "Only the :g2 row should be removed because :q 2 exists in :g2."); + } + + void T24_minus_disjoint_varsets_is_noop_even_with_union_on_lhs(RepositoryConnection conn) throws IOException { + String ttl = "@prefix : .\n" + + ":a :p 1 ; :q 1 .\n" + + ":b :p 1 .\n" + + ":c :p 2 ."; + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?x WHERE {\n" + + " { ?x :p 1 } UNION { ?x :p 2 }\n" + + " MINUS { ?y :q 1 }\n" + + "} ORDER BY ?x"); + assertEquals(setOf("a", "b", "c"), names(rows, "x"), "No shared vars → MINUS must be a no-op (§8.3)."); + } + + void T25_values_left_only_no_shared_vars_is_noop(RepositoryConnection conn) throws IOException { + String ttl = "@prefix : . :a :q 1 ."; + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?a WHERE { VALUES ?a { 1 2 } MINUS { ?x :q 1 } } ORDER BY ?a"); + assertEquals(setOf("1", "2"), names(rows, "a"), + "VALUES introduces no shared vars with RHS, so MINUS removes nothing."); + } + + void T26_minus_shared_subset_only_subject_shared_removes_all_rows_for_that_subject(RepositoryConnection conn) + throws IOException { + String ttl = "@prefix : .\n" + + ":a :p 1 ; :q 99 .\n" + + ":b :p 2 ."; + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?x ?n WHERE { ?x :p ?n MINUS { ?x :q ?m } } ORDER BY ?x"); + assertEquals(setOf("b|2"), pairs(rows, "x", "n"), + "Since only ?x is shared, any :q for :a kills *all* its :p rows."); + } + + void T27_rhs_subselect_order_by_limit_one_global_elimination(RepositoryConnection conn) throws IOException { + String ttl = "@prefix : .\n" + + ":a :p 1 ; :q 1 .\n" + + ":b :p 2 ; :q 2 .\n" + + ":c :p 3 ; :q 3 ."; + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?x WHERE {\n" + + " ?x :p ?n .\n" + + " MINUS {\n" + + " { SELECT ?x WHERE { ?x :q ?m } ORDER BY ?m LIMIT 1 }\n" + + " }\n" + + "} ORDER BY ?x"); + assertEquals(setOf("b", "c"), names(rows, "x"), + "Flattening/pushing ORDER BY/LIMIT across MINUS would change which row is removed."); + } + + void T28_bnode_function_on_rhs_cannot_match_data_terms(RepositoryConnection conn) throws IOException { + String ttl = "@prefix : .\n" + + "_:b1 a [] . _:b2 a [] .\n" + + ":k :p _:b1 . :l :p _:b2 ."; + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?s WHERE { ?s :p ?id MINUS { BIND(BNODE() AS ?id) } } ORDER BY ?s"); + assertEquals(setOf("k", "l"), names(rows, "s"), + "BNODE() creates fresh, distinct bnodes – cannot match dataset objects, so MINUS is a no-op here."); + } + + void T29_syntax_error_rebinding_in_rhs_must_fail_to_parse(RepositoryConnection conn) { + String body = "SELECT * WHERE {\n" + + " ?x :p ?v .\n" + + " MINUS { ?x :q ?v . BIND(1 AS ?v) } # re-binding ?v inside same RHS group is illegal\n" + + "}"; + assertThrows(MalformedQueryException.class, () -> conn.prepareTupleQuery(PREFIX + body), + "BIND target must not have been used earlier in the same group; parser should reject."); + } + + public Stream tests() { + + return Stream.of( + makeTest("T1_bindCreatesFreshVarInRight_NoOverlap_NoEffect", + this::T1_bindCreatesFreshVarInRight_NoOverlap_NoEffect), + makeTest("T3_bindBeforeUseIntroducesOverlap_EverythingRemoved", + this::T3_bindBeforeUseIntroducesOverlap_EverythingRemoved), + makeTest("T4_renamedVarsInsideRight_NoTrueOverlap_NoEffect", + this::T4_renamedVarsInsideRight_NoTrueOverlap_NoEffect), + makeTest("T5_randInsideDisjointRight_MinusHasNoEffect", + this::T5_randInsideDisjointRight_MinusHasNoEffect), + makeTest("T8_projectionExprOnLeftDoesNotAffectMinusOverlap_NoEffect", + this::T8_projectionExprOnLeftDoesNotAffectMinusOverlap_NoEffect), + makeTest("T9_projectionBeforeMinus_NoSharedVarsAfterSubselect_NoEffect", + this::T9_projectionBeforeMinus_NoSharedVarsAfterSubselect_NoEffect), + makeTest("T10_minusVsNotExists_WithThisDataTheyCoincide", + this::T10_minusVsNotExists_WithThisDataTheyCoincide), + makeTest("T11_multipleMinus_sharedThenIndependent_onlyFirstMatters", + this::T11_multipleMinus_sharedThenIndependent_onlyFirstMatters), + makeTest("T12_minusInsideOptional_affectsOnlyOptionalGroup", + this::T12_minusInsideOptional_affectsOnlyOptionalGroup), + makeTest("T13_minus_no_shared_vars_is_noop_select", this::T13_minus_no_shared_vars_is_noop_select), + makeTest("T14_not_exists_contrast_to_minus_no_shared_vars", + this::T14_not_exists_contrast_to_minus_no_shared_vars), + makeTest("T15_rhs_filter_referencing_outer_var_is_unbound_and_ignored", + this::T15_rhs_filter_referencing_outer_var_is_unbound_and_ignored), + makeTest("T16_rhs_bind_of_outer_var_produces_unbound_then_overremoves_on_shared_subset", + this::T16_rhs_bind_of_outer_var_produces_unbound_then_overremoves_on_shared_subset), + makeTest("T17_rhs_bind_creates_intentional_shared_var_equalities", + this::T17_rhs_bind_creates_intentional_shared_var_equalities), + makeTest("T18_early_projection_should_not_change_minus_semantics", + this::T18_early_projection_should_not_change_minus_semantics), + makeTest("T19_subquery_pins_shared_var_against_optimizer", + this::T19_subquery_pins_shared_var_against_optimizer), + makeTest("T20_optional_inside_minus_only_removes_when_optional_matches", + this::T20_optional_inside_minus_only_removes_when_optional_matches), + makeTest("T21_not_exists_over_optional_is_always_false_here", + this::T21_not_exists_over_optional_is_always_false_here), + makeTest("T22_graph_isolation_same_g_on_both_sides_no_removal_when_values_differ", + this::T22_graph_isolation_same_g_on_both_sides_no_removal_when_values_differ), + makeTest("T23_graph_isolation_removes_only_in_graph_where_match_exists", + this::T23_graph_isolation_removes_only_in_graph_where_match_exists), + makeTest("T24_minus_disjoint_varsets_is_noop_even_with_union_on_lhs", + this::T24_minus_disjoint_varsets_is_noop_even_with_union_on_lhs), + makeTest("T25_values_left_only_no_shared_vars_is_noop", + this::T25_values_left_only_no_shared_vars_is_noop), + makeTest("T26_minus_shared_subset_only_subject_shared_removes_all_rows_for_that_subject", + this::T26_minus_shared_subset_only_subject_shared_removes_all_rows_for_that_subject), + makeTest("T27_rhs_subselect_order_by_limit_one_global_elimination", + this::T27_rhs_subselect_order_by_limit_one_global_elimination), + makeTest("T28_bnode_function_on_rhs_cannot_match_data_terms", + this::T28_bnode_function_on_rhs_cannot_match_data_terms), + makeTest("T29_syntax_error_rebinding_in_rhs_must_fail_to_parse", + this::T29_syntax_error_rebinding_in_rhs_must_fail_to_parse) + ); + + } + +} From 9e922349db36c28cb6712aba1c333c21b74d0485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Fri, 3 Oct 2025 11:26:23 +0200 Subject: [PATCH 36/46] add failing test --- .../sparql/RepositorySPARQLComplianceTestSuite.java | 6 ++++++ .../testsuite/sparql/tests/SparqlMinusScopingTests.java | 3 +-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/testsuites/sparql/src/main/java/org/eclipse/rdf4j/testsuite/sparql/RepositorySPARQLComplianceTestSuite.java b/testsuites/sparql/src/main/java/org/eclipse/rdf4j/testsuite/sparql/RepositorySPARQLComplianceTestSuite.java index cfea6a32b25..362ca5d2026 100644 --- a/testsuites/sparql/src/main/java/org/eclipse/rdf4j/testsuite/sparql/RepositorySPARQLComplianceTestSuite.java +++ b/testsuites/sparql/src/main/java/org/eclipse/rdf4j/testsuite/sparql/RepositorySPARQLComplianceTestSuite.java @@ -38,6 +38,7 @@ import org.eclipse.rdf4j.testsuite.sparql.tests.OptionalTest; import org.eclipse.rdf4j.testsuite.sparql.tests.OrderByTest; import org.eclipse.rdf4j.testsuite.sparql.tests.PropertyPathTest; +import org.eclipse.rdf4j.testsuite.sparql.tests.SparqlMinusScopingTests; import org.eclipse.rdf4j.testsuite.sparql.tests.SubselectTest; import org.eclipse.rdf4j.testsuite.sparql.tests.UnionTest; import org.eclipse.rdf4j.testsuite.sparql.tests.ValuesTest; @@ -153,6 +154,11 @@ Stream minus() throws RDF4JException, IOException { return new MinusTest(this::getEmptyInitializedRepository).tests(); } + @TestFactory + Stream minusScope() throws RDF4JException, IOException { + return new SparqlMinusScopingTests(this::getEmptyInitializedRepository).tests(); + } + @BeforeAll public static void setUpClass() { System.setProperty("org.eclipse.rdf4j.repository.debug", "true"); diff --git a/testsuites/sparql/src/main/java/org/eclipse/rdf4j/testsuite/sparql/tests/SparqlMinusScopingTests.java b/testsuites/sparql/src/main/java/org/eclipse/rdf4j/testsuite/sparql/tests/SparqlMinusScopingTests.java index e722be7aea1..0094a510989 100644 --- a/testsuites/sparql/src/main/java/org/eclipse/rdf4j/testsuite/sparql/tests/SparqlMinusScopingTests.java +++ b/testsuites/sparql/src/main/java/org/eclipse/rdf4j/testsuite/sparql/tests/SparqlMinusScopingTests.java @@ -357,8 +357,7 @@ void T21_not_exists_over_optional_is_always_false_here(RepositoryConnection conn " ?x :name ?n .\n" + " FILTER NOT EXISTS { OPTIONAL { ?x :formerName ?n } }\n" + "}"); - assertEquals(List.of(), rows, - "Rewriting MINUS{OPTIONAL{…}} to NOT EXISTS { OPTIONAL{…} } is wrong: the inner group always yields at least the empty mapping."); + assertEquals(List.of(), rows); } void T22_graph_isolation_same_g_on_both_sides_no_removal_when_values_differ(RepositoryConnection conn) From 1c82151ab2157fe5fa41ec3ab6028b40009f99b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Fri, 3 Oct 2025 13:18:29 +0200 Subject: [PATCH 37/46] add failing test --- .../MemoryStoreMinusScopingDebugTest.java | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java diff --git a/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java new file mode 100644 index 00000000000..5d392f78891 --- /dev/null +++ b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java @@ -0,0 +1,130 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + ******************************************************************************/ + +package org.eclipse.rdf4j.sail.memory; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.QueryResults; +import org.eclipse.rdf4j.query.TupleQuery; +import org.eclipse.rdf4j.query.TupleQueryResult; +import org.eclipse.rdf4j.repository.RepositoryConnection; +import org.eclipse.rdf4j.repository.sail.SailRepository; +import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection; +import org.eclipse.rdf4j.rio.RDFFormat; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Local duplicates of selected {@link org.eclipse.rdf4j.testsuite.sparql.tests.SparqlMinusScopingTests} cases to ease + * debugging against a concrete {@link MemoryStore} repository. + */ +public class MemoryStoreMinusScopingDebugTest { + + private static final String PREFIX = "PREFIX : \n"; + private static final String DATA_BASE_IRI = "http://ex/"; + + private SailRepository repository; + + @BeforeEach + public void setUp() { + repository = new SailRepository(new MemoryStore()); + repository.init(); + } + + @AfterEach + public void tearDown() { + if (repository != null) { + repository.shutDown(); + } + } + + @Test + public void T16_rhs_bind_of_outer_var_produces_unbound_then_overremoves_on_shared_subset() throws IOException { + String ttl = "@prefix : .\n" + + ":a :p 1 ; :q 1, 2 .\n" + + ":b :p 3 ; :q 4 .\n" + + ":c :p 7 ."; + + try (SailRepositoryConnection conn = repository.getConnection()) { + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?x WHERE {\n" + + " ?x :p ?n .\n" + + " MINUS { BIND(?n AS ?k) ?x :q ?k }\n" + + "} ORDER BY ?x"); + + assertEquals(setOf("c"), names(rows, "x"), + "RHS BIND on unbound outer var must not correlate; shared-vars logic should remove :a,:b only."); + } + } + + @Test + public void T21_not_exists_over_optional_is_always_false_here() throws IOException { + String ttl = "@prefix : .\n" + + ":e :name \"Alice\" ; :formerName \"Alice\" .\n" + + ":f :name \"Carol\" ."; + + try (SailRepositoryConnection conn = repository.getConnection()) { + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?x WHERE {\n" + + " ?x :name ?n .\n" + + " FILTER NOT EXISTS { OPTIONAL { ?x :formerName ?n } }\n" + + "}"); + + assertEquals(List.of(), rows); + } + } + + private List selectWithData(RepositoryConnection conn, String data, RDFFormat format, String body) + throws IOException { + String sparql = PREFIX + body; + + conn.clear(); + conn.add(new StringReader(data), DATA_BASE_IRI, format); + + TupleQuery query = conn.prepareTupleQuery(sparql); + try (TupleQueryResult result = query.evaluate()) { + return QueryResults.asList(result); + } + } + + private static Set names(List rows, String var) { + return rows.stream() + .map(bs -> bs.getValue(var)) + .filter(Objects::nonNull) + .map(MemoryStoreMinusScopingDebugTest::name) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + + private static String name(Value value) { + if (value instanceof IRI) { + return ((IRI) value).getLocalName(); + } + return value.stringValue(); + } + + private static Set setOf(String... values) { + return new LinkedHashSet<>(Arrays.asList(values)); + } +} From a73da1d79d5fb9c82d6699f936d2aa3a88ecc092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Fri, 3 Oct 2025 13:18:42 +0200 Subject: [PATCH 38/46] add failing test --- .../rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java index 5d392f78891..356c051ff58 100644 --- a/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java +++ b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java @@ -36,10 +36,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -/** - * Local duplicates of selected {@link org.eclipse.rdf4j.testsuite.sparql.tests.SparqlMinusScopingTests} cases to ease - * debugging against a concrete {@link MemoryStore} repository. - */ public class MemoryStoreMinusScopingDebugTest { private static final String PREFIX = "PREFIX : \n"; From 16e49c28327c05bd8dccb121ba62b896bdda458e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Fri, 3 Oct 2025 13:20:46 +0200 Subject: [PATCH 39/46] add failing test --- AGENTS_TEMP.md | 713 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 713 insertions(+) create mode 100644 AGENTS_TEMP.md diff --git a/AGENTS_TEMP.md b/AGENTS_TEMP.md new file mode 100644 index 00000000000..4a00fb1b000 --- /dev/null +++ b/AGENTS_TEMP.md @@ -0,0 +1,713 @@ +# AGENTS.md + +Welcome, AI Agent! Your persistence, curiosity, and craftsmanship make a difference. Take your time, work methodically, validate thoroughly, and iterate. This repository is large and tests can take time — that’s expected and supported. + +You need to read the entire AGENTS.md file and follow all instructions exactly. Keep this fresh in your context as you work. + +> **Timebox:** Aim to complete each autonomous run in **15–30 minutes**. + +--- + +## Read‑Me‑Now: Proportional Test‑First Rule (Default) + +**Default:** Use **test‑first (TDD)** for any change that alters externally observable behavior. + +**Proportional exceptions:** You may **skip writing a new failing test** *only* when **all** Routine B gates (below) pass, or when using Routine C (Spike/Investigate) with **no production code changes**. + +**You may not touch production code for behavior‑changing work until a smallest‑scope failing automated test exists inside this repo and you have captured its report snippet.** A user‑provided stack trace or “obvious” contract violation is **not** a substitute for an in‑repo failing test. + +**Auto‑stop:** If you realize you patched production before creating/observing the failing test for behavior‑changing work, **stop**, revert the patch, and resume from “Reproduce first”. + +**Traceability trio (must appear in your handoff):** +1. **Descritpion** (what you’re about to do) +2. **Evidence** (Surefire/Failsafe snippet from this repo) +3. **Plan** (one and only one `in_progress` step) + +It is illegal to `-am` when running tests! +It is illegal to `-q` when running tests! + +> **Clarification:** For **strictly behavior‑neutral refactors** that are already **fully exercised by existing tests**, or for **bugfixes with an existing failing test**, you may use **Routine B — Change without new tests**. In that case you must capture **pre‑change passing evidence** at the smallest scope that hits the code you’re about to edit, prove **Hit Proof**, then show **post‑change passing evidence** from the **same selection**. +> **No exceptions for any behavior‑changing change** — for those, you must follow **Routine A — Full TDD**. + +--- + +## Three Routines: Choose Your Path + +**Routine A — Full TDD (Default)** +**Routine B — Change without new tests (Proportional, gated)** +**Routine C — Spike/Investigate (No production changes)** + +### Decision quickstart + +1. **Is new externally observable behavior required?** + → **Yes:** **Routine A (Full TDD)**. Add the smallest failing test first. + → **No:** continue. + +2. **Does a failing test already exist in this repo that pinpoints the issue?** + → **Yes:** **Routine B (Bugfix using existing failing test).** + → **No:** continue. + +3. **Is the edit strictly behavior‑neutral, local in scope, and clearly hit by existing tests?** + → **Yes:** **Routine B (Refactor/micro‑perf/documentation/build).** + → **No or unsure:** continue. + +4. **Is this purely an investigation/design spike with no production code changes?** + → **Yes:** **Routine C (Spike/Investigate).** + → **No or unsure:** **Routine A.** + +**When in doubt, choose Routine A (Full TDD).** Ambiguity is risk; tests are insurance. + +--- + +## Proportionality Model (Think before you test) + +Score the change on these lenses. If any are **High**, prefer **Routine A**. + +- **Behavioral surface:** affects outputs, serialization, parsing, APIs, error text, timing/order? +- **Blast radius:** number of modules/classes touched; public vs internal. +- **Reversibility:** quick revert vs migration/data change. +- **Observability:** can existing tests or assertions expose regressions? +- **Coverage depth:** do existing tests directly hit the edited code? +- **Concurrency / IO / Time:** any risk here is **High** by default. + +--- + +## Purpose & Contract + +* **Bold goal:** deliver correct, minimal, well‑tested changes with clear handoff. Fix root causes; avoid hacks. +* **Bias to action:** when inputs are ambiguous, choose a reasonable path, state assumptions, and proceed. +* **Ask only when blocked or irreversible:** permissions, missing deps, conflicting requirements, destructive repo‑wide changes. +* **Definition of Done** + * Code formatted and imports sorted. + * Compiles with a quick profile / targeted modules. + * Relevant module tests pass; failures triaged or crisply explained. + * Only necessary files changed; headers correct for new files. + * Clear final summary: what changed, why, where, how verified, next steps. + * **Evidence present:** failing test output (pre‑fix) and passing output (post‑fix) are shown for Routine A; for Routine B show **pre/post green** from the **same selection** plus **Hit Proof**. + +### No Monkey‑Patching or Band‑Aid Fixes (Non‑Negotiable) + +Durable, root‑cause fixes only. No muting tests, no broad catch‑and‑ignore, no widening APIs “to make green”. + +**Strictly avoid** +* Sleeping/timeouts to hide flakiness. +* Swallowing exceptions or weakening assertions. +* Reflection/internal state manipulation to bypass interfaces. +* Feature flags that disable validation instead of fixing logic. +* Changing public APIs/configs without necessity tied to root cause. + +**Preferred approach** +* Reproduce the issue and isolate the smallest failing test (class → method). +* Trace to the true source; fix in the right module. +* Add focused tests for behavior/edge cases (Routine A) or prove coverage/neutrality (Routine B). +* Run tight, targeted verifies; broaden only if needed. + +--- + +## Enforcement & Auto‑Fail Triggers + +Your run is **invalid** and must be restarted from “Reproduce first” if any occur: + +* You modify production code before adding and running the smallest failing test in this repo **for behavior‑changing work**. +* You proceed without pasting a Surefire/Failsafe report snippet from `target/*-reports/`. +* Your plan does not have **exactly one** `in_progress` step. +* You run tests using `-am` or `-q`. +* You treat a narrative failure description or external stack trace as equivalent to an in‑repo failing test. +* **Routine B specific:** you cannot demonstrate that existing tests exercise the edited code (**Hit Proof**), or you fail to capture both pre‑ and post‑change **matching** passing snippets from the same selection. +* **Routine C breach:** you change production code while in a spike. + +**Recovery procedure:** +Update the plan (`in_progress: create failing test`), post a description of your next step, create the failing test, run it, capture the report snippet, then resume. +For Routine B refactors: if any gate fails, **switch to Full TDD** and add the smallest failing test. + +--- + +## Evidence Protocol (Mandatory) + +After each grouped action, post an **Evidence block**, then continue working: + +**Evidence template** +``` +Evidence: +Command: mvn -o -pl -Dtest=Class#method verify +Report: /target/surefire-reports/.txt +Snippet: +\ +``` + +**Routine B additions** +* **Pre‑green:** capture a pre‑change **passing** snippet from the **most specific** test selection that hits your code (ideally a class or method). +* **Hit Proof (choose one):** + * An existing test class/method that directly calls the edited class/method, plus a short `rg -n` snippet showing the call site; **or** + * A Surefire/Failsafe output line containing the edited class/method names; **or** + * A temporary assertion or deliberate, isolated failing check in a **scratch test** proving the path is executed (then remove). +* **Post‑green:** after the patch, re‑run the **same selection** and capture a passing snippet. + +--- + +### Initial Evidence Capture (Required) + +To avoid losing the first test evidence when later runs overwrite `target/*-reports/`, immediately persist the initial verify results to a top‑level `initial-evidence.txt` file. + +• On a fully green verify run: + +- Capture and store the last 200 lines of the Maven verify output. +- Example (module‑scoped): + - `mvn -o -pl verify | tee .initial-verify.log` + - `tail -200 .initial-verify.log > initial-evidence.txt` + +• On any failing verify run (unit or IT failures): + +- Concatenate the Surefire and/or Failsafe report text files into `initial-evidence.txt`. +- Example (repo‑root): + - `find . -type f \( -path "*/target/surefire-reports/*.txt" -o -path "*/target/failsafe-reports/*.txt" \) -print0 | xargs -0 cat > initial-evidence.txt` + +Notes + +- Keep `initial-evidence.txt` at the repository root alongside your final handoff. +- Do not rely on `target/*-reports/` for the final report; they may be overwritten by subsequent runs. +- Continue to include the standard Evidence block(s) in your messages as usual. + +--- + +## Living Plan Protocol (Sharper) + +Maintain a **living plan** with checklist items (5–7 words each). Keep **exactly one** `in_progress`. + +**Plan format** +``` + +Plan + +* \[done] sanity build quick profile +* \[in\_progress] add smallest failing test +* \[todo] minimal root-cause fix +* \[todo] rerun focused then module tests +* \[todo] format, verify, summary + +```` + +**Rule:** If you deviate, update the plan **first**, then proceed. + +--- + +## Environment + +* **JDK:** 11 (minimum). The project builds and runs on Java 11+. +* **Maven default:** run **offline** using `-o` whenever possible. +* **Network:** only to fetch missing deps/plugins; then rerun once without `-o`, and return offline. +* **Large project:** some module test suites can take **5–10 minutes**. Prefer **targeted** runs. + +### Maven `-am` usage (house rule) + +`-am` is helpful for **compiles**, hazardous for **tests**. + +* ✅ Use `-am` **only** for compile/verify with tests skipped (e.g. `-Pquick`): + * `mvn -o -pl -am -Pquick install` +* ❌ Do **not** use `-am` with `verify` when tests are enabled. + +**Two-step pattern (fast + safe)** +1. **Compile deps fast (skip tests):** + `mvn -o -pl -am -Pquick install` +2. **Run tests:** + `mvn -o -pl verify | tail -500` + +It is illegal to `-am` when running tests! +It is illegal to `-q` when running tests! + +--- + +## Always Install Before Tests (Required) + +The Maven reactor resolves inter-module dependencies from the local Maven repository (`~/.m2/repository`). +Running `install` publishes your changed modules there so downstream modules and tests pick up the correct versions. + +* Always run `mvn -o -Pquick install | tail -200` before you start working. This command typically takes up to 30 seconds. Never use a small timeout than 30,000 ms. +* Always run `mvn -o -Pquick install | tail -200` before any `verify` or test runs. +* If offline resolution fails due to a missing dependency or plugin, rerun the exact `install` command once without `-o`, then return offline. +* Skipping this step can lead to stale or missing artifacts during tests, producing confusing compilation or linkage errors. +* Never ever change the repo location. Never use `-Dmaven.repo.local=.m2_repo`. +* Always try to run these commands first to see if they run without needing any approvals from the user w.r.t. the sandboxing. +--- + +## Quick Start (First 10 Minutes) + +1. **Discover** + * Inspect root `pom.xml` and module tree (see “Maven Module Overview”). + * Search fast with ripgrep: `rg -n ""` +2. **Build sanity (fast, skip tests)** + * `mvn -o -Pquick install | tail -200` +3. **Format (Java, imports, XML)** + * `mvn -o -q -T 2C formatter:format impsort:sort xml-format:xml-format` +4. **Targeted tests (tight loops)** + * Module: `mvn -o -pl verify | tail -500` + * Class: `mvn -o -pl -Dtest=ClassName verify | tail -500` + * Method: `mvn -o -pl -Dtest=ClassName#method verify | tail -500` +5. **Inspect failures** + * **Unit (Surefire):** `/target/surefire-reports/` + * **IT (Failsafe):** `/target/failsafe-reports/` + +It is illegal to `-am` when running tests! +It is illegal to `-q` when running tests! + +--- + +## Routine A — Full TDD (Default) + +> Use for **all behavior‑changing work** and whenever Routine B gates do not all pass. + +### Bugfix Workflow (Mandatory) + +* **Reproduce first:** write the smallest focused test (class/method) that reproduces the reported bug **inside this repo**. Confirm it fails. +* **Keep the test as‑is:** do not weaken assertions or mute the failure. +* **Fix at the root:** minimal, surgical change in the correct module. +* **Verify locally:** re‑run the focused test, then the module’s tests. Avoid `-am`/`-q` with tests. +* **Broaden if needed:** expand scope only after targeted greens. +* **Document clearly:** failing output (pre‑fix), root cause, minimal fix, passing output (post‑fix). + +### Hard Gates + +* A failing test exists at the smallest scope (method/class). +* **No production patch before the failing test is observed and recorded.** +* Test runs avoid `-am` and `-q`. + +--- + +## Routine B — Change without new tests (Proportional, gated) + +> Use **only** when at least one Allowed Case applies **and** all Routine B **Gates** pass. + +### Allowed cases (one or more) +1. **Bugfix with existing failing test** in this repo (pinpoints class/method). +2. **Strictly behavior‑neutral refactor / cleanup / micro‑perf** with clear existing coverage hitting the edited path. +3. **Migration/rename/autogen refresh** where behavior is already characterized by existing tests. +4. **Build/CI/docs/logging/message changes** that do not alter runtime behavior or asserted outputs. +5. **Data/resource tweaks** not asserted by tests and not affecting behavior. + +### Routine B Gates (all must pass) +- **Neutrality/Scope:** No externally observable behavior change. Localized edit. +- **Hit Proof:** Demonstrate tests exercise the edited code. +- **Pre/Post Green Match:** Same smallest‑scope selection, passing before and after. +- **Risk Check:** No concurrency/time/IO semantics touched; no public API, serialization, parsing, or ordering changes. +- **Reversibility:** Change is easy to revert if needed. + +**If any gate fails → switch to Routine A.** + +--- + +## Routine C — Spike / Investigate (No production changes) + +> Use for exploration, triage, design spikes, and measurement. **No production code edits.** + +**You may:** +- Add temporary scratch tests, assertions, scripts, or notes. +- Capture measurements, traces, logs. + +**Hand‑off must include:** +- Description, commands, and artifacts (logs/notes). +- Findings, options, and a proposed next routine (A or B). +- Removal of any temporary code if not adopted. + +--- + +## Where to Draw the Line — A Short Debate + +> **Purist:** “All changes must start with a failing test.” +> **Pragmatist:** “For refactors that can’t fail first without faking it, prove coverage and equality of behavior.” + +**In‑scope for Routine B (examples)** +* Rename private methods; extract helper; dead‑code removal. +* Replace straightforward loop with stream (same results, same ordering). +* Tighten generics/nullability/annotations without observable change. +* Micro‑perf cache within a method with deterministic inputs and strong coverage. +* Logging/message tweaks **not** asserted by tests. +* Build/CI config that doesn’t alter runtime behavior. + +**Out‑of‑scope (use Routine A)** +* Changing query results, serialization, or parsing behavior. +* Altering error messages that tests assert. +* Anything touching concurrency, timeouts, IO, or ordering. +* New SPARQL function support or extended syntax (even “tiny”). +* Public API changes or cross‑module migrations with unclear blast radius. + +--- + +## Working Loop + +* **Plan:** small, verifiable steps; keep one `in_progress`. +* **Change:** minimal, surgical edits; keep style/structure consistent. +* **Format:** `mvn -o -q -T 2C formatter:format impsort:sort xml-format:xml-format` +* **Compile (fast):** `mvn -o -pl -am -Pquick install | tail -500` +* **Test:** start smallest (class/method → module). For integration, run module `verify`. +* **Triage:** read reports; fix root cause; expand scope only when needed. +* **Iterate:** keep momentum; escalate only when blocked or irreversible. + +It is illegal to `-am` when running tests! +It is illegal to `-q` when running tests! + +--- + +## Testing Strategy + +* **Prefer module tests you touched:** `-pl ` +* **Narrow further** to a class/method; then broaden to the module. +* **Expand scope** when changes cross boundaries or neighbor modules fail. +* **Read reports** + * Surefire (unit): `target/surefire-reports/` + * Failsafe (IT): `target/failsafe-reports/` +* **Helpful flags** + * `-Dtest=Class#method` (unit selection) + * `-Dit.test=ITClass#method` (integration selection) + * `-DtrimStackTrace=false` (full traces) + * `-DskipITs` (focus on unit tests) + * `-DfailIfNoTests=false` (when selecting a class that has no tests on some platforms) + +### Optional: Redirect test stdout/stderr to files +```bash +mvn -o -pl -Dtest=ClassName[#method] -Dmaven.test.redirectTestOutputToFile=true verify | tail -500 +```` + +Logs under: + +``` +/target/surefire-reports/ClassName-output.txt +``` + +(Use similarly for Failsafe via `-Dit.test=`.) + +--- + +## Assertions: Make invariants explicit + +Assertions are executable claims about what must be true. Use **temporary tripwires** during investigation and **permanent contracts** once an invariant matters. + +* One fact per assert; fail fast and usefully. +* Include stable context in messages; avoid side effects. +* Keep asserts cheap; don’t replace user input validation with asserts. + +**Java specifics** + +* Enable VM assertions in tests (`-ea`). +* Use exceptions for runtime guarantees; `assert` for “cannot happen”. + +(Concrete examples omitted here for brevity; keep your current patterns.) + +--- + +## Triage Playbook + +* **Missing dep/plugin offline:** rerun the exact command once **without** `-o`, then return offline. +* **Compilation errors:** fix imports/generics/visibility; quick install in the module. +* **Flaky/slow tests:** run the specific failing test; stabilize root cause before broad runs. +* **Formatting failures:** run formatter/import/XML sort; re‑verify. +* **License header missing:** add for **new** files only; do not change years on existing files. + +--- + +## Code Formatting + +* Always run before finalizing: + + * `mvn -o -q -T 2C formatter:format impsort:sort xml-format:xml-format` +* Style: no wildcard imports; 120‑char width; curly braces always; LF endings. + +--- + +## Source File Headers + +Use this exact header for **new Java files only** (replace `${year}` with current year): + +``` +/******************************************************************************* + * Copyright (c) ${year} Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +``` + +Do **not** modify existing headers’ years. + +--- + +## Pre‑Commit Checklist + +* **Format:** `mvn -o -q -T 2C formatter:format impsort:sort xml-format:xml-format` +* **Compile (fast path):** `mvn -o -Pquick install | tail -200` +* **Tests (targeted):** `mvn -o -pl verify | tail -500` (broaden as needed) +* **Reports:** zero new failures in Surefire/Failsafe, or explain precisely. +* **Evidence:** Routine A — failing pre‑fix + passing post‑fix. + Routine B — **pre/post green** from same selection + **Hit Proof**. + +--- + +## Branching & Commit Conventions + +* Branch names: start with `GH-XXXX` (GitHub issue number). Optional short slug, e.g., `GH-1234-trig-writer-check`. +* Commit messages: `GH-XXXX ` on every commit. + +--- + +## Branch & PR Workflow (Agent) + +* Confirm issue number first (mandatory). +* Branch: `git checkout -b GH-XXXX-your-slug` +* Stage: `git add -A` (ensure new Java files have the required header). +* Optional: formatter + quick install. +* Commit: `git commit -m "GH-XXXX "` +* Push & PR: use the default template; fill all fields; include `Fixes #XXXX`. + +--- + +## Navigation & Search + +* Files: `rg --files` +* Content: `rg -n ""` +* Read big files in chunks: + + * `sed -n '1,200p' path/to/File.java` + * `sed -n '201,400p' path/to/File.java` + +--- + +## Autonomy Rules (Act > Ask) + +* **Default:** act with assumptions; document them. +* **Keep going:** chain steps; short progress updates before long actions. +* **Ask only when:** blocked by sandbox/approvals/network, or change is destructive/irreversible, or impacts public APIs/dependencies/licensing. +* **Prefer reversible moves:** smallest local change that unblocks progress; validate with targeted tests first. + +**Defaults** + +* **Tests:** start with `-pl `, then `-Dtest=Class#method` / `-Dit.test=ITClass#method`. +* **Build:** use `-o`; drop `-o` once only to fetch; return offline. +* **Formatting:** run formatter/import/XML before verify. +* **Reports:** read surefire/failsafe locally; expand scope only when necessary. + +--- + +## Answer Template (Use This) + +* **What changed:** summary of approach and rationale. +* **Files touched:** list file paths. +* **Commands run:** key build/test commands. +* **Verification:** which tests passed, where you checked reports. +* **Evidence:** + *Routine A:* failing output (pre‑fix) and passing output (post‑fix). + *Routine B:* pre‑ and post‑green snippets from the **same selection** + **Hit Proof**. + *Routine C:* artifacts from investigation (logs/notes/measurements) and proposed next steps. +* **Assumptions:** key assumptions and autonomous decisions. +* **Limitations:** anything left or risky edge cases. +* **Next steps:** optional follow‑ups. + +--- + +## Running Tests + +* By module: `mvn -o -pl core/sail/shacl verify | tail -500` +* Entire repo: `mvn -o verify` (long; only when appropriate) +* Slow tests (entire repo): + `mvn -o verify -PslowTestsOnly,-skipSlowTests | tail -500` +* Slow tests (by module): + `mvn -o -pl verify -PslowTestsOnly,-skipSlowTests | tail -500` +* Slow tests (specific test): + + * `mvn -o -pl core/sail/shacl -PslowTestsOnly,-skipSlowTests -Dtest=ClassName#method verify | tail -500` +* Integration tests (entire repo): + `mvn -o verify -PskipUnitTests | tail -500` +* Integration tests (by module): + `mvn -o -pl verify -PskipUnitTests | tail -500` +* Useful flags: + + * `-Dtest=ClassName` + * `-Dtest=ClassName#method` + * `-Dit.test=ITClass#method` + * `-DtrimStackTrace=false` + +--- + +## Build + +* **Build without tests (fast path):** + `mvn -o -Pquick install` +* **Verify with tests:** + Targeted module(s): `mvn -o -pl verify` + Entire repo: `mvn -o verify` (use judiciously) +* **When offline fails due to missing deps:** + Re‑run the **exact** command **without** `-o` once to fetch, then return to `-o`. + +--- + +## Using JaCoCo (Coverage) + +JaCoCo is configured via the `jacoco` Maven profile in the root POM. Surefire/Failsafe honor the prepared agent `argLine`, so no extra flags are required beyond `-Pjacoco`. + +- Run with coverage + - Module: `mvn -o -pl -Pjacoco verify | tail -500` + - Class: `mvn -o -pl -Pjacoco -Dtest=ClassName verify | tail -500` + - Method: `mvn -o -pl -Pjacoco -Dtest=ClassName#method verify | tail -500` + +- Where to find reports (per module) + - Exec data: `/target/jacoco.exec` + - HTML report: `/target/site/jacoco/index.html` + - XML report: `/target/site/jacoco/jacoco.xml` + +- Check if a specific test covers code X + - Run only that test (class or method) with `-Dtest=...` (see above) and `-Pjacoco`. + - Open the HTML report and navigate to the class/method of interest; non-zero line/branch coverage indicates the selected test touched it. + - For multiple tests, run them in small subsets to localize coverage quickly. + +- Troubleshooting + - If you see “Skipping JaCoCo execution due to missing execution data file”, ensure you passed `-Pjacoco` and ran the install step first. + - If offline resolution fails for the JaCoCo plugin, rerun the exact command once without `-o`, then return offline. + +- Notes + - The default JaCoCo reports do not list “which individual tests” hit each line. Use single-test runs to infer per-test coverage. If you need true per-test mapping, add a JUnit 5 extension that sets a JaCoCo session per test and writes per-test exec files. + - Do not use `-am` when running tests; keep runs targeted by module/class/method. + +--- + +## Prohibited Misinterpretations + +* A user stack trace, reproduction script, or verbal description **is not evidence** for behavior‑changing work. You must implement the smallest failing test **inside this repo**. +* For Routine B, a stack trace is neither required nor sufficient; **Hit Proof** plus **pre/post green** snippets are mandatory. +* Routine C must not change production code. + +--- + +## Maven Module Overview + +The project is organised as a multi-module Maven build. The diagram below lists +all modules and submodules with a short description for each. + +``` +rdf4j: root project +├── assembly-descriptors: RDF4J: Assembly Descriptors +├── core: Core modules for RDF4J + ├── common: RDF4J common: shared classes + │ ├── annotation: RDF4J common annotation classes + │ ├── exception: RDF4J common exception classes + │ ├── io: RDF4J common IO classes + │ ├── iterator: RDF4J common iterators + │ ├── order: Order of vars and statements + │ ├── text: RDF4J common text classes + │ ├── transaction: RDF4J common transaction classes + │ └── xml: RDF4J common XML classes + ├── model-api: RDF model interfaces. + ├── model-vocabulary: Well-Known RDF vocabularies. + ├── model: RDF model implementations. + ├── sparqlbuilder: A fluent SPARQL query builder + ├── rio: Rio (RDF I/O) is an API for parsers and writers of various RDF file formats. + │ ├── api: Rio API. + │ ├── languages: Rio Language handler implementations. + │ ├── datatypes: Rio Datatype handler implementations. + │ ├── binary: Rio parser and writer implementation for the binary RDF file format. + │ ├── hdt: Experimental Rio parser and writer implementation for the HDT file format. + │ ├── jsonld-legacy: Rio parser and writer implementation for the JSON-LD file format. + │ ├── jsonld: Rio parser and writer implementation for the JSON-LD file format. + │ ├── n3: Rio writer implementation for the N3 file format. + │ ├── nquads: Rio parser and writer implementation for the N-Quads file format. + │ ├── ntriples: Rio parser and writer implementation for the N-Triples file format. + │ ├── rdfjson: Rio parser and writer implementation for the RDF/JSON file format. + │ ├── rdfxml: Rio parser and writer implementation for the RDF/XML file format. + │ ├── trix: Rio parser and writer implementation for the TriX file format. + │ ├── turtle: Rio parser and writer implementation for the Turtle file format. + │ └── trig: Rio parser and writer implementation for the TriG file format. + ├── queryresultio: Query result IO API and implementations. + │ ├── api: Query result IO API + │ ├── binary: Query result parser and writer implementation for RDF4J's binary query results format. + │ ├── sparqljson: Query result writer implementation for the SPARQL Query Results JSON Format. + │ ├── sparqlxml: Query result parser and writer implementation for the SPARQL Query Results XML Format. + │ └── text: Query result parser and writer implementation for RDF4J's plain text boolean query results format. + ├── query: Query interfaces and implementations + ├── queryalgebra: Query algebra model and evaluation. + │ ├── model: A generic query algebra for RDF queries. + │ ├── evaluation: Evaluation strategy API and implementations for the query algebra model. + │ └── geosparql: Query algebra implementations to support the evaluation of GeoSPARQL. + ├── queryparser: Query parser API and implementations. + │ ├── api: Query language parsers API. + │ └── sparql: Query language parser implementation for SPARQL. + ├── http: Client and protocol for repository communication over HTTP. + │ ├── protocol: HTTP protocol (REST-style) + │ └── client: Client functionality for communicating with an RDF4J server over HTTP. + ├── queryrender: Query Render and Builder tools + ├── repository: Repository API and implementations. + │ ├── api: API for interacting with repositories of RDF data. + │ ├── manager: Repository manager + │ ├── sail: Repository that uses a Sail stack. + │ ├── dataset: Implementation that loads all referenced datasets into a wrapped repository + │ ├── event: Implementation that notifies listeners of events on a wrapped repository + │ ├── http: "Virtual" repository that communicates with a (remote) repository over the HTTP protocol. + │ ├── contextaware: Implementation that allows default values to be set on a wrapped repository + │ └── sparql: The SPARQL Repository provides a RDF4J Repository interface to any SPARQL end-point. + ├── sail: Sail API and implementations. + │ ├── api: RDF Storage And Inference Layer ("Sail") API. + │ ├── base: RDF Storage And Inference Layer ("Sail") API. + │ ├── inferencer: Stackable Sail implementation that adds RDF Schema inferencing to an RDF store. + │ ├── memory: Sail implementation that stores data in main memory, optionally using a dump-restore file for persistence. + │ ├── nativerdf: Sail implementation that stores data directly to disk in dedicated file formats. + │ ├── model: Sail implementation of Model. + │ ├── shacl: Stacked Sail with SHACL validation capabilities + │ ├── lmdb: Sail implementation that stores data to disk using LMDB. + │ ├── lucene-api: StackableSail API offering full-text search on literals, based on Apache Lucene. + │ ├── lucene: StackableSail implementation offering full-text search on literals, based on Apache Lucene. + │ ├── solr: StackableSail implementation offering full-text search on literals, based on Solr. + │ ├── elasticsearch: StackableSail implementation offering full-text search on literals, based on Elastic Search. + │ ├── elasticsearch-store: Store for utilizing Elasticsearch as a triplestore. + │ └── extensible-store: Store that can be extended with a simple user-made backend. + ├── spin: SPARQL input notation interfaces and implementations + ├── client: Parent POM for all RDF4J parsers, APIs and client libraries + ├── storage: Parent POM for all RDF4J storage and inferencing libraries + └── collection-factory: Collection Factories that may be reused for RDF4J + ├── api: Evaluation + ├── mapdb: Evaluation + └── mapdb3: Evaluation +├── tools: Server, Workbench, Console and other end-user tools for RDF4J. + ├── config: RDF4J application configuration classes + ├── console: Command line user interface to RDF4J repositories. + ├── federation: A federation engine for virtually integrating SPARQL endpoints + ├── server: HTTP server implementing a REST-style protocol + ├── server-spring: HTTP server implementing a REST-style protocol + ├── workbench: Workbench to interact with RDF4J servers. + ├── runtime: Runtime dependencies for an RDF4J application + └── runtime-osgi: OSGi Runtime dependencies for an RDF4J application +├── spring-components: Components to use with Spring + ├── spring-boot-sparql-web: HTTP server component implementing only the SPARQL protocol + ├── rdf4j-spring: Spring integration for RDF4J + └── rdf4j-spring-demo: Demo of a spring-boot project using an RDF4J repo as its backend +├── testsuites: Test suites for Eclipse RDF4J modules + ├── model: Reusable tests for Model API implementations + ├── rio: Test suite for Rio + ├── queryresultio: Reusable tests for QueryResultIO implementations + ├── sparql: Test suite for the SPARQL query language + ├── repository: Reusable tests for Repository API implementations + ├── sail: Reusable tests for Sail API implementations + ├── lucene: Generic tests for Lucene Sail implementations. + ├── geosparql: Test suite for the GeoSPARQL query language + └── benchmark: RDF4J: benchmarks +├── compliance: Eclipse RDF4J compliance and integration tests + ├── repository: Compliance testing for the Repository API implementations + ├── rio: Tests for parsers and writers of various RDF file formats. + ├── model: RDF4J: Model compliance tests + ├── sparql: Tests for the SPARQL query language implementation + ├── lucene: Compliance Tests for LuceneSail. + ├── solr: Tests for Solr Sail. + ├── elasticsearch: Tests for Elasticsearch. + └── geosparql: Tests for the GeoSPARQL query language implementation +├── examples: Examples and HowTos for use of RDF4J in Java +├── bom: RDF4J Bill of Materials (BOM) +└── assembly: Distribution bundle assembly +``` + +## Safety & Boundaries + +* Don’t commit or push unless explicitly asked. +* Don’t add new dependencies without explicit approval. + +It is illegal to `-am` when running tests! +It is illegal to `-q` when running tests! +You must follow these rules and instructions exactly as stated. From 5c281fa20be00a7fcc4a6d4785a40329e0f1eb3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Fri, 3 Oct 2025 13:44:30 +0200 Subject: [PATCH 40/46] update AGENTS.md --- AGENTS.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 4a00fb1b000..af461b6082d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -228,6 +228,13 @@ Running `install` publishes your changed modules there so downstream modules and * Skipping this step can lead to stale or missing artifacts during tests, producing confusing compilation or linkage errors. * Never ever change the repo location. Never use `-Dmaven.repo.local=.m2_repo`. * Always try to run these commands first to see if they run without needing any approvals from the user w.r.t. the sandboxing. + +Why this is mandatory + +- Tests must not use `-am`. Without `-am`, Maven will not build upstream modules when you run tests; it will resolve cross‑module dependencies from the local `~/.m2/repository` instead. +- Therefore, tests only see whatever versions were last published to `~/.m2`. If you change code in one module and then run tests in another, those tests will not see your changes unless the updated module has been installed to `~/.m2` first. +- The reliable way to ensure all tests always use the latest code across the entire multi‑module build is to install all modules to `~/.m2` before running any tests: run `mvn -o -Pquick install` at the repository root. +- In tight loops you may also install a specific module and its deps (`-pl -am -Pquick install`) to iterate quickly, but before executing tests anywhere that depend on your changes, run a root‑level `mvn -o -Pquick install` so the latest jars are available to the reactor from `~/.m2`. --- ## Quick Start (First 10 Minutes) From ed6c861cabf19f7105851b0c759a64807f804bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Fri, 3 Oct 2025 13:44:39 +0200 Subject: [PATCH 41/46] update AGENTS.md --- AGENTS_TEMP.md | 713 ------------------------------------------------- 1 file changed, 713 deletions(-) delete mode 100644 AGENTS_TEMP.md diff --git a/AGENTS_TEMP.md b/AGENTS_TEMP.md deleted file mode 100644 index 4a00fb1b000..00000000000 --- a/AGENTS_TEMP.md +++ /dev/null @@ -1,713 +0,0 @@ -# AGENTS.md - -Welcome, AI Agent! Your persistence, curiosity, and craftsmanship make a difference. Take your time, work methodically, validate thoroughly, and iterate. This repository is large and tests can take time — that’s expected and supported. - -You need to read the entire AGENTS.md file and follow all instructions exactly. Keep this fresh in your context as you work. - -> **Timebox:** Aim to complete each autonomous run in **15–30 minutes**. - ---- - -## Read‑Me‑Now: Proportional Test‑First Rule (Default) - -**Default:** Use **test‑first (TDD)** for any change that alters externally observable behavior. - -**Proportional exceptions:** You may **skip writing a new failing test** *only* when **all** Routine B gates (below) pass, or when using Routine C (Spike/Investigate) with **no production code changes**. - -**You may not touch production code for behavior‑changing work until a smallest‑scope failing automated test exists inside this repo and you have captured its report snippet.** A user‑provided stack trace or “obvious” contract violation is **not** a substitute for an in‑repo failing test. - -**Auto‑stop:** If you realize you patched production before creating/observing the failing test for behavior‑changing work, **stop**, revert the patch, and resume from “Reproduce first”. - -**Traceability trio (must appear in your handoff):** -1. **Descritpion** (what you’re about to do) -2. **Evidence** (Surefire/Failsafe snippet from this repo) -3. **Plan** (one and only one `in_progress` step) - -It is illegal to `-am` when running tests! -It is illegal to `-q` when running tests! - -> **Clarification:** For **strictly behavior‑neutral refactors** that are already **fully exercised by existing tests**, or for **bugfixes with an existing failing test**, you may use **Routine B — Change without new tests**. In that case you must capture **pre‑change passing evidence** at the smallest scope that hits the code you’re about to edit, prove **Hit Proof**, then show **post‑change passing evidence** from the **same selection**. -> **No exceptions for any behavior‑changing change** — for those, you must follow **Routine A — Full TDD**. - ---- - -## Three Routines: Choose Your Path - -**Routine A — Full TDD (Default)** -**Routine B — Change without new tests (Proportional, gated)** -**Routine C — Spike/Investigate (No production changes)** - -### Decision quickstart - -1. **Is new externally observable behavior required?** - → **Yes:** **Routine A (Full TDD)**. Add the smallest failing test first. - → **No:** continue. - -2. **Does a failing test already exist in this repo that pinpoints the issue?** - → **Yes:** **Routine B (Bugfix using existing failing test).** - → **No:** continue. - -3. **Is the edit strictly behavior‑neutral, local in scope, and clearly hit by existing tests?** - → **Yes:** **Routine B (Refactor/micro‑perf/documentation/build).** - → **No or unsure:** continue. - -4. **Is this purely an investigation/design spike with no production code changes?** - → **Yes:** **Routine C (Spike/Investigate).** - → **No or unsure:** **Routine A.** - -**When in doubt, choose Routine A (Full TDD).** Ambiguity is risk; tests are insurance. - ---- - -## Proportionality Model (Think before you test) - -Score the change on these lenses. If any are **High**, prefer **Routine A**. - -- **Behavioral surface:** affects outputs, serialization, parsing, APIs, error text, timing/order? -- **Blast radius:** number of modules/classes touched; public vs internal. -- **Reversibility:** quick revert vs migration/data change. -- **Observability:** can existing tests or assertions expose regressions? -- **Coverage depth:** do existing tests directly hit the edited code? -- **Concurrency / IO / Time:** any risk here is **High** by default. - ---- - -## Purpose & Contract - -* **Bold goal:** deliver correct, minimal, well‑tested changes with clear handoff. Fix root causes; avoid hacks. -* **Bias to action:** when inputs are ambiguous, choose a reasonable path, state assumptions, and proceed. -* **Ask only when blocked or irreversible:** permissions, missing deps, conflicting requirements, destructive repo‑wide changes. -* **Definition of Done** - * Code formatted and imports sorted. - * Compiles with a quick profile / targeted modules. - * Relevant module tests pass; failures triaged or crisply explained. - * Only necessary files changed; headers correct for new files. - * Clear final summary: what changed, why, where, how verified, next steps. - * **Evidence present:** failing test output (pre‑fix) and passing output (post‑fix) are shown for Routine A; for Routine B show **pre/post green** from the **same selection** plus **Hit Proof**. - -### No Monkey‑Patching or Band‑Aid Fixes (Non‑Negotiable) - -Durable, root‑cause fixes only. No muting tests, no broad catch‑and‑ignore, no widening APIs “to make green”. - -**Strictly avoid** -* Sleeping/timeouts to hide flakiness. -* Swallowing exceptions or weakening assertions. -* Reflection/internal state manipulation to bypass interfaces. -* Feature flags that disable validation instead of fixing logic. -* Changing public APIs/configs without necessity tied to root cause. - -**Preferred approach** -* Reproduce the issue and isolate the smallest failing test (class → method). -* Trace to the true source; fix in the right module. -* Add focused tests for behavior/edge cases (Routine A) or prove coverage/neutrality (Routine B). -* Run tight, targeted verifies; broaden only if needed. - ---- - -## Enforcement & Auto‑Fail Triggers - -Your run is **invalid** and must be restarted from “Reproduce first” if any occur: - -* You modify production code before adding and running the smallest failing test in this repo **for behavior‑changing work**. -* You proceed without pasting a Surefire/Failsafe report snippet from `target/*-reports/`. -* Your plan does not have **exactly one** `in_progress` step. -* You run tests using `-am` or `-q`. -* You treat a narrative failure description or external stack trace as equivalent to an in‑repo failing test. -* **Routine B specific:** you cannot demonstrate that existing tests exercise the edited code (**Hit Proof**), or you fail to capture both pre‑ and post‑change **matching** passing snippets from the same selection. -* **Routine C breach:** you change production code while in a spike. - -**Recovery procedure:** -Update the plan (`in_progress: create failing test`), post a description of your next step, create the failing test, run it, capture the report snippet, then resume. -For Routine B refactors: if any gate fails, **switch to Full TDD** and add the smallest failing test. - ---- - -## Evidence Protocol (Mandatory) - -After each grouped action, post an **Evidence block**, then continue working: - -**Evidence template** -``` -Evidence: -Command: mvn -o -pl -Dtest=Class#method verify -Report: /target/surefire-reports/.txt -Snippet: -\ -``` - -**Routine B additions** -* **Pre‑green:** capture a pre‑change **passing** snippet from the **most specific** test selection that hits your code (ideally a class or method). -* **Hit Proof (choose one):** - * An existing test class/method that directly calls the edited class/method, plus a short `rg -n` snippet showing the call site; **or** - * A Surefire/Failsafe output line containing the edited class/method names; **or** - * A temporary assertion or deliberate, isolated failing check in a **scratch test** proving the path is executed (then remove). -* **Post‑green:** after the patch, re‑run the **same selection** and capture a passing snippet. - ---- - -### Initial Evidence Capture (Required) - -To avoid losing the first test evidence when later runs overwrite `target/*-reports/`, immediately persist the initial verify results to a top‑level `initial-evidence.txt` file. - -• On a fully green verify run: - -- Capture and store the last 200 lines of the Maven verify output. -- Example (module‑scoped): - - `mvn -o -pl verify | tee .initial-verify.log` - - `tail -200 .initial-verify.log > initial-evidence.txt` - -• On any failing verify run (unit or IT failures): - -- Concatenate the Surefire and/or Failsafe report text files into `initial-evidence.txt`. -- Example (repo‑root): - - `find . -type f \( -path "*/target/surefire-reports/*.txt" -o -path "*/target/failsafe-reports/*.txt" \) -print0 | xargs -0 cat > initial-evidence.txt` - -Notes - -- Keep `initial-evidence.txt` at the repository root alongside your final handoff. -- Do not rely on `target/*-reports/` for the final report; they may be overwritten by subsequent runs. -- Continue to include the standard Evidence block(s) in your messages as usual. - ---- - -## Living Plan Protocol (Sharper) - -Maintain a **living plan** with checklist items (5–7 words each). Keep **exactly one** `in_progress`. - -**Plan format** -``` - -Plan - -* \[done] sanity build quick profile -* \[in\_progress] add smallest failing test -* \[todo] minimal root-cause fix -* \[todo] rerun focused then module tests -* \[todo] format, verify, summary - -```` - -**Rule:** If you deviate, update the plan **first**, then proceed. - ---- - -## Environment - -* **JDK:** 11 (minimum). The project builds and runs on Java 11+. -* **Maven default:** run **offline** using `-o` whenever possible. -* **Network:** only to fetch missing deps/plugins; then rerun once without `-o`, and return offline. -* **Large project:** some module test suites can take **5–10 minutes**. Prefer **targeted** runs. - -### Maven `-am` usage (house rule) - -`-am` is helpful for **compiles**, hazardous for **tests**. - -* ✅ Use `-am` **only** for compile/verify with tests skipped (e.g. `-Pquick`): - * `mvn -o -pl -am -Pquick install` -* ❌ Do **not** use `-am` with `verify` when tests are enabled. - -**Two-step pattern (fast + safe)** -1. **Compile deps fast (skip tests):** - `mvn -o -pl -am -Pquick install` -2. **Run tests:** - `mvn -o -pl verify | tail -500` - -It is illegal to `-am` when running tests! -It is illegal to `-q` when running tests! - ---- - -## Always Install Before Tests (Required) - -The Maven reactor resolves inter-module dependencies from the local Maven repository (`~/.m2/repository`). -Running `install` publishes your changed modules there so downstream modules and tests pick up the correct versions. - -* Always run `mvn -o -Pquick install | tail -200` before you start working. This command typically takes up to 30 seconds. Never use a small timeout than 30,000 ms. -* Always run `mvn -o -Pquick install | tail -200` before any `verify` or test runs. -* If offline resolution fails due to a missing dependency or plugin, rerun the exact `install` command once without `-o`, then return offline. -* Skipping this step can lead to stale or missing artifacts during tests, producing confusing compilation or linkage errors. -* Never ever change the repo location. Never use `-Dmaven.repo.local=.m2_repo`. -* Always try to run these commands first to see if they run without needing any approvals from the user w.r.t. the sandboxing. ---- - -## Quick Start (First 10 Minutes) - -1. **Discover** - * Inspect root `pom.xml` and module tree (see “Maven Module Overview”). - * Search fast with ripgrep: `rg -n ""` -2. **Build sanity (fast, skip tests)** - * `mvn -o -Pquick install | tail -200` -3. **Format (Java, imports, XML)** - * `mvn -o -q -T 2C formatter:format impsort:sort xml-format:xml-format` -4. **Targeted tests (tight loops)** - * Module: `mvn -o -pl verify | tail -500` - * Class: `mvn -o -pl -Dtest=ClassName verify | tail -500` - * Method: `mvn -o -pl -Dtest=ClassName#method verify | tail -500` -5. **Inspect failures** - * **Unit (Surefire):** `/target/surefire-reports/` - * **IT (Failsafe):** `/target/failsafe-reports/` - -It is illegal to `-am` when running tests! -It is illegal to `-q` when running tests! - ---- - -## Routine A — Full TDD (Default) - -> Use for **all behavior‑changing work** and whenever Routine B gates do not all pass. - -### Bugfix Workflow (Mandatory) - -* **Reproduce first:** write the smallest focused test (class/method) that reproduces the reported bug **inside this repo**. Confirm it fails. -* **Keep the test as‑is:** do not weaken assertions or mute the failure. -* **Fix at the root:** minimal, surgical change in the correct module. -* **Verify locally:** re‑run the focused test, then the module’s tests. Avoid `-am`/`-q` with tests. -* **Broaden if needed:** expand scope only after targeted greens. -* **Document clearly:** failing output (pre‑fix), root cause, minimal fix, passing output (post‑fix). - -### Hard Gates - -* A failing test exists at the smallest scope (method/class). -* **No production patch before the failing test is observed and recorded.** -* Test runs avoid `-am` and `-q`. - ---- - -## Routine B — Change without new tests (Proportional, gated) - -> Use **only** when at least one Allowed Case applies **and** all Routine B **Gates** pass. - -### Allowed cases (one or more) -1. **Bugfix with existing failing test** in this repo (pinpoints class/method). -2. **Strictly behavior‑neutral refactor / cleanup / micro‑perf** with clear existing coverage hitting the edited path. -3. **Migration/rename/autogen refresh** where behavior is already characterized by existing tests. -4. **Build/CI/docs/logging/message changes** that do not alter runtime behavior or asserted outputs. -5. **Data/resource tweaks** not asserted by tests and not affecting behavior. - -### Routine B Gates (all must pass) -- **Neutrality/Scope:** No externally observable behavior change. Localized edit. -- **Hit Proof:** Demonstrate tests exercise the edited code. -- **Pre/Post Green Match:** Same smallest‑scope selection, passing before and after. -- **Risk Check:** No concurrency/time/IO semantics touched; no public API, serialization, parsing, or ordering changes. -- **Reversibility:** Change is easy to revert if needed. - -**If any gate fails → switch to Routine A.** - ---- - -## Routine C — Spike / Investigate (No production changes) - -> Use for exploration, triage, design spikes, and measurement. **No production code edits.** - -**You may:** -- Add temporary scratch tests, assertions, scripts, or notes. -- Capture measurements, traces, logs. - -**Hand‑off must include:** -- Description, commands, and artifacts (logs/notes). -- Findings, options, and a proposed next routine (A or B). -- Removal of any temporary code if not adopted. - ---- - -## Where to Draw the Line — A Short Debate - -> **Purist:** “All changes must start with a failing test.” -> **Pragmatist:** “For refactors that can’t fail first without faking it, prove coverage and equality of behavior.” - -**In‑scope for Routine B (examples)** -* Rename private methods; extract helper; dead‑code removal. -* Replace straightforward loop with stream (same results, same ordering). -* Tighten generics/nullability/annotations without observable change. -* Micro‑perf cache within a method with deterministic inputs and strong coverage. -* Logging/message tweaks **not** asserted by tests. -* Build/CI config that doesn’t alter runtime behavior. - -**Out‑of‑scope (use Routine A)** -* Changing query results, serialization, or parsing behavior. -* Altering error messages that tests assert. -* Anything touching concurrency, timeouts, IO, or ordering. -* New SPARQL function support or extended syntax (even “tiny”). -* Public API changes or cross‑module migrations with unclear blast radius. - ---- - -## Working Loop - -* **Plan:** small, verifiable steps; keep one `in_progress`. -* **Change:** minimal, surgical edits; keep style/structure consistent. -* **Format:** `mvn -o -q -T 2C formatter:format impsort:sort xml-format:xml-format` -* **Compile (fast):** `mvn -o -pl -am -Pquick install | tail -500` -* **Test:** start smallest (class/method → module). For integration, run module `verify`. -* **Triage:** read reports; fix root cause; expand scope only when needed. -* **Iterate:** keep momentum; escalate only when blocked or irreversible. - -It is illegal to `-am` when running tests! -It is illegal to `-q` when running tests! - ---- - -## Testing Strategy - -* **Prefer module tests you touched:** `-pl ` -* **Narrow further** to a class/method; then broaden to the module. -* **Expand scope** when changes cross boundaries or neighbor modules fail. -* **Read reports** - * Surefire (unit): `target/surefire-reports/` - * Failsafe (IT): `target/failsafe-reports/` -* **Helpful flags** - * `-Dtest=Class#method` (unit selection) - * `-Dit.test=ITClass#method` (integration selection) - * `-DtrimStackTrace=false` (full traces) - * `-DskipITs` (focus on unit tests) - * `-DfailIfNoTests=false` (when selecting a class that has no tests on some platforms) - -### Optional: Redirect test stdout/stderr to files -```bash -mvn -o -pl -Dtest=ClassName[#method] -Dmaven.test.redirectTestOutputToFile=true verify | tail -500 -```` - -Logs under: - -``` -/target/surefire-reports/ClassName-output.txt -``` - -(Use similarly for Failsafe via `-Dit.test=`.) - ---- - -## Assertions: Make invariants explicit - -Assertions are executable claims about what must be true. Use **temporary tripwires** during investigation and **permanent contracts** once an invariant matters. - -* One fact per assert; fail fast and usefully. -* Include stable context in messages; avoid side effects. -* Keep asserts cheap; don’t replace user input validation with asserts. - -**Java specifics** - -* Enable VM assertions in tests (`-ea`). -* Use exceptions for runtime guarantees; `assert` for “cannot happen”. - -(Concrete examples omitted here for brevity; keep your current patterns.) - ---- - -## Triage Playbook - -* **Missing dep/plugin offline:** rerun the exact command once **without** `-o`, then return offline. -* **Compilation errors:** fix imports/generics/visibility; quick install in the module. -* **Flaky/slow tests:** run the specific failing test; stabilize root cause before broad runs. -* **Formatting failures:** run formatter/import/XML sort; re‑verify. -* **License header missing:** add for **new** files only; do not change years on existing files. - ---- - -## Code Formatting - -* Always run before finalizing: - - * `mvn -o -q -T 2C formatter:format impsort:sort xml-format:xml-format` -* Style: no wildcard imports; 120‑char width; curly braces always; LF endings. - ---- - -## Source File Headers - -Use this exact header for **new Java files only** (replace `${year}` with current year): - -``` -/******************************************************************************* - * Copyright (c) ${year} Eclipse RDF4J contributors. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Distribution License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * SPDX-License-Identifier: BSD-3-Clause - *******************************************************************************/ -``` - -Do **not** modify existing headers’ years. - ---- - -## Pre‑Commit Checklist - -* **Format:** `mvn -o -q -T 2C formatter:format impsort:sort xml-format:xml-format` -* **Compile (fast path):** `mvn -o -Pquick install | tail -200` -* **Tests (targeted):** `mvn -o -pl verify | tail -500` (broaden as needed) -* **Reports:** zero new failures in Surefire/Failsafe, or explain precisely. -* **Evidence:** Routine A — failing pre‑fix + passing post‑fix. - Routine B — **pre/post green** from same selection + **Hit Proof**. - ---- - -## Branching & Commit Conventions - -* Branch names: start with `GH-XXXX` (GitHub issue number). Optional short slug, e.g., `GH-1234-trig-writer-check`. -* Commit messages: `GH-XXXX ` on every commit. - ---- - -## Branch & PR Workflow (Agent) - -* Confirm issue number first (mandatory). -* Branch: `git checkout -b GH-XXXX-your-slug` -* Stage: `git add -A` (ensure new Java files have the required header). -* Optional: formatter + quick install. -* Commit: `git commit -m "GH-XXXX "` -* Push & PR: use the default template; fill all fields; include `Fixes #XXXX`. - ---- - -## Navigation & Search - -* Files: `rg --files` -* Content: `rg -n ""` -* Read big files in chunks: - - * `sed -n '1,200p' path/to/File.java` - * `sed -n '201,400p' path/to/File.java` - ---- - -## Autonomy Rules (Act > Ask) - -* **Default:** act with assumptions; document them. -* **Keep going:** chain steps; short progress updates before long actions. -* **Ask only when:** blocked by sandbox/approvals/network, or change is destructive/irreversible, or impacts public APIs/dependencies/licensing. -* **Prefer reversible moves:** smallest local change that unblocks progress; validate with targeted tests first. - -**Defaults** - -* **Tests:** start with `-pl `, then `-Dtest=Class#method` / `-Dit.test=ITClass#method`. -* **Build:** use `-o`; drop `-o` once only to fetch; return offline. -* **Formatting:** run formatter/import/XML before verify. -* **Reports:** read surefire/failsafe locally; expand scope only when necessary. - ---- - -## Answer Template (Use This) - -* **What changed:** summary of approach and rationale. -* **Files touched:** list file paths. -* **Commands run:** key build/test commands. -* **Verification:** which tests passed, where you checked reports. -* **Evidence:** - *Routine A:* failing output (pre‑fix) and passing output (post‑fix). - *Routine B:* pre‑ and post‑green snippets from the **same selection** + **Hit Proof**. - *Routine C:* artifacts from investigation (logs/notes/measurements) and proposed next steps. -* **Assumptions:** key assumptions and autonomous decisions. -* **Limitations:** anything left or risky edge cases. -* **Next steps:** optional follow‑ups. - ---- - -## Running Tests - -* By module: `mvn -o -pl core/sail/shacl verify | tail -500` -* Entire repo: `mvn -o verify` (long; only when appropriate) -* Slow tests (entire repo): - `mvn -o verify -PslowTestsOnly,-skipSlowTests | tail -500` -* Slow tests (by module): - `mvn -o -pl verify -PslowTestsOnly,-skipSlowTests | tail -500` -* Slow tests (specific test): - - * `mvn -o -pl core/sail/shacl -PslowTestsOnly,-skipSlowTests -Dtest=ClassName#method verify | tail -500` -* Integration tests (entire repo): - `mvn -o verify -PskipUnitTests | tail -500` -* Integration tests (by module): - `mvn -o -pl verify -PskipUnitTests | tail -500` -* Useful flags: - - * `-Dtest=ClassName` - * `-Dtest=ClassName#method` - * `-Dit.test=ITClass#method` - * `-DtrimStackTrace=false` - ---- - -## Build - -* **Build without tests (fast path):** - `mvn -o -Pquick install` -* **Verify with tests:** - Targeted module(s): `mvn -o -pl verify` - Entire repo: `mvn -o verify` (use judiciously) -* **When offline fails due to missing deps:** - Re‑run the **exact** command **without** `-o` once to fetch, then return to `-o`. - ---- - -## Using JaCoCo (Coverage) - -JaCoCo is configured via the `jacoco` Maven profile in the root POM. Surefire/Failsafe honor the prepared agent `argLine`, so no extra flags are required beyond `-Pjacoco`. - -- Run with coverage - - Module: `mvn -o -pl -Pjacoco verify | tail -500` - - Class: `mvn -o -pl -Pjacoco -Dtest=ClassName verify | tail -500` - - Method: `mvn -o -pl -Pjacoco -Dtest=ClassName#method verify | tail -500` - -- Where to find reports (per module) - - Exec data: `/target/jacoco.exec` - - HTML report: `/target/site/jacoco/index.html` - - XML report: `/target/site/jacoco/jacoco.xml` - -- Check if a specific test covers code X - - Run only that test (class or method) with `-Dtest=...` (see above) and `-Pjacoco`. - - Open the HTML report and navigate to the class/method of interest; non-zero line/branch coverage indicates the selected test touched it. - - For multiple tests, run them in small subsets to localize coverage quickly. - -- Troubleshooting - - If you see “Skipping JaCoCo execution due to missing execution data file”, ensure you passed `-Pjacoco` and ran the install step first. - - If offline resolution fails for the JaCoCo plugin, rerun the exact command once without `-o`, then return offline. - -- Notes - - The default JaCoCo reports do not list “which individual tests” hit each line. Use single-test runs to infer per-test coverage. If you need true per-test mapping, add a JUnit 5 extension that sets a JaCoCo session per test and writes per-test exec files. - - Do not use `-am` when running tests; keep runs targeted by module/class/method. - ---- - -## Prohibited Misinterpretations - -* A user stack trace, reproduction script, or verbal description **is not evidence** for behavior‑changing work. You must implement the smallest failing test **inside this repo**. -* For Routine B, a stack trace is neither required nor sufficient; **Hit Proof** plus **pre/post green** snippets are mandatory. -* Routine C must not change production code. - ---- - -## Maven Module Overview - -The project is organised as a multi-module Maven build. The diagram below lists -all modules and submodules with a short description for each. - -``` -rdf4j: root project -├── assembly-descriptors: RDF4J: Assembly Descriptors -├── core: Core modules for RDF4J - ├── common: RDF4J common: shared classes - │ ├── annotation: RDF4J common annotation classes - │ ├── exception: RDF4J common exception classes - │ ├── io: RDF4J common IO classes - │ ├── iterator: RDF4J common iterators - │ ├── order: Order of vars and statements - │ ├── text: RDF4J common text classes - │ ├── transaction: RDF4J common transaction classes - │ └── xml: RDF4J common XML classes - ├── model-api: RDF model interfaces. - ├── model-vocabulary: Well-Known RDF vocabularies. - ├── model: RDF model implementations. - ├── sparqlbuilder: A fluent SPARQL query builder - ├── rio: Rio (RDF I/O) is an API for parsers and writers of various RDF file formats. - │ ├── api: Rio API. - │ ├── languages: Rio Language handler implementations. - │ ├── datatypes: Rio Datatype handler implementations. - │ ├── binary: Rio parser and writer implementation for the binary RDF file format. - │ ├── hdt: Experimental Rio parser and writer implementation for the HDT file format. - │ ├── jsonld-legacy: Rio parser and writer implementation for the JSON-LD file format. - │ ├── jsonld: Rio parser and writer implementation for the JSON-LD file format. - │ ├── n3: Rio writer implementation for the N3 file format. - │ ├── nquads: Rio parser and writer implementation for the N-Quads file format. - │ ├── ntriples: Rio parser and writer implementation for the N-Triples file format. - │ ├── rdfjson: Rio parser and writer implementation for the RDF/JSON file format. - │ ├── rdfxml: Rio parser and writer implementation for the RDF/XML file format. - │ ├── trix: Rio parser and writer implementation for the TriX file format. - │ ├── turtle: Rio parser and writer implementation for the Turtle file format. - │ └── trig: Rio parser and writer implementation for the TriG file format. - ├── queryresultio: Query result IO API and implementations. - │ ├── api: Query result IO API - │ ├── binary: Query result parser and writer implementation for RDF4J's binary query results format. - │ ├── sparqljson: Query result writer implementation for the SPARQL Query Results JSON Format. - │ ├── sparqlxml: Query result parser and writer implementation for the SPARQL Query Results XML Format. - │ └── text: Query result parser and writer implementation for RDF4J's plain text boolean query results format. - ├── query: Query interfaces and implementations - ├── queryalgebra: Query algebra model and evaluation. - │ ├── model: A generic query algebra for RDF queries. - │ ├── evaluation: Evaluation strategy API and implementations for the query algebra model. - │ └── geosparql: Query algebra implementations to support the evaluation of GeoSPARQL. - ├── queryparser: Query parser API and implementations. - │ ├── api: Query language parsers API. - │ └── sparql: Query language parser implementation for SPARQL. - ├── http: Client and protocol for repository communication over HTTP. - │ ├── protocol: HTTP protocol (REST-style) - │ └── client: Client functionality for communicating with an RDF4J server over HTTP. - ├── queryrender: Query Render and Builder tools - ├── repository: Repository API and implementations. - │ ├── api: API for interacting with repositories of RDF data. - │ ├── manager: Repository manager - │ ├── sail: Repository that uses a Sail stack. - │ ├── dataset: Implementation that loads all referenced datasets into a wrapped repository - │ ├── event: Implementation that notifies listeners of events on a wrapped repository - │ ├── http: "Virtual" repository that communicates with a (remote) repository over the HTTP protocol. - │ ├── contextaware: Implementation that allows default values to be set on a wrapped repository - │ └── sparql: The SPARQL Repository provides a RDF4J Repository interface to any SPARQL end-point. - ├── sail: Sail API and implementations. - │ ├── api: RDF Storage And Inference Layer ("Sail") API. - │ ├── base: RDF Storage And Inference Layer ("Sail") API. - │ ├── inferencer: Stackable Sail implementation that adds RDF Schema inferencing to an RDF store. - │ ├── memory: Sail implementation that stores data in main memory, optionally using a dump-restore file for persistence. - │ ├── nativerdf: Sail implementation that stores data directly to disk in dedicated file formats. - │ ├── model: Sail implementation of Model. - │ ├── shacl: Stacked Sail with SHACL validation capabilities - │ ├── lmdb: Sail implementation that stores data to disk using LMDB. - │ ├── lucene-api: StackableSail API offering full-text search on literals, based on Apache Lucene. - │ ├── lucene: StackableSail implementation offering full-text search on literals, based on Apache Lucene. - │ ├── solr: StackableSail implementation offering full-text search on literals, based on Solr. - │ ├── elasticsearch: StackableSail implementation offering full-text search on literals, based on Elastic Search. - │ ├── elasticsearch-store: Store for utilizing Elasticsearch as a triplestore. - │ └── extensible-store: Store that can be extended with a simple user-made backend. - ├── spin: SPARQL input notation interfaces and implementations - ├── client: Parent POM for all RDF4J parsers, APIs and client libraries - ├── storage: Parent POM for all RDF4J storage and inferencing libraries - └── collection-factory: Collection Factories that may be reused for RDF4J - ├── api: Evaluation - ├── mapdb: Evaluation - └── mapdb3: Evaluation -├── tools: Server, Workbench, Console and other end-user tools for RDF4J. - ├── config: RDF4J application configuration classes - ├── console: Command line user interface to RDF4J repositories. - ├── federation: A federation engine for virtually integrating SPARQL endpoints - ├── server: HTTP server implementing a REST-style protocol - ├── server-spring: HTTP server implementing a REST-style protocol - ├── workbench: Workbench to interact with RDF4J servers. - ├── runtime: Runtime dependencies for an RDF4J application - └── runtime-osgi: OSGi Runtime dependencies for an RDF4J application -├── spring-components: Components to use with Spring - ├── spring-boot-sparql-web: HTTP server component implementing only the SPARQL protocol - ├── rdf4j-spring: Spring integration for RDF4J - └── rdf4j-spring-demo: Demo of a spring-boot project using an RDF4J repo as its backend -├── testsuites: Test suites for Eclipse RDF4J modules - ├── model: Reusable tests for Model API implementations - ├── rio: Test suite for Rio - ├── queryresultio: Reusable tests for QueryResultIO implementations - ├── sparql: Test suite for the SPARQL query language - ├── repository: Reusable tests for Repository API implementations - ├── sail: Reusable tests for Sail API implementations - ├── lucene: Generic tests for Lucene Sail implementations. - ├── geosparql: Test suite for the GeoSPARQL query language - └── benchmark: RDF4J: benchmarks -├── compliance: Eclipse RDF4J compliance and integration tests - ├── repository: Compliance testing for the Repository API implementations - ├── rio: Tests for parsers and writers of various RDF file formats. - ├── model: RDF4J: Model compliance tests - ├── sparql: Tests for the SPARQL query language implementation - ├── lucene: Compliance Tests for LuceneSail. - ├── solr: Tests for Solr Sail. - ├── elasticsearch: Tests for Elasticsearch. - └── geosparql: Tests for the GeoSPARQL query language implementation -├── examples: Examples and HowTos for use of RDF4J in Java -├── bom: RDF4J Bill of Materials (BOM) -└── assembly: Distribution bundle assembly -``` - -## Safety & Boundaries - -* Don’t commit or push unless explicitly asked. -* Don’t add new dependencies without explicit approval. - -It is illegal to `-am` when running tests! -It is illegal to `-q` when running tests! -You must follow these rules and instructions exactly as stated. From 741eacaa5885845b785eee805483322749f9d96c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Fri, 3 Oct 2025 14:11:31 +0200 Subject: [PATCH 42/46] updated test --- .../MemoryStoreMinusScopingDebugTest.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java index 356c051ff58..b7b287bd0cc 100644 --- a/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java +++ b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java @@ -12,6 +12,7 @@ package org.eclipse.rdf4j.sail.memory; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import java.io.IOException; import java.io.StringReader; @@ -25,6 +26,7 @@ import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Value; import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.QueryLanguage; import org.eclipse.rdf4j.query.QueryResults; import org.eclipse.rdf4j.query.TupleQuery; import org.eclipse.rdf4j.query.TupleQueryResult; @@ -92,6 +94,29 @@ public void T21_not_exists_over_optional_is_always_false_here() throws IOExcepti } } + @Test + public void testSES2250BindErrors() { + + try (SailRepositoryConnection conn = repository.getConnection()) { + + conn.prepareUpdate(QueryLanguage.SPARQL, "insert data { _:blank }") + .execute(); + + String qb = "SELECT * {\n" + + " ?s1 ?p1 ?blank . " + + " FILTER(isBlank(?blank))" + + " BIND (iri(?blank) as ?biri)" + + " ?biri ?p2 ?o2 ." + + "}"; + + TupleQuery tq = conn.prepareTupleQuery(QueryLanguage.SPARQL, qb); + try (TupleQueryResult evaluate = tq.evaluate()) { + assertFalse(evaluate.hasNext(), + "The query should not return a result: " + Arrays.toString(evaluate.stream().toArray())); + } + } + } + private List selectWithData(RepositoryConnection conn, String data, RDFFormat format, String body) throws IOException { String sparql = PREFIX + body; From 9e1001e03b1e7f1df16b6a16b745861766761c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Fri, 3 Oct 2025 14:22:27 +0200 Subject: [PATCH 43/46] updated test --- .../MemoryStoreMinusScopingDebugTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java index b7b287bd0cc..3e7c7b4c350 100644 --- a/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java +++ b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java @@ -13,6 +13,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.io.IOException; import java.io.StringReader; @@ -117,6 +118,22 @@ public void testSES2250BindErrors() { } } + @Test + public void testEmptyUnion() { + try (SailRepositoryConnection conn = repository.getConnection()) { + + String query = "PREFIX : " + + "SELECT ?visibility WHERE {" + + "OPTIONAL { SELECT ?var WHERE { :s a :MyType . BIND (:s as ?var ) .} } ." + + "BIND (IF(BOUND(?var), 'VISIBLE', 'HIDDEN') as ?visibility)" + + "}"; + try (TupleQueryResult result = conn.prepareTupleQuery(QueryLanguage.SPARQL, query).evaluate()) { + assertNotNull(result); + assertFalse(result.hasNext()); + } + } + } + private List selectWithData(RepositoryConnection conn, String data, RDFFormat format, String body) throws IOException { String sparql = PREFIX + body; From 6df1ac199c0e8262a57f30e76c95c2c9be85ebcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Fri, 3 Oct 2025 14:55:25 +0200 Subject: [PATCH 44/46] updated test --- .../MemoryStoreMinusScopingDebugTest.java | 193 ++++++++++++++++++ 1 file changed, 193 insertions(+) diff --git a/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java index 3e7c7b4c350..6b2f1976a21 100644 --- a/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java +++ b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/MemoryStoreMinusScopingDebugTest.java @@ -134,6 +134,199 @@ public void testEmptyUnion() { } } + @Test + public void complexMinus_rhsOptionalBindOfOuterVar_unsharedBindIgnored() throws IOException { + String ttl = "@prefix : .\n" + + ":a :p 1 ; :q 1, 2 .\n" + + ":b :p 3 ; :q 4 .\n" + + ":c :p 7 ."; + + try (SailRepositoryConnection conn = repository.getConnection()) { + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?x WHERE {\n" + + " ?x :p ?n .\n" + + " MINUS { OPTIONAL { BIND(?n AS ?k) } ?x :q ?k }\n" + + "} ORDER BY ?x"); + + // Only :c lacks :q; binding of out-of-scope ?n in the RHS is ignored for scoping, + // and ?k is bound by ?x :q ?k when available. Thus :a and :b are removed. + assertEquals(setOf("c"), names(rows, "x")); + } + } + + @Test + public void complexMinus_rhsUnionSharedAndUnsharedBranches_onlySharedAffects() throws IOException { + String ttl = "@prefix : .\n" + + ":a :p 10 ; :q 20 .\n" + + ":b :p 20 ; :q 30 .\n" + + ":c :p 30 .\n" + + ":z :q 999 ."; + + try (SailRepositoryConnection conn = repository.getConnection()) { + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?x WHERE {\n" + + " ?x :p ?v .\n" + + " MINUS { { ?x :q ?v } UNION { ?y :q ?w } }\n" + + "} ORDER BY ?x"); + + // Unshared UNION branch must not affect MINUS; only { ?x :q ?v } would remove rows + // where q==p, which does not occur in the dataset. All subjects with :p remain. + assertEquals(setOf("a", "b", "c"), names(rows, "x")); + } + } + + @Test + public void complexNotExists_overBareOptional_alwaysFalse() { + try (SailRepositoryConnection conn = repository.getConnection()) { + String query = "SELECT * WHERE { BIND(1 AS ?d) FILTER NOT EXISTS { OPTIONAL { BIND(1 AS ?z) } } }"; + TupleQuery tq = conn.prepareTupleQuery(QueryLanguage.SPARQL, query); + try (TupleQueryResult r = tq.evaluate()) { + assertNotNull(r); + assertFalse(r.hasNext()); + } + } + } + + @Test + public void complexOptionalSubselect_noLeftBindings_emptyOptionalYieldsNoRow() { + try (SailRepositoryConnection conn = repository.getConnection()) { + String query = "SELECT ?flag WHERE { " + + "OPTIONAL { SELECT ?v WHERE { VALUES ?u { } } } " + + "BIND(IF(BOUND(?v), 'Y','N') AS ?flag) }"; + TupleQuery tq = conn.prepareTupleQuery(QueryLanguage.SPARQL, query); + try (TupleQueryResult r = tq.evaluate()) { + assertNotNull(r); + assertFalse(r.hasNext()); + } + } + } + + @Test + public void complexMinus_rhsSubselectOrderLimitBindEquality_removesLimitedMatches() throws IOException { + String ttl = "@prefix : .\n" + + ":e :p 10 ; :q 10 .\n" + + ":f :p 20 ; :q 20 .\n" + + ":g :p 30 ; :q 99 .\n" + + ":h :p 40 ; :q 40 ."; + + try (SailRepositoryConnection conn = repository.getConnection()) { + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?x WHERE {\n" + + " ?x :p ?v .\n" + + " MINUS { { SELECT ?x ?rv WHERE { ?x :q ?rv } ORDER BY ?rv LIMIT 2 } BIND(?rv AS ?v) }\n" + + "} ORDER BY ?x"); + + // The two smallest q-values are 10 and 20; only e and f match q==p among those, + // so they are removed. g and h remain. + assertEquals(setOf("g", "h"), names(rows, "x")); + } + } + + @Test + public void graphIsolation_sameGraphNoRemovalWhenValuesDiffer() throws IOException { + String trig = "@prefix : .\n" + + "GRAPH :g1 { :a :p 1 . }\n" + + "GRAPH :g2 { :a :q 1 . :a :p 2 . }"; + + try (SailRepositoryConnection conn = repository.getConnection()) { + List rows = selectWithData(conn, trig, RDFFormat.TRIG, + "SELECT ?g ?x ?n WHERE {\n" + + " GRAPH ?g { ?x :p ?n }\n" + + " MINUS { GRAPH ?g { ?x :q ?n } }\n" + + "} ORDER BY ?g ?x ?n"); + assertEquals(setOf("g1|a|1", "g2|a|2"), + rows.stream() + .map(bs -> name(bs.getValue("g")) + "|" + name(bs.getValue("x")) + "|" + + name(bs.getValue("n"))) + .collect(Collectors.toCollection(LinkedHashSet::new))); + } + } + + @Test + public void graphIsolation_removesOnlyInGraphWithMatch() throws IOException { + String trig = "@prefix : .\n" + + "GRAPH :g1 { :a :p 1 . }\n" + + "GRAPH :g2 { :a :q 1 . :a :p 2 . :a :q 2 . }"; + + try (SailRepositoryConnection conn = repository.getConnection()) { + List rows = selectWithData(conn, trig, RDFFormat.TRIG, + "SELECT ?g ?x ?n WHERE {\n" + + " GRAPH ?g { ?x :p ?n }\n" + + " MINUS { GRAPH ?g { ?x :q ?n } }\n" + + "} ORDER BY ?g"); + assertEquals(setOf("g1|a|1"), + rows.stream() + .map(bs -> name(bs.getValue("g")) + "|" + name(bs.getValue("x")) + "|" + + name(bs.getValue("n"))) + .collect(Collectors.toCollection(LinkedHashSet::new))); + } + } + + @Test + public void valuesOnRight_sharedVarRemovesOnlyListedSubjects() throws IOException { + String ttl = "@prefix : .\n" + + ":a :p 1 . :b :p 2 . :c :p 3 ."; + try (SailRepositoryConnection conn = repository.getConnection()) { + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?x WHERE { ?x :p ?v MINUS { VALUES ?x { :a :c } } } ORDER BY ?x"); + assertEquals(setOf("b"), names(rows, "x")); + } + } + + @Test + public void nestedNotExistsOverOptionalWithUnion_isAlwaysFalse() { + try (SailRepositoryConnection conn = repository.getConnection()) { + String q = "SELECT * WHERE { BIND(1 AS ?d) FILTER NOT EXISTS { OPTIONAL { { BIND(1 AS ?z) } UNION { BIND(2 AS ?z) FILTER(?z>1) } } } }"; + TupleQuery tq = conn.prepareTupleQuery(QueryLanguage.SPARQL, q); + try (TupleQueryResult r = tq.evaluate()) { + assertNotNull(r); + assertFalse(r.hasNext()); + } + } + } + + @Test + public void optionalSubselectWithLeftBindings_keepsLeftRows() throws IOException { + String ttl = "@prefix : .\n" + + ":a :p 1 ."; + try (SailRepositoryConnection conn = repository.getConnection()) { + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?x WHERE { ?x :p ?v OPTIONAL { SELECT ?z WHERE { FILTER(false) } } } ORDER BY ?x"); + assertEquals(setOf("a"), names(rows, "x")); + } + } + + @Test + public void minusRhsUnionSubselects_withGraphs_onlySameGraphBranchRemoves() throws IOException { + String trig = "@prefix : .\n" + + "GRAPH :g1 { :a :p 1 . :a :q 1 . }\n" + + "GRAPH :g2 { :b :p 2 . :c :q 2 . }"; + try (SailRepositoryConnection conn = repository.getConnection()) { + List rows = selectWithData(conn, trig, RDFFormat.TRIG, + "SELECT ?g ?x ?n WHERE {\n" + + " GRAPH ?g { ?x :p ?n }\n" + + " MINUS { { GRAPH ?g { ?x :q ?n } } UNION { GRAPH :g2 { ?x :q ?n } } }\n" + + "} ORDER BY ?g ?x ?n"); + assertEquals(setOf("g2|b|2"), + rows.stream() + .map(bs -> name(bs.getValue("g")) + "|" + name(bs.getValue("x")) + "|" + + name(bs.getValue("n"))) + .collect(Collectors.toCollection(LinkedHashSet::new))); + } + } + + @Test + public void bnodeOnRhsViaUnionAndSubselect_cannotMatchDataIds() throws IOException { + String ttl = "@prefix : .\n" + + "_:b1 a [] . _:b2 a [] .\n" + + ":k :id _:b1 . :l :id _:b2 ."; + try (SailRepositoryConnection conn = repository.getConnection()) { + List rows = selectWithData(conn, ttl, RDFFormat.TURTLE, + "SELECT ?s WHERE { ?s :id ?id MINUS { { BIND(BNODE() AS ?id) } UNION { SELECT ?id WHERE { BIND(BNODE() AS ?id) } } } } ORDER BY ?s"); + assertEquals(setOf("k", "l"), names(rows, "s")); + } + } + private List selectWithData(RepositoryConnection conn, String data, RDFFormat format, String body) throws IOException { String sparql = PREFIX + body; From 8a3a131105c7c004d1c68fd950025e37e5116940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Fri, 3 Oct 2025 15:07:53 +0200 Subject: [PATCH 45/46] fix for failing tests --- .../impl/DefaultEvaluationStrategy.java | 35 +++++++++++++++++-- .../iterator/ExtensionIterator.java | 22 +++++++----- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/DefaultEvaluationStrategy.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/DefaultEvaluationStrategy.java index 901557a1b78..76714b12d75 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/DefaultEvaluationStrategy.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/DefaultEvaluationStrategy.java @@ -603,11 +603,31 @@ protected QueryEvaluationStep prepare(Slice node, QueryEvaluationContext context protected QueryEvaluationStep prepare(Extension node, QueryEvaluationContext context) throws QueryEvaluationException { QueryEvaluationStep arg = precompile(node.getArg(), context); + boolean setNullOnError = !isWithinMinusRightArg(node); Consumer consumer = ExtensionIterator.buildLambdaToEvaluateTheExpressions(node, this, - context); + context, setNullOnError); return new ExtensionQueryEvaluationStep(arg, consumer, context); } + private static boolean isWithinMinusRightArg(QueryModelNode node) { + QueryModelNode child = node; + QueryModelNode parent = node.getParentNode(); + while (parent != null) { + if (parent instanceof Difference) { + Difference diff = (Difference) parent; + if (diff.getRightArg() == child) { + return true; + } + if (diff.getLeftArg() == child) { + return false; + } + } + child = parent; + parent = parent.getParentNode(); + } + return false; + } + protected QueryEvaluationStep prepare(Service service, QueryEvaluationContext context) throws QueryEvaluationException { Var serviceRef = service.getServiceRef(); @@ -1321,7 +1341,18 @@ protected QueryValueEvaluationStep prepare(CompareAll node, QueryEvaluationConte protected QueryValueEvaluationStep prepare(Exists node, QueryEvaluationContext context) throws QueryEvaluationException { - QueryEvaluationStep subquery = precompile(node.getSubQuery(), context); + // Optimization/semantic shortcut: EXISTS { OPTIONAL { ... } } is always true. + // In algebra, OPTIONAL is a LeftJoin. With a SingletonSet left-arg, LeftJoin + // always yields at least the input binding set. Therefore EXISTS evaluates to TRUE. + TupleExpr subQuery = node.getSubQuery(); + if (subQuery instanceof LeftJoin) { + LeftJoin leftJoin = (LeftJoin) subQuery; + if (leftJoin.getLeftArg() instanceof SingletonSet) { + return bindings -> BooleanLiteral.TRUE; + } + } + + QueryEvaluationStep subquery = precompile(subQuery, context); return new ExistsQueryValueEvaluationStep(subquery); } diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/ExtensionIterator.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/ExtensionIterator.java index 8c6fbbd65ff..af2c049a773 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/ExtensionIterator.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/ExtensionIterator.java @@ -37,7 +37,7 @@ public ExtensionIterator(Extension extension, CloseableIteration ite EvaluationStrategy strategy, QueryEvaluationContext context) throws QueryEvaluationException { super(iter); this.context = context; - this.setter = buildLambdaToEvaluateTheExpressions(extension, strategy, context); + this.setter = buildLambdaToEvaluateTheExpressions(extension, strategy, context, true); } public ExtensionIterator(CloseableIteration iter, @@ -48,14 +48,16 @@ public ExtensionIterator(CloseableIteration iter, } public static Consumer buildLambdaToEvaluateTheExpressions(Extension extension, - EvaluationStrategy strategy, QueryEvaluationContext context) { + EvaluationStrategy strategy, QueryEvaluationContext context, boolean setNullOnError) { Consumer consumer = null; for (ExtensionElem extElem : extension.getElements()) { ValueExpr expr = extElem.getExpr(); if (!(expr instanceof AggregateOperator)) { QueryValueEvaluationStep prepared = strategy.precompile(extElem.getExpr(), context); BiConsumer setBinding = context.setBinding(extElem.getName()); - consumer = andThen(consumer, targetBindings -> setValue(setBinding, prepared, targetBindings)); + boolean setNullOnErrorLocal = setNullOnError; + consumer = andThen(consumer, + targetBindings -> setValue(setBinding, prepared, targetBindings, setNullOnErrorLocal)); } } if (consumer == null) { @@ -67,7 +69,7 @@ public static Consumer buildLambdaToEvaluateTheExpressions(Ex } private static void setValue(BiConsumer setBinding, QueryValueEvaluationStep prepared, - MutableBindingSet targetBindings) { + MutableBindingSet targetBindings, boolean setNullOnError) { try { // we evaluate each extension element over the targetbindings, so that bindings from // a previous extension element in this same extension can be used by other extension elements. @@ -79,11 +81,13 @@ private static void setValue(BiConsumer setBinding, Qu setBinding.accept(targetValue, targetBindings); } } catch (ValueExprEvaluationException e) { - // silently ignore type errors in extension arguments. They should not cause the - // query to fail but result in no bindings for this solution - // see https://www.w3.org/TR/sparql11-query/#assignment - // use null as place holder for unbound variables that must remain so - setBinding.accept(null, targetBindings); + // Silently ignore type errors in extension arguments. Whether to install a + // placeholder null-binding (variable must remain unbound) or leave it unset + // depends on context. By default we set a null-binding to enforce the SPARQL + // assignment rule; in special contexts (e.g., MINUS RHS), we may skip it. + if (setNullOnError) { + setBinding.accept(null, targetBindings); + } } } From 30e8bc06894ae1cbc2e8171020923d11f7dd2b53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Fri, 3 Oct 2025 15:13:20 +0200 Subject: [PATCH 46/46] preserve exact literals --- .../java/org/eclipse/rdf4j/model/base/AbstractValueFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractValueFactory.java b/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractValueFactory.java index c2f49a56adf..5670ccb0da3 100644 --- a/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractValueFactory.java +++ b/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractValueFactory.java @@ -132,7 +132,7 @@ public Literal createLiteral(String label, CoreDatatype datatype) { if (label.length() <= 3 && !label.startsWith("+") && !label.startsWith("-")) { try { int v = Integer.parseInt(label); - if (v >= 0 && v <= 999) { + if (v >= 0 && v <= 999 && label.equals(Integer.toString(v))) { return smallIntLiterals[v]; } } catch (NumberFormatException e) {