Skip to content
Draft
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
23 changes: 22 additions & 1 deletion fdb-relational-api/fdb-relational-api.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import java.time.Instant

plugins {
alias(libs.plugins.gitversion)
alias(libs.plugins.errorprone)
id 'java-test-fixtures'
}

Expand Down Expand Up @@ -64,16 +65,36 @@ dependencies {
exclude(group: "com.squareup", module: "javapoet")
}
api(libs.protobuf)
// jsr305 is still needed for javax.annotation.concurrent.Immutable/NotThreadSafe usages
// in this module; jspecify only covers null-checking (see @NullMarked package-info.java
// files in com.apple.foundationdb.relational.api / .util).
compileOnly(libs.jsr305)
compileOnly(libs.jspecify)
implementation(libs.guava)

errorprone(libs.errorprone.core)
errorprone(libs.nullaway)

testImplementation(libs.bundles.test.impl)
testImplementation(libs.bundles.test.runtime)
testCompileOnly(libs.jsr305)

testFixturesImplementation(libs.assertj)
}

// jspecify + NullAway null-checking, scoped to this module only. See @NullMarked
// package-info.java files in com.apple.foundationdb.relational.api (and sub-packages) and
// com.apple.foundationdb.relational.util. Companion change to the fdb-relational-grpc and
// fdb-relational-jdbc null-checking adoptions.
tasks.withType(JavaCompile).configureEach {
options.errorprone {
disableAllChecks = true
error("NullAway")
option("NullAway:AnnotatedPackages", "com.apple.foundationdb.relational.api,com.apple.foundationdb.relational.util")
option("NullAway:JSpecifyMode", "true")
option("NullAway:AcknowledgeRestrictiveAnnotations", "true")
}
}

sourceSets {
main {
java.srcDir ".out/generated/sources/annotationProcessor/java/main/"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import com.apple.foundationdb.relational.api.exceptions.ErrorCode;
import com.apple.foundationdb.relational.api.metadata.DataType;

import javax.annotation.Nonnull;
import java.sql.SQLException;
import java.sql.Wrapper;

Expand Down Expand Up @@ -68,6 +67,5 @@ default StructMetaData getElementStructMetaData() throws SQLException {
* @return the datatype object.
* @throws SQLException if something goes wrong.
*/
@Nonnull
DataType.ArrayType asRelationalType() throws SQLException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

package com.apple.foundationdb.relational.api;

import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;

public interface Continuation {

Expand Down Expand Up @@ -67,6 +67,7 @@ default boolean atBeginning() {
return getExecutionState() == null;
}

@SuppressWarnings("NullAway") // NullAway/JSpecify does not currently track @Nullable on array (byte[]) return types reliably across this local assignment; getExecutionState() genuinely returns null when there is no cursor state.
default boolean atEnd() {
byte[] bytes = getExecutionState();
return bytes != null && bytes.length == 0;
Expand All @@ -76,5 +77,6 @@ default boolean atEnd() {
* Returns the reason why the continuation was generated in the first place.
* @return the reason
*/
@Nullable
Reason getReason();
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import com.google.protobuf.Descriptors;
import com.google.protobuf.Message;

import javax.annotation.Nonnull;
import javax.annotation.concurrent.NotThreadSafe;
import java.sql.SQLException;
import java.util.Set;
Expand Down Expand Up @@ -80,6 +79,5 @@ public interface DynamicMessageBuilder {

Descriptors.Descriptor getDescriptor();

@Nonnull
DynamicMessageBuilder newBuilder();
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

import com.apple.foundationdb.relational.api.exceptions.ErrorCode;

import org.jspecify.annotations.Nullable;

import java.sql.SQLException;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -38,13 +40,14 @@ public Map<String, Object> toMap() {
}

@Override
public KeySet setKeyColumn(String columnName, Object value) throws SQLException {
public KeySet setKeyColumn(String columnName, @Nullable Object value) throws SQLException {
throw new SQLException("The Empty Keyset cannot be modified", ErrorCode.UNSUPPORTED_OPERATION.getErrorCode());

}
};

@SuppressWarnings("PMD.AvoidFieldNameMatchingTypeName")
@Nullable
private Map<String, Object> keySet;

public Map<String, Object> toMap() {
Expand All @@ -61,7 +64,7 @@ public Map<String, Object> toMap() {
* @return the constructed key set that was inserted in the map
* @throws SQLException Unsupported operation if the KeySet is immutable
*/
public KeySet setKeyColumn(String columnName, Object value) throws SQLException {
public KeySet setKeyColumn(String columnName, @Nullable Object value) throws SQLException {
if (keySet == null) {
keySet = new HashMap<>();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;

import java.sql.SQLException;
import java.util.Collection;
import java.util.Collections;
Expand Down Expand Up @@ -298,7 +298,6 @@ public enum VectorIndexEnginePreference {
@SuppressWarnings("PMD.AvoidFieldNameMatchingTypeName")
private static final Map<Name, List<OptionContract>> OPTIONS = makeContracts();

@Nonnull
private static final Map<Name, Object> OPTIONS_DEFAULT_VALUES;

private static final Object NULL_STANDIN = new Object();
Expand Down Expand Up @@ -336,45 +335,41 @@ public enum VectorIndexEnginePreference {

@Nullable
private final Options parentOptions;
@Nonnull
private final Map<Name, Object> optionsMap;

@Nonnull
public static Options none() {
return NONE;
}

@Nonnull
public static Map<Name, Object> defaultOptions() {
return OPTIONS_DEFAULT_VALUES;
}

private Options(@Nonnull Map<Name, Object> optionsMap, @Nullable Options parentOptions) {
private Options(Map<Name, Object> optionsMap, @Nullable Options parentOptions) {
this.optionsMap = optionsMap;
this.parentOptions = parentOptions;
}

@SuppressWarnings("unchecked")
public <T> T getOption(@Nonnull Name name) {
T option = getOptionInternal(name);
@SuppressWarnings({"unchecked", "NullAway"}) // Whether this can return null depends on whether `name` has a registered default in OPTIONS_DEFAULT_VALUES (a runtime fact, not visible to the type system); genuinely returns null for options such as INDEX_HINT that have neither a set value nor a default.
public <T extends @Nullable Object> T getOption(Name name) {
@Nullable T option = getOptionInternal(name);
if (option == null) {
return (T) OPTIONS_DEFAULT_VALUES.get(name);
} else {
return option;
}
}

public Options withOption(@Nonnull Name name, @Nullable Object value) throws SQLException {
public Options withOption(Name name, @Nullable Object value) throws SQLException {
return builder().fromOptions(this).withOption(name, value).build();
}

public Options withChild(@Nonnull Options childOptions) throws SQLException {
public Options withChild(Options childOptions) throws SQLException {
return Options.combine(this, childOptions);
}

@Nonnull
@SuppressWarnings({"PMD.CompareObjectsWithEquals"})
private static Options combine(@Nonnull Options parentOptions, @Nonnull Options childOptions) throws SQLException {
private static Options combine(Options parentOptions, Options childOptions) throws SQLException {
if (childOptions.parentOptions != null) {
throw new SQLException("Cannot override parent options", ErrorCode.INTERNAL_ERROR.getErrorCode());
}
Expand All @@ -386,14 +381,12 @@ private static Options combine(@Nonnull Options parentOptions, @Nonnull Options
return new Options(childOptions.optionsMap, parentOptions);
}

@Nonnull
public static Builder builder() {
return new Builder();
}

public static final class Builder {

@Nonnull
private final Map<Name, Object> optionsMap;

@Nullable
Expand All @@ -403,14 +396,12 @@ private Builder() {
optionsMap = Maps.newHashMap();
}

@Nonnull
public Builder withOptionFromString(Name name, String valueAsString) throws SQLException {
final Object value = parseStringOption(name, valueAsString);
return withOption(name, value);
}

@Nonnull
public Builder withOption(@Nonnull Name name, @Nullable Object value) throws SQLException {
public Builder withOption(Name name, @Nullable Object value) throws SQLException {
if (value == NULL_STANDIN) {
optionsMap.put(name, NULL_STANDIN);
} else {
Expand All @@ -424,7 +415,6 @@ public Builder withOption(@Nonnull Name name, @Nullable Object value) throws SQL
return this;
}

@Nonnull
public Builder fromOptions(Options options) throws SQLException {
optionsMap.putAll(options.optionsMap);
if (parentOptions != null) {
Expand All @@ -441,24 +431,25 @@ public void setParentOption(@Nullable final Options parentOptions) {
this.parentOptions = parentOptions;
}

@Nonnull
public Options build() {
return new Options(ImmutableMap.copyOf(optionsMap), parentOptions);
}
}

@Nullable
private static Object parseStringOption(@Nonnull final Name name, String valueAsString) throws SQLException {
for (OptionContract contract : Objects.requireNonNull(OPTIONS).get(name)) {
private static Object parseStringOption(final Name name, String valueAsString) throws SQLException {
// makeContracts() registers a contract list for every Name constant, so this lookup is never null.
for (OptionContract contract : Objects.requireNonNull(OPTIONS.get(name))) {
if (contract instanceof OptionContractWithConversion<?>) {
return ((OptionContractWithConversion<?>)contract).fromString(valueAsString);
}
}
throw new SQLException("option must have at least one type contract", ErrorCode.INTERNAL_ERROR.getErrorCode());
}

private static void validateOption(@Nonnull final Name name, Object value) throws SQLException {
for (OptionContract contract : Objects.requireNonNull(OPTIONS).get(name)) {
private static void validateOption(final Name name, @Nullable Object value) throws SQLException {
// makeContracts() registers a contract list for every Name constant, so this lookup is never null.
for (OptionContract contract : Objects.requireNonNull(OPTIONS.get(name))) {
contract.validate(name, value);
}
}
Expand All @@ -476,7 +467,6 @@ private <T> T getOptionInternal(Name name) {
}
}

@Nonnull
public Iterable<? extends Map.Entry<Name, ?>> entries() {
if (parentOptions != null) {
return Iterables.concat(parentOptions.entries(), optionsMap.entrySet());
Expand All @@ -490,7 +480,7 @@ public static boolean isNull(@Nullable Object object) {
}

@Override
public boolean equals(final Object o) {
public boolean equals(@Nullable final Object o) {
if (!(o instanceof Options)) {
return false;
}
Expand Down Expand Up @@ -521,7 +511,6 @@ public static Options fromProperties(@Nullable Properties properties) throws SQL
return builder.build();
}

@Nonnull
@SuppressWarnings("unchecked")
public static Properties toProperties(final Options options) {
final Properties result = new Properties();
Expand All @@ -545,7 +534,7 @@ public static Properties toProperties(final Options options) {
}

// TODO: This is just to avoid dependencies; use HexFormat when upgraded to JDK 17.
private static String bytesToHex(@Nonnull byte[] bytes) {
private static String bytesToHex(byte[] bytes) {
char[] hex = new char[bytes.length * 2];
for (int i = 0; i < bytes.length; i++ ) {
int b = bytes[i] & 0xFF;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@

package com.apple.foundationdb.relational.api;

import javax.annotation.Nonnull;

/**
* This represents query parsing information.
*
Expand All @@ -43,6 +41,5 @@ enum QueryType {
OTHER
}

@Nonnull
QueryType getQueryType();
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@

import com.apple.foundationdb.relational.api.metadata.DataType;

import javax.annotation.Nonnull;
import java.sql.Array;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
Expand Down Expand Up @@ -101,7 +100,6 @@ default boolean isWrapperFor(Class<?> iface) throws SQLException {
return iface.isInstance(this);
}

@Nonnull
@Override
default DataType getRelationalMetaData() throws SQLException {
return getMetaData().asRelationalType();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

package com.apple.foundationdb.relational.api;

import javax.annotation.Nonnull;
import java.sql.SQLException;
import java.util.UUID;

Expand All @@ -37,17 +36,17 @@ public interface RelationalArrayBuilder {
*/
RelationalArray build() throws SQLException;

RelationalArrayBuilder addAll(@Nonnull Object... value) throws SQLException;
RelationalArrayBuilder addAll(Object... value) throws SQLException;

RelationalArrayBuilder addBytes(@Nonnull byte[] value) throws SQLException;
RelationalArrayBuilder addBytes(byte[] value) throws SQLException;

RelationalArrayBuilder addString(@Nonnull String value) throws SQLException;
RelationalArrayBuilder addString(String value) throws SQLException;

RelationalArrayBuilder addLong(long value) throws SQLException;

RelationalArrayBuilder addUuid(@Nonnull UUID value) throws SQLException;
RelationalArrayBuilder addUuid(UUID value) throws SQLException;

RelationalArrayBuilder addObject(@Nonnull Object value) throws SQLException;
RelationalArrayBuilder addObject(Object value) throws SQLException;

RelationalArrayBuilder addStruct(RelationalStruct struct) throws SQLException;
}
Loading