Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .mvn/jvm.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,8 @@ record Failed(String message) implements DataState {
private static String formatValue(Array array, int i, DType declared) {
if (declared instanceof DType.Extension ext
&& io.github.dfa1.vortex.extension.ExtensionId.tryFrom(ext.extensionId())
== io.github.dfa1.vortex.extension.ExtensionId.VORTEX_DATE) {
.filter(id -> id == io.github.dfa1.vortex.extension.ExtensionId.VORTEX_DATE)
.isPresent()) {
try {
return io.github.dfa1.vortex.extension.DateExtension.INSTANCE.decode(array, i).toString();
} catch (RuntimeException e) {
Expand Down
4 changes: 4 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
<groupId>com.github.luben</groupId>
<artifactId>zstd-jni</artifactId>
</dependency>
<dependency>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>
</dependency>
<!-- testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
Expand Down
17 changes: 7 additions & 10 deletions core/src/main/java/io/github/dfa1/vortex/extension/Extension.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.github.dfa1.vortex.core.VortexException;

import java.util.Collection;
import java.util.Optional;

/// Contract for a Vortex extension type — pairs the wire-format identity
/// (an [ExtensionId]) with a factory for the matching [DType.Extension]
Expand Down Expand Up @@ -37,23 +38,19 @@ default Object encodeAll(DType.Extension dtype, Collection<?> values) {
throw new VortexException("encode not supported for " + extensionId());
}

/// Resolves a {@link DType.Extension} to its spec-defined singleton, or
/// {@code null} when the wire id isn't one of the four spec extensions.
/// Resolves a {@link DType.Extension} to its spec-defined singleton.
/// Closes over the closed-set spec impls; third-party extensions go
/// through {@link io.github.dfa1.vortex.encoding.Registry#lookup(ExtensionId)}.
///
/// @param dtype declared extension dtype
/// @return matching spec extension singleton, or {@code null}
static Extension findKnown(DType.Extension dtype) {
ExtensionId id = ExtensionId.tryFrom(dtype.extensionId());
if (id == null) {
return null;
}
return switch (id) {
/// @return matching spec extension singleton, or empty when the wire id
/// isn't one of the four spec extensions
static Optional<Extension> findKnown(DType.Extension dtype) {
return ExtensionId.tryFrom(dtype.extensionId()).map(id -> switch (id) {
case VORTEX_DATE -> DateExtension.INSTANCE;
case VORTEX_TIME -> TimeExtension.INSTANCE;
case VORTEX_TIMESTAMP -> TimestampExtension.INSTANCE;
case VORTEX_UUID -> UuidExtension.INSTANCE;
};
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import io.github.dfa1.vortex.core.VortexException;

import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand Down Expand Up @@ -48,9 +49,9 @@ public static ExtensionId from(String id) {
/// Non-throwing lookup for a raw extension id string.
///
/// @param id raw extension id string
/// @return matching constant, or {@code null} if not a known spec extension
public static ExtensionId tryFrom(String id) {
return LOOKUP.get(id);
/// @return matching constant, or empty if not a known spec extension
public static Optional<ExtensionId> tryFrom(String id) {
return Optional.ofNullable(LOOKUP.get(id));
}

/// Returns the canonical wire-format id string.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public DType.Extension dtype(boolean nullable) {
/// @param zone IANA timezone, or {@code null} for none
/// @param nullable whether the column allows nulls
/// @return matching extension dtype
public DType.Extension dtype(TimeUnit unit, ZoneId zone, boolean nullable) {
public DType.Extension dtype(TimeUnit unit, @org.jspecify.annotations.Nullable ZoneId zone, boolean nullable) {
byte[] tzBytes = zone == null ? new byte[0] : zone.getId().getBytes(StandardCharsets.UTF_8);
ByteBuffer meta = ByteBuffer.allocate(3 + tzBytes.length).order(ByteOrder.LITTLE_ENDIAN);
meta.put(0, (byte) unit.ordinal());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/// Spec extension types: {@link io.github.dfa1.vortex.extension.DateExtension},
/// {@link io.github.dfa1.vortex.extension.TimeExtension},
/// {@link io.github.dfa1.vortex.extension.TimestampExtension},
/// {@link io.github.dfa1.vortex.extension.UuidExtension}, plus the
/// {@link io.github.dfa1.vortex.extension.Extension} SPI for third-party impls.
@org.jspecify.annotations.NullMarked
package io.github.dfa1.vortex.extension;
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ class ExtensionIdTest {
void tryFrom_knownIds_returnEnumConstant(String wire, ExtensionId expected) {
// Given / When / Then — wire string round-trips to the enum constant
// so the LOOKUP map stays in sync with the enum definition
assertThat(ExtensionId.tryFrom(wire)).isSameAs(expected);
assertThat(ExtensionId.tryFrom(wire)).contains(expected);
}

@Test
void tryFrom_unknownId_returnsNull() {
void tryFrom_unknownId_returnsEmpty() {
// Given — open-world extension id; library doesn't recognise it
// When / Then — non-throwing miss so the registry can route to passthrough
assertThat(ExtensionId.tryFrom("acme.geopoint")).isNull();
assertThat(ExtensionId.tryFrom("acme.geopoint")).isEmpty();
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ static ByteBuffer unitByte(byte tag) {
return meta;
}

static ByteBuffer tzMeta(byte unitTag, String tz) {
static ByteBuffer tzMeta(byte unitTag, @org.jspecify.annotations.Nullable String tz) {
byte[] tzBytes = tz == null ? new byte[0] : tz.getBytes(StandardCharsets.UTF_8);
ByteBuffer meta = ByteBuffer.allocate(3 + tzBytes.length).order(ByteOrder.LITTLE_ENDIAN);
meta.put(0, unitTag);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,8 @@ private static boolean fillCell(Object buffer, int rowIdx, ResultSet rs, int col
@SuppressWarnings("unchecked")
private static void fillExtensionCell(List<Object> buffer, ResultSet rs, int colIdx,
DType.Extension ext) throws SQLException {
ExtensionId id = ExtensionId.tryFrom(ext.extensionId());
if (id == null) {
throw new UnsupportedOperationException("unsupported extension: " + ext.extensionId());
}
ExtensionId id = ExtensionId.tryFrom(ext.extensionId())
.orElseThrow(() -> new UnsupportedOperationException("unsupported extension: " + ext.extensionId()));
// SQL NULL → null in the buffer. Nullable extension columns round-trip through the
// writer's ExtEncoding → MaskedEncoding → primitive layout (validity child preserved);
// NOT NULL columns reject any null element with VortexException during encode.
Expand Down
3 changes: 2 additions & 1 deletion performance/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,11 @@
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>25</release>
<annotationProcessorPaths>
<annotationProcessorPaths combine.children="append">
<path>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
Expand Down
32 changes: 32 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@
<maven-gpg-plugin.version>3.2.8</maven-gpg-plugin.version>
<maven-source-plugin.version>3.4.0</maven-source-plugin.version>
<maven-javadoc-plugin.version>3.12.0</maven-javadoc-plugin.version>
<!-- static analysis -->
<errorprone.version>2.36.0</errorprone.version>
<nullaway.version>0.12.3</nullaway.version>
<jspecify.version>1.0.0</jspecify.version>
</properties>

<dependencyManagement>
Expand Down Expand Up @@ -150,6 +154,11 @@
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
</dependency>
<dependency>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>
<version>${jspecify.version}</version>
</dependency>
<!-- testing -->
<dependency>
<groupId>com.h2database</groupId>
Expand Down Expand Up @@ -223,6 +232,29 @@
<version>3.15.0</version>
<configuration>
<release>25</release>
<!-- Error Prone + NullAway. Generated proto/fbs sources stay outside the
annotated set; checks fire only on hand-written io.github.dfa1.vortex code. -->
<annotationProcessorPaths>
<path>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_core</artifactId>
<version>${errorprone.version}</version>
</path>
<path>
<groupId>com.uber.nullaway</groupId>
<artifactId>nullaway</artifactId>
<version>${nullaway.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<arg>-XDcompilePolicy=simple</arg>
<arg>--should-stop=ifError=FLOW</arg>
<arg>-Xplugin:ErrorProne -XepDisableAllChecks -Xep:NullAway:ERROR -XepOpt:NullAway:OnlyNullMarked=true -XepOpt:NullAway:JSpecifyMode=true -XepOpt:NullAway:ExcludedClassAnnotations=javax.annotation.processing.Generated,jakarta.annotation.Generated -XepExcludedPaths:.*/(fbs|proto|cli/.*tui|cli/.*server)/.*</arg>
</compilerArgs>
<!-- Error Prone needs jdk.compiler internals exposed to the Maven JVM. The
required add-exports / add-opens flags live in .mvn/jvm.config so they
apply to every Maven invocation; setting them as compilerArgs has
no effect because maven-compiler-plugin runs javac in the same JVM. -->
</configuration>
</plugin>
<plugin>
Expand Down
6 changes: 2 additions & 4 deletions reader/src/main/java/io/github/dfa1/vortex/scan/Chunk.java
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,8 @@ public <T> List<T> as(String name, Class<T> domainType) {
if (!(colDtype instanceof DType.Extension ext)) {
throw new VortexException("not an extension column: " + name);
}
ExtensionId id = ExtensionId.tryFrom(ext.extensionId());
if (id == null) {
throw new VortexException("not a spec extension id: " + ext.extensionId());
}
ExtensionId id = ExtensionId.tryFrom(ext.extensionId())
.orElseThrow(() -> new VortexException("not a spec extension id: " + ext.extensionId()));
Array storage = column(name);
Object result = switch (id) {
case VORTEX_DATE -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,10 +310,10 @@ public void writeChunk(Map<String, Object> columns) throws IOException {
// ExtEncoding wraps the storage child below — matches Rust's nested layout
// (ExtEncoding → PrimitiveEncoding) and lets Registry skip its unwrap path.
if (colDtype instanceof DType.Extension extDtype && data instanceof java.util.Collection<?> coll) {
io.github.dfa1.vortex.extension.ExtensionId extId =
io.github.dfa1.vortex.extension.ExtensionId.tryFrom(extDtype.extensionId());
io.github.dfa1.vortex.extension.Extension impl =
extId == null ? null : defaultRegistry.lookup(extId);
io.github.dfa1.vortex.extension.ExtensionId.tryFrom(extDtype.extensionId())
.map(defaultRegistry::lookup)
.orElse(null);
if (impl != null) {
data = impl.encodeAll(extDtype, coll);
}
Expand Down
Loading