diff --git a/bindings/java/build.gradle.kts b/bindings/java/build.gradle.kts index 286bf5c1b..9936bec60 100644 --- a/bindings/java/build.gradle.kts +++ b/bindings/java/build.gradle.kts @@ -50,7 +50,10 @@ tasks.test { tasks.withType { options.errorprone { + // Let's select which checks to perform. NullAway is enough for now. + disableAllChecks = true check("NullAway", CheckSeverity.ERROR) + option("NullAway:AnnotatedPackages", "org.github.tursodatabase") option( "NullAway:CustomNullableAnnotations", diff --git a/bindings/java/src/main/java/org/github/tursodatabase/JDBC.java b/bindings/java/src/main/java/org/github/tursodatabase/JDBC.java index 87200ff5c..10eeb2687 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/JDBC.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/JDBC.java @@ -1,8 +1,11 @@ package org.github.tursodatabase; +import org.github.tursodatabase.annotations.Nullable; +import org.github.tursodatabase.annotations.SkipNullableCheck; import org.github.tursodatabase.jdbc4.JDBC4Connection; import java.sql.*; +import java.util.Locale; import java.util.Properties; import java.util.logging.Logger; @@ -17,6 +20,7 @@ public class JDBC implements Driver { } } + @Nullable public static LimboConnection createConnection(String url, Properties properties) throws SQLException { if (!isValidURL(url)) return null; @@ -25,13 +29,14 @@ public class JDBC implements Driver { } private static boolean isValidURL(String url) { - return url != null && url.toLowerCase().startsWith(VALID_URL_PREFIX); + return url != null && url.toLowerCase(Locale.ROOT).startsWith(VALID_URL_PREFIX); } private static String extractAddress(String url) { return url.substring(VALID_URL_PREFIX.length()); } + @Nullable @Override public Connection connect(String url, Properties info) throws SQLException { return createConnection(url, info); @@ -65,6 +70,7 @@ public class JDBC implements Driver { } @Override + @SkipNullableCheck public Logger getParentLogger() throws SQLFeatureNotSupportedException { // TODO return null; diff --git a/bindings/java/src/main/java/org/github/tursodatabase/LimboDataSource.java b/bindings/java/src/main/java/org/github/tursodatabase/LimboDataSource.java index 12a53c303..97eec44d2 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/LimboDataSource.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/LimboDataSource.java @@ -1,5 +1,7 @@ package org.github.tursodatabase; +import org.github.tursodatabase.annotations.Nullable; + import javax.sql.DataSource; import java.io.PrintWriter; import java.sql.Connection; @@ -27,12 +29,14 @@ public class LimboDataSource implements DataSource { } @Override + @Nullable public Connection getConnection() throws SQLException { return getConnection(null, null); } @Override - public Connection getConnection(String username, String password) throws SQLException { + @Nullable + public Connection getConnection(@Nullable String username, @Nullable String password) throws SQLException { Properties properties = limboConfig.toProperties(); if (username != null) properties.put("user", username); if (password != null) properties.put("pass", password); @@ -40,6 +44,7 @@ public class LimboDataSource implements DataSource { } @Override + @Nullable public PrintWriter getLogWriter() throws SQLException { // TODO return null; @@ -62,12 +67,14 @@ public class LimboDataSource implements DataSource { } @Override + @Nullable public Logger getParentLogger() throws SQLFeatureNotSupportedException { // TODO return null; } @Override + @Nullable public T unwrap(Class iface) throws SQLException { // TODO return null; diff --git a/bindings/java/src/main/java/org/github/tursodatabase/annotations/Nullable.java b/bindings/java/src/main/java/org/github/tursodatabase/annotations/Nullable.java new file mode 100644 index 000000000..88451f8b4 --- /dev/null +++ b/bindings/java/src/main/java/org/github/tursodatabase/annotations/Nullable.java @@ -0,0 +1,18 @@ +package org.github.tursodatabase.annotations; + + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to mark nullable types. + *

+ * This annotation is used to indicate that a method, field, or parameter can be null. + * It helps in identifying potential nullability issues and improving code quality. + */ +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) +public @interface Nullable { +} diff --git a/bindings/java/src/main/java/org/github/tursodatabase/annotations/SkipNullableCheck.java b/bindings/java/src/main/java/org/github/tursodatabase/annotations/SkipNullableCheck.java new file mode 100644 index 000000000..69214e7c4 --- /dev/null +++ b/bindings/java/src/main/java/org/github/tursodatabase/annotations/SkipNullableCheck.java @@ -0,0 +1,18 @@ +package org.github.tursodatabase.annotations; + + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marker annotation to skip nullable checks. + *

+ * This annotation is used to mark methods, fields, or parameters that should be excluded from nullable checks. + * It is typically applied to code that is still under development or requires special handling. + */ +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) +public @interface SkipNullableCheck { +} diff --git a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java index accbad76b..42d2e09d3 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java @@ -4,6 +4,7 @@ package org.github.tursodatabase.core; import org.github.tursodatabase.LimboErrorCode; import org.github.tursodatabase.NativeInvocation; import org.github.tursodatabase.VisibleForTesting; +import org.github.tursodatabase.annotations.Nullable; import org.github.tursodatabase.exceptions.LimboException; import java.nio.charset.StandardCharsets; @@ -82,9 +83,15 @@ public final class LimboDB extends AbstractDB { @Override protected void open0(String fileName, int openFlags) throws SQLException { if (isOpen) { - throwLimboException(LimboErrorCode.UNKNOWN_ERROR.code, "Already opened"); + throw buildLimboException(LimboErrorCode.ETC.code, "Already opened"); } - dbPtr = openUtf8(stringToUtf8ByteArray(fileName), openFlags); + + byte[] fileNameBytes = stringToUtf8ByteArray(fileName); + if (fileNameBytes == null) { + throw buildLimboException(LimboErrorCode.ETC.code, "File name cannot be converted to byteArray. File name: " + fileName); + } + + dbPtr = openUtf8(fileNameBytes, openFlags); isOpen = true; } @@ -114,7 +121,7 @@ public final class LimboDB extends AbstractDB { @NativeInvocation private void throwLimboException(int errorCode, byte[] errorMessageBytes) throws SQLException { String errorMessage = utf8ByteBufferToString(errorMessageBytes); - throwLimboException(errorCode, errorMessage); + throw buildLimboException(errorCode, errorMessage); } /** @@ -123,7 +130,7 @@ public final class LimboDB extends AbstractDB { * @param errorCode Error code. * @param errorMessage Error message. */ - public void throwLimboException(int errorCode, String errorMessage) throws SQLException { + public LimboException buildLimboException(int errorCode, @Nullable String errorMessage) throws SQLException { LimboErrorCode code = LimboErrorCode.getErrorCode(errorCode); String msg; if (code == LimboErrorCode.UNKNOWN_ERROR) { @@ -132,10 +139,11 @@ public final class LimboDB extends AbstractDB { msg = String.format("%s (%s)", code, errorMessage); } - throw new LimboException(msg, code); + return new LimboException(msg, code); } - private static String utf8ByteBufferToString(byte[] buffer) { + @Nullable + private static String utf8ByteBufferToString(@Nullable byte[] buffer) { if (buffer == null) { return null; } @@ -143,7 +151,8 @@ public final class LimboDB extends AbstractDB { return new String(buffer, StandardCharsets.UTF_8); } - private static byte[] stringToUtf8ByteArray(String str) { + @Nullable + private static byte[] stringToUtf8ByteArray(@Nullable String str) { if (str == null) { return null; } diff --git a/bindings/java/src/main/java/org/github/tursodatabase/jdbc4/JDBC4Connection.java b/bindings/java/src/main/java/org/github/tursodatabase/jdbc4/JDBC4Connection.java index 04c83b6b9..6ffb41e3e 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/jdbc4/JDBC4Connection.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/jdbc4/JDBC4Connection.java @@ -1,6 +1,7 @@ package org.github.tursodatabase.jdbc4; import org.github.tursodatabase.LimboConnection; +import org.github.tursodatabase.annotations.SkipNullableCheck; import java.sql.*; import java.util.Map; @@ -14,24 +15,28 @@ public class JDBC4Connection extends LimboConnection { } @Override + @SkipNullableCheck public Statement createStatement() throws SQLException { // TODO return null; } @Override + @SkipNullableCheck public PreparedStatement prepareStatement(String sql) throws SQLException { // TODO return null; } @Override + @SkipNullableCheck public CallableStatement prepareCall(String sql) throws SQLException { // TODO return null; } @Override + @SkipNullableCheck public String nativeSQL(String sql) throws SQLException { // TODO return ""; @@ -70,6 +75,7 @@ public class JDBC4Connection extends LimboConnection { } @Override + @SkipNullableCheck public DatabaseMetaData getMetaData() throws SQLException { // TODO return null; @@ -109,6 +115,7 @@ public class JDBC4Connection extends LimboConnection { } @Override + @SkipNullableCheck public SQLWarning getWarnings() throws SQLException { // TODO return null; @@ -120,18 +127,21 @@ public class JDBC4Connection extends LimboConnection { } @Override + @SkipNullableCheck public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { // TODO return null; } @Override + @SkipNullableCheck public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { // TODO return null; } @Override + @SkipNullableCheck public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { // TODO return null; @@ -159,12 +169,14 @@ public class JDBC4Connection extends LimboConnection { } @Override + @SkipNullableCheck public Savepoint setSavepoint() throws SQLException { // TODO return null; } @Override + @SkipNullableCheck public Savepoint setSavepoint(String name) throws SQLException { // TODO return null; @@ -181,60 +193,70 @@ public class JDBC4Connection extends LimboConnection { } @Override + @SkipNullableCheck public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { // TODO return null; } @Override + @SkipNullableCheck public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { // TODO return null; } @Override + @SkipNullableCheck public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { // TODO return null; } @Override + @SkipNullableCheck public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { // TODO return null; } @Override + @SkipNullableCheck public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { // TODO return null; } @Override + @SkipNullableCheck public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { // TODO return null; } @Override + @SkipNullableCheck public Clob createClob() throws SQLException { // TODO return null; } @Override + @SkipNullableCheck public Blob createBlob() throws SQLException { // TODO return null; } @Override + @SkipNullableCheck public NClob createNClob() throws SQLException { // TODO return null; } @Override + @SkipNullableCheck public SQLXML createSQLXML() throws SQLException { // TODO return null; @@ -263,18 +285,21 @@ public class JDBC4Connection extends LimboConnection { } @Override + @SkipNullableCheck public Properties getClientInfo() throws SQLException { // TODO return null; } @Override + @SkipNullableCheck public Array createArrayOf(String typeName, Object[] elements) throws SQLException { // TODO return null; } @Override + @SkipNullableCheck public Struct createStruct(String typeName, Object[] attributes) throws SQLException { // TODO return null; @@ -286,6 +311,7 @@ public class JDBC4Connection extends LimboConnection { } @Override + @SkipNullableCheck public String getSchema() throws SQLException { // TODO return ""; @@ -308,8 +334,8 @@ public class JDBC4Connection extends LimboConnection { } @Override + @SkipNullableCheck public T unwrap(Class iface) throws SQLException { - // TODO return null; }