Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
f20f4ab
[CALCITE-7736] Replace the Checker Framework with NullAway and JSpecify
vlsi Aug 23, 2026
f5dd74b
[CALCITE-7736] Add the JSpecify Java 8 transformation workaround
vlsi Aug 30, 2026
f20f22e
[CALCITE-7736] Move the Checker Framework annotations to JSpecify
vlsi Aug 30, 2026
7e1564d
[CALCITE-7736] Replace @PolyNull with @Contract
vlsi Aug 23, 2026
96b63c8
[CALCITE-7736] Give type parameters the nullable bounds the Checker F…
vlsi Aug 23, 2026
bfed1fa
[CALCITE-7736] Let the linq4j tree visitor return null
vlsi Aug 24, 2026
9cc91fb
[CALCITE-7736] Admit that min and max return null for an empty sequence
vlsi Aug 24, 2026
6341b29
[CALCITE-7736] Let a seeded aggregate start from null
vlsi Aug 24, 2026
c77bf62
[CALCITE-7736] Drop castNonNull calls whose argument is already non-null
vlsi Aug 24, 2026
26a9c8b
[CALCITE-7736] Handle the nulls the JDK actually hands out
vlsi Aug 24, 2026
4950b01
[CALCITE-7736] Remove two annotations the migration made untrue
vlsi Aug 24, 2026
6ab9f0a
[CALCITE-7736] Let the memory window hold the nulls it is padded with
vlsi Aug 24, 2026
9618571
[CALCITE-7736] Let the join enumerators carry the null rows they emit
vlsi Aug 24, 2026
445da0a
[CALCITE-7736] Suppress the three do-nothing visitors
vlsi Aug 24, 2026
d2c1638
[CALCITE-7736] Instantiate the result-ignoring visitors with @Nullabl…
vlsi Aug 24, 2026
1c07442
[CALCITE-7736] Drop castNonNull calls whose argument is already non-null
vlsi Aug 24, 2026
b629a0f
[CALCITE-7736] Let the PairList implementations hold nullable pairs
vlsi Aug 24, 2026
35937dd
[CALCITE-7736] Let select and its siblings produce a nullable result
vlsi Aug 24, 2026
ca6d59c
[CALCITE-7736] Let the SQL collection helpers carry null elements
vlsi Aug 24, 2026
76740d2
[CALCITE-7736] Let the null-tolerant collection helpers take nullable…
vlsi Aug 24, 2026
d0e24ed
[CALCITE-7736] Let SqlFunctions carry the nulls that SQL values have
vlsi Aug 24, 2026
659e4da
[CALCITE-7736] Let the shared utilities take nullable arguments
vlsi Aug 24, 2026
df2d833
[CALCITE-7736] Let the utility collections and visitors carry nulls
vlsi Aug 24, 2026
0a0ce91
[CALCITE-7736] Drop the preconditions that only the Checker Framework…
vlsi Aug 24, 2026
eab6c5e
[CALCITE-7736] Say what the guards already check, and drop two more d…
vlsi Aug 24, 2026
9ce1a3e
[CALCITE-7736] Take the nullable bound on the acceptCall overrides, a…
vlsi Aug 24, 2026
fd0763c
[CALCITE-7736] Read the nulls the JDK hands out, and restore two narr…
vlsi Aug 24, 2026
1db3124
[CALCITE-7736] Let the values that are genuinely absent say so
vlsi Aug 24, 2026
3d3597a
[CALCITE-7736] Type the collections and results that hold absent values
vlsi Aug 24, 2026
1950aad
[CALCITE-7736] Let unwrap say it may find nothing, and split two cont…
vlsi Aug 24, 2026
26b97bb
[CALCITE-7736] Ask for what is already established, and let the expre…
vlsi Aug 24, 2026
b15a434
[CALCITE-7736] Say where a value is genuinely absent, and work around…
vlsi Aug 24, 2026
6c5a455
[CALCITE-7736] Let the profiled rows and composed lists hold absent v…
vlsi Aug 24, 2026
bc81670
[CALCITE-7736] Read each value once, and drop a castNonNull that outl…
vlsi Aug 24, 2026
02a6879
[CALCITE-7736] Close out the last of the core findings
vlsi Aug 24, 2026
71ce9d6
[CALCITE-7736] Give the Void visitors the return type their visitor i…
vlsi Aug 25, 2026
a469566
[CALCITE-7736] Carry the nullable element types through the JSON and …
vlsi Aug 30, 2026
44cc278
[CALCITE-7736] Carry the nullable element types through the adapters …
vlsi Aug 30, 2026
02c9628
[CALCITE-7736] Carry the nullable element types through the planner a…
vlsi Aug 30, 2026
0fb157b
[CALCITE-7736] Let NullAway itself require the explicit null-marking
vlsi Aug 25, 2026
2735be8
[CALCITE-7736] Put :server back under nullness verification
vlsi Aug 25, 2026
e0cc600
[CALCITE-7736] Put :druid under nullness verification
vlsi Aug 25, 2026
bcadcf8
[CALCITE-7736] Put :file under nullness verification
vlsi Aug 25, 2026
97dd4a5
[CALCITE-7736] Put :kafka under nullness verification
vlsi Aug 25, 2026
212273f
[CALCITE-7736] Put :spark under nullness verification
vlsi Aug 25, 2026
e74c151
[CALCITE-7736] Put :babel under nullness verification
vlsi Aug 25, 2026
48f4d9e
[CALCITE-7736] Put :redis under nullness verification
vlsi Aug 25, 2026
7837470
[CALCITE-7736] Put :splunk under nullness verification
vlsi Aug 25, 2026
da84088
[CALCITE-7736] Put :mongodb under nullness verification
vlsi Aug 25, 2026
7dcdd07
[CALCITE-7736] Put :cassandra under nullness verification
vlsi Aug 25, 2026
adfb154
[CALCITE-7736] Put :pig under nullness verification
vlsi Aug 25, 2026
fd2bc88
[CALCITE-7736] Put :arrow under nullness verification
vlsi Aug 25, 2026
a72e2fb
[CALCITE-7736] Put :innodb under nullness verification
vlsi Aug 25, 2026
78b1fcf
[CALCITE-7736] Put :piglet under nullness verification
vlsi Aug 25, 2026
adcf30e
[CALCITE-7736] Put :geode under nullness verification
vlsi Aug 25, 2026
1b3c752
[CALCITE-7736] Put :elasticsearch under nullness verification
vlsi Aug 25, 2026
bcddb57
[CALCITE-7736] Put :plus under nullness verification
vlsi Aug 25, 2026
00fef91
[CALCITE-7736] Put :example:csv and :example:function under nullness …
vlsi Aug 25, 2026
0bf3071
[CALCITE-7736] Put :testkit under nullness verification
vlsi Aug 26, 2026
a01ca17
[CALCITE-7736] Put :ubenchmark under nullness verification
vlsi Aug 26, 2026
da1171c
[CALCITE-7736] Upgrade NullAway to 0.14.2
vlsi Oct 11, 2026
21471ba
[CALCITE-7736] Move the nullness annotations to calcite-annotations
vlsi Oct 11, 2026
d8d3e22
[CALCITE-7736] Declare jspecify as an API dependency of every module
vlsi Oct 11, 2026
4d1630d
[CALCITE-7736] Reject a null query or ORDER BY list wherever SqlOrder…
vlsi Oct 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 0 additions & 3 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ ij_java_use_single_class_imports = true
max_line_length = 100
ij_any_wrap_long_lines = true

[*.astub]
indent_size = 2

[*.java]
# Doc: https://youtrack.jetbrains.com/issue/IDEA-170643#focus=streamItem-27-3708697.0-0
# $ means "static"
Expand Down
55 changes: 9 additions & 46 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -374,66 +374,29 @@ jobs:
./sqlsh -o headers "select count(*) commits, author from (select substring(author, 1, position(' <' in author)-1) author from git_commits) group by author order by count(*) desc, author limit 20"

errorprone-guava-latest:
name: 'ErrorProne (JDK 11, latest Guava)'
# This is the only job that verifies nullness, so a nullability problem fails here alone and
# the test jobs keep reporting test failures
name: 'ErrorProne and NullAway (JDK 25, latest Guava)'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 50
- name: 'Set up JDK 11'
# Gradle 8.14 does not run on JDK 25, so Gradle runs on JDK 21 and javac comes from JDK 25
- name: 'Set up JDK 25 for compilation and JDK 21 for Gradle'
uses: actions/setup-java@v6
with:
java-version: 11
java-version: |
25
21
distribution: 'zulu'
- uses: burrunan/gradle-cache-action@v1
name: Test
with:
job-id: errprone
remote-build-cache-proxy-enabled: false
# ErrorProne checks for Beta APIs, so use the latest supported Guava version
arguments: --scan --no-parallel --no-daemon -PjdkBuildVersion=11 -Pguava.version=${{ env.GUAVA_MAX }} -PenableErrorprone classes
properties: |
org.gradle.java.installations.auto-download=false

linux-checkerframework:
name: 'CheckerFramework (JDK 11)'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 50
- name: 'Set up JDK 11'
uses: actions/setup-java@v6
with:
java-version: 11
distribution: 'zulu'
- name: 'Run CheckerFramework'
uses: burrunan/gradle-cache-action@v1
with:
job-id: checkerframework-jdk11
remote-build-cache-proxy-enabled: false
arguments: --scan --no-parallel --no-daemon -PjdkBuildVersion=11 -PenableCheckerframework :linq4j:classes :core:classes :server:classes
properties: |
org.gradle.java.installations.auto-download=false

linux-checkerframework-oldest-guava:
name: 'CheckerFramework (JDK 11, oldest Guava)'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 50
- name: 'Set up JDK 11'
uses: actions/setup-java@v6
with:
java-version: 11
distribution: 'zulu'
- name: 'Run CheckerFramework'
uses: burrunan/gradle-cache-action@v1
with:
job-id: checkerframework-jdk11
remote-build-cache-proxy-enabled: false
arguments: --scan --no-parallel --no-daemon -PjdkBuildVersion=11 -Pguava.version=${{ env.GUAVA_MIN }} -PenableCheckerframework :linq4j:classes :core:classes :server:classes
arguments: --scan --no-parallel --no-daemon -Pguava.version=${{ env.GUAVA_MAX }} -PenableErrorprone classes jmhClasses
properties: |
org.gradle.java.installations.auto-download=false

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pentaho.aggdes.model;

import org.checkerframework.checker.nullness.qual.*;

interface Table {
@Nullable Table getParent();
dependencies {
api("org.jspecify:jspecify")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.calcite.annotations;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* States how the nullness of a method's result follows from the nullness of its arguments.
*
* <p>The value holds one or more clauses separated by {@code ;}. A clause lists one constraint per
* parameter, then {@code ->}, then what the method guarantees:
*
* <blockquote><pre>
* &#64;Contract("!null, _ -&gt; !null")
* public static &#64;Nullable Integer plus(&#64;Nullable Integer b0, int b1) { ... }
* </pre></blockquote>
*
* <p>A constraint is {@code null}, {@code !null}, {@code true}, {@code false} or {@code _} for
* "anything". The guarantee is one of those, or {@code fail} for a method that always throws.
*
* <p>This replaces the {@code @PolyNull} qualifier of the Checker Framework, which has no JSpecify
* equivalent. It cannot describe a receiver parameter, a varargs method, or a type argument such
* as {@code Enumerable<@Nullable T>}.
*/
@Documented
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface Contract {
/** Returns the contract clauses.
*
* @return contract clauses, separated by {@code ;} */
String value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.collect;
package org.apache.calcite.annotations;

import org.checkerframework.checker.nullness.qual.*;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Guava has {@code Nullable} argument and return value by default.
* Checkerframework cna infer nullability from the actual generic types.
* States which nullable fields the method has assigned by the time it returns.
*
* @param <F> argument type
* @param <T> return type
* <p>Callers may read those fields without a null check afterwards. This is what an
* {@code init}-style method needs in order to satisfy the constructor's initialization check.
*/
public class Iterables {
public static <T> T[] toArray(Iterable<? extends @Nullable T> iterable, Class<T> type);
@Documented
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface EnsuresNonNull {
/** Returns the fields that are non-null once the method returns.
*
* @return field names, optionally qualified with {@code this.} */
String[] value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.calcite.annotations;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* States which nullable fields are non-null when the method returns a given boolean.
*
* <p>It turns a {@code hasFoo()} predicate into something a caller can rely on:
*
* <blockquote><pre>
* &#64;EnsuresNonNullIf(value = "hints", result = true)
* public boolean hasHints() { return hints != null &amp;&amp; !hints.isEmpty(); }
* </pre></blockquote>
*
* <p>The fields are guaranteed only on the branch that matches {@link #result()}.
*/
@Documented
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface EnsuresNonNullIf {
/** Returns the fields that are non-null when the method returns {@link #result()}.
*
* @return field names, optionally qualified with {@code this.} */
String[] value();

/** Returns the result for which the guarantee holds.
*
* @return the boolean result that makes the fields non-null */
boolean result();
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.base;
package org.apache.calcite.annotations;

import org.checkerframework.checker.nullness.qual.*;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Guava has {@code Nullable} argument and return value by default.
* Checkerframework cna infer nullability from the actual generic types.
* States that a field starts as null and is never assigned null again once it holds a value.
*
* @param <F> argument type
* @param <T> return type
* <p>The field is exempt from the constructor's initialization check, and a null check on it holds
* across an intervening method call. Every read still needs that check: the field is null until
* something assigns it.
*/
public interface Function<F, T> {
T apply(F input);
@Documented
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.FIELD, ElementType.TYPE_USE})
public @interface MonotonicNonNull {
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.collect;
package org.apache.calcite.annotations;

import org.checkerframework.checker.nullness.qual.*;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Guava has {@code Nullable} argument and return value by default.
* Checkerframework cna infer nullability from the actual generic types.
* States which nullable fields a caller must have checked before calling the method.
*
* @param <F> argument type
* @param <T> return type
* <p>Inside the method those fields count as non-null. The caller has to establish that, so this
* moves the check to the one place that can do it rather than repeating it in every method that
* reads the field.
*/
public abstract class Ordering<T> {
// The Checker Framework can infer nullness from generic itself, so we do not need
// "always nullable" from Guava.
public abstract int compare(T left, T right);
@Documented
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface RequiresNonNull {
/** Returns the fields that must be non-null on entry.
*
* @return field names, optionally qualified with {@code this.} */
String[] value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* Nullness annotations that JSpecify does not define.
*
* <p>JSpecify covers {@code @Nullable}, {@code @NonNull} and {@code @NullMarked} only. The
* annotations here cover what Calcite additionally needs to express: a method whose result is
* null exactly when an argument is null, a field that starts as null and stays non-null once
* assigned, and pre- and postconditions on fields.
*
* <p>A nullness checker recognises them by the last component of their name rather than by their
* package, so they need no dependency on the checker itself. NullAway is the checker Calcite
* runs; see {@code NullabilityUtil.findAnnotation} and
* {@code Nullness.isMonotonicNonNullAnnotation} in NullAway for the matching rules.
*/
@NullMarked
package org.apache.calcite.annotations;

import org.jspecify.annotations.NullMarked;
1 change: 1 addition & 0 deletions arrow/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import org.gradle.internal.os.OperatingSystem

dependencies {
api(project(":core"))
api("org.jspecify:jspecify")

implementation("com.google.guava:guava")
implementation("org.apache.arrow:arrow-memory-netty")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,16 @@
import org.apache.arrow.vector.types.TimeUnit;
import org.apache.arrow.vector.types.pojo.ArrowType;

import org.jspecify.annotations.Nullable;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
* Enumerator that reads from a collection of Arrow value-vectors.
*/
abstract class AbstractArrowEnumerator implements Enumerator<Object> {
abstract class AbstractArrowEnumerator implements Enumerator<@Nullable Object> {
protected final ArrowFileReader arrowFileReader;
protected final List<Integer> fields;
protected final List<ValueVector> valueVectors;
Expand Down Expand Up @@ -82,11 +84,11 @@ protected boolean loadNextNonEmptyArrowBatch() {
}
}

@Override public Object current() {
@Override public @Nullable Object current() {
if (fields.size() == 1) {
return getValue(this.valueVectors.get(0), currRowIndex);
}
Object[] current = new Object[valueVectors.size()];
final @Nullable Object[] current = new Object[valueVectors.size()];
for (int i = 0; i < valueVectors.size(); i++) {
ValueVector vector = this.valueVectors.get(i);
current[i] = getValue(vector, currRowIndex);
Expand All @@ -99,7 +101,7 @@ protected boolean loadNextNonEmptyArrowBatch() {
* <p>For {@link TimeStampVector}, converts the raw value to
* milliseconds since epoch, which is the representation used by
* Calcite's Enumerable runtime for TIMESTAMP types. */
protected static Object getValue(ValueVector vector, int index) {
protected static @Nullable Object getValue(ValueVector vector, int index) {
if (vector instanceof TimeStampVector) {
if (vector.isNull(index)) {
return null;
Expand Down
Loading
Loading