diff --git a/AGENTS.md b/AGENTS.md index 43824dfe312..1a43fca9b46 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -718,6 +718,12 @@ rdf4j: root project * Don’t commit or push unless explicitly asked. * Don’t add new dependencies without explicit approval. +### Version Control Conventions + +* Branch names must always start with the GitHub issue identifier in the form `GH-XXXX`, where `XXXX` is the numeric issue number. +* Every commit message must be prefixed with the corresponding `GH-XXXX` label. +* Exception: if no GitHub issue number is available for the task, clearly note this in your handoff and align with the requester on an appropriate branch/commit prefix before proceeding. + 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. 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 dd412527a9d..199d349ed7f 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 @@ -32,6 +32,9 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.time.DateTimeException; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; @@ -704,15 +707,24 @@ private static int key(Predicate include, ChronoField... fields) { this.value = value; - datatype = DATATYPES.get(key(value)); + TemporalAccessor lexicalValue = value; - if (datatype == null) { + CoreDatatype.XSD detectedDatatype = DATATYPES.get(key(lexicalValue)); + + if (detectedDatatype == null && value.isSupported(ChronoField.INSTANT_SECONDS)) { + Instant instant = Instant.from(value); + lexicalValue = OffsetDateTime.ofInstant(instant, ZoneOffset.UTC); + detectedDatatype = DATATYPES.get(key(lexicalValue)); + } + + if (detectedDatatype == null) { throw new IllegalArgumentException(String.format( "value <%s> cannot be represented by an XML Schema date/time datatype", value )); } - this.label = FORMATTERS.get(datatype).format(value); + datatype = detectedDatatype; + this.label = FORMATTERS.get(datatype).format(lexicalValue); } @Override diff --git a/core/model/src/test/java/org/eclipse/rdf4j/model/util/LiteralsTest.java b/core/model/src/test/java/org/eclipse/rdf4j/model/util/LiteralsTest.java index 3fa09603f70..2e8930b9bd0 100644 --- a/core/model/src/test/java/org/eclipse/rdf4j/model/util/LiteralsTest.java +++ b/core/model/src/test/java/org/eclipse/rdf4j/model/util/LiteralsTest.java @@ -11,6 +11,7 @@ package org.eclipse.rdf4j.model.util; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -18,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import java.time.Instant; import java.util.Date; import java.util.GregorianCalendar; import java.util.IllformedLocaleException; @@ -107,6 +109,17 @@ public void testNormaliseBCP47Tag() { .isThrownBy(() -> Literals.normalizeLanguageTag("ru-ua-latn")); } + @Test + public void valuesLiteralSupportsInstant() { + Instant instant = Instant.parse("2022-08-01T21:14:38.470534100Z"); + + Literal literal = Values.literal(instant); + + assertThat(Instant.parse(literal.getLabel())).isEqualTo(instant); + assertThat(literal.getDatatype()).isEqualTo(XSD.DATETIME); + assertThat(literal.temporalAccessorValue()).isEqualTo(instant); + } + /** * Test method for * {@link org.eclipse.rdf4j.model.util.Literals#getLabel(org.eclipse.rdf4j.model.Literal, java.lang.String)} . diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStore.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStore.java index f13bde517ab..2dc7fd6e517 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStore.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStore.java @@ -133,6 +133,9 @@ public LmdbStore(LmdbStoreConfig config) { IsolationLevels.SNAPSHOT, IsolationLevels.SERIALIZABLE); setDefaultIsolationLevel(IsolationLevels.SNAPSHOT_READ); config.getDefaultQueryEvaluationMode().ifPresent(this::setDefaultQueryEvaluationMode); + if (config.getIterationCacheSyncThreshold() > 0) { + setIterationCacheSyncThreshold(config.getIterationCacheSyncThreshold()); + } EvaluationStrategyFactory evalStrategyFactory = config.getEvaluationStrategyFactory(); if (evalStrategyFactory != null) { setEvaluationStrategyFactory(evalStrategyFactory); 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 index 6bffee78436..62dffba9ffe 100644 --- 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 @@ -22,6 +22,8 @@ import org.eclipse.rdf4j.model.impl.LinkedHashModel; import org.eclipse.rdf4j.model.util.ModelBuilder; import org.eclipse.rdf4j.model.util.Values; +import org.eclipse.rdf4j.sail.lmdb.LmdbStore; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -65,6 +67,17 @@ void testThatLmdbStoreConfigParseAndExportValueCacheSize(final int valueCacheSiz // TODO: Add more tests for other properties + @Test + void setIterationCacheSyncThresholdShouldApplyToCreatedStore() { + final long threshold = 42; + final LmdbStoreConfig config = new LmdbStoreConfig(); + config.setIterationCacheSyncThreshold(threshold); + + final LmdbStore store = new LmdbStore(config); + + assertThat(store.getIterationCacheSyncThreshold()).isEqualTo(threshold); + } + /** * Generic method to test parsing and exporting of config properties. * diff --git a/site/content/about.md b/site/content/about.md index 948c6c9c07d..0dd9fdae8e5 100644 --- a/site/content/about.md +++ b/site/content/about.md @@ -31,6 +31,10 @@ This is useful if you are already using Elasticsearch for other things in your p A good usecase is if you need reference data or an ontology for your application. The built-in read cache makes it a good choice for data that updates infrequently, though for most usecases the NativeStore will be considerably faster. +{{< alert title="Licensing note" color="warning" >}} +Elasticsearch itself is distributed under the Elastic License (with SSPL as an alternative). If you intend to use the optional Elasticsearch-backed features in RDF4J, please make sure to evaluate whether the licensing terms of Elasticsearch align with the needs of your project before adopting it. +{{< /alert >}} + On top of these core databases, RDF4J offers a number of functional extensions. These extensions add functionality such as improved full-text search, RDFS inferencing, rule-based reasoning and validation using SHACL/SPIN, and geospatial querying support. For more information see the [RDF4J documentation](/documentation). ### Third party database solutions diff --git a/site/content/documentation/programming/repository.md b/site/content/documentation/programming/repository.md index 691a839c74e..52cca3cd8c1 100644 --- a/site/content/documentation/programming/repository.md +++ b/site/content/documentation/programming/repository.md @@ -113,8 +113,8 @@ The ElasticsearchStore stores RDF data in Elasticsearch. Not to be confused with The ElasticsearchStore is experimental and future releases may be incompatible with the current version. Write-ahead-logging is not supported. This means that a write operation can appear to have partially succeeded if the ElasticsearchStore looses its connection to Elasticsearch during a commit. -Note that, while RDF4J is licensed under the EDL, several ElasticSearch dependencies are licensed under the Elastic License or the SSPL, -which may have implications for some projects. +Note that, while RDF4J is licensed under the EDL, Elasticsearch itself is distributed under the Elastic License (with SSPL as an alternative). +The Elasticsearch-backed functionality in RDF4J is optional, and adopters should carefully evaluate the Elasticsearch licensing terms to ensure they meet the needs of their projects before enabling it. Please consult the ElasticSearch website and [license FAQ](https://www.elastic.co/licensing/elastic-license/faq) for more information. Transaction isolation is not as strong as for the other stores. The highest supported level is READ_COMMITTED, and even this diff --git a/site/layouts/shortcodes/alert.html b/site/layouts/shortcodes/alert.html new file mode 100644 index 00000000000..6b2eab23334 --- /dev/null +++ b/site/layouts/shortcodes/alert.html @@ -0,0 +1,19 @@ +{{- $type := lower (default "info" (.Get "type")) -}} +{{- $title := .Get "title" -}} +{{- $icons := dict "info" "fa-info-circle" "warning" "fa-exclamation-triangle" "danger" "fa-exclamation-circle" "success" "fa-check-circle" -}} +{{- $backgrounds := dict "info" "#e0f4ff" "warning" "#fff4e0" "danger" "#ffe3e3" "success" "#e7f5e7" -}} +{{- $borders := dict "info" "#228be6" "warning" "#f08c00" "danger" "#c92a2a" "success" "#2f9e44" -}} +{{- $icon := index $icons $type | default (index $icons "info") -}} +{{- $background := index $backgrounds $type | default (index $backgrounds "info") -}} +{{- $border := index $borders $type | default (index $borders "info") -}} +