Merge 'bindings/java: Add support for publishing to Maven Central' from Kim Seon Woo

## Purpose
- Deploy `tech.turso:turso:<version>` to maven central so that users can
easily use java bindings
  - For example :
https://repo1.maven.org/maven2/io/github/seonwkim/turso/0.0.1/
## Requirements
- [x] Add the following github secrets.
  - [x] MAVEN_CENTRAL_USERNAME
  - [x] MAVEN_CENTRAL_PASSWORD
  - [x] GPG_PRIVATE_KEY
  - [x] GPG_PASSPHRASE
- [ ] Namespace `tech.turso` must be registered at maven central
- [ ] GPG key registration to key servers
- Notes
  - Retrieve MAVEN_CENTRAL_USERNAME and MAVEN_CENTRAL_PASSWORD from
[maven central](https://central.sonatype.com/usertoken)
  - GPG keys should be registered. You should distribute your keys to
designated(maven central supported) servers
    -  Refer to [GPG key related docs](https://central.sonatype.org/publ
ish/requirements/gpg/#distributing-your-public-key)
    - Btw, I used `keyserver.ubuntu.com` key server while testing
### [Maven Central Username &
Password](https://central.sonatype.com/usertoken)
<img width="2878" height="1338" alt="image"
src="https://github.com/user-
attachments/assets/03e6f967-a7f6-46b8-aef5-d15772bd9eea" />
### [Maven Central
Namespace](https://central.sonatype.com/publishing/namespaces)
<img width="1424" height="456" alt="image" src="https://github.com/user-
attachments/assets/8c0f4f17-bf5a-4c6a-bc47-748d86cd1f1a" />
## Future Works
- Currently, we depend on gradle.properties to determine the version of
our dependency and it's cumbersome to always change the version
manually. Let's find a better solution.

Closes #3624
This commit is contained in:
Pekka Enberg
2025-10-10 13:12:01 +03:00
committed by GitHub
16 changed files with 541 additions and 16 deletions

View File

@@ -10,6 +10,7 @@ import tech.turso.jdbc4.JDBC4Connection;
import tech.turso.utils.Logger;
import tech.turso.utils.LoggerFactory;
/** Turso JDBC driver implementation. */
public final class JDBC implements Driver {
private static final Logger logger = LoggerFactory.getLogger(JDBC.class);
@@ -24,6 +25,14 @@ public final class JDBC implements Driver {
}
}
/**
* Creates a new Turso JDBC connection.
*
* @param url the database URL
* @param properties connection properties
* @return a new connection instance, or null if the URL is not valid
* @throws SQLException if a database access error occurs
*/
@Nullable
public static JDBC4Connection createConnection(String url, Properties properties)
throws SQLException {

View File

@@ -23,7 +23,7 @@ public final class TursoDBFactory {
* @param url the URL of the database
* @param filePath the path to the database file
* @param properties additional properties for the database connection
* @return an instance of {@link tursoDB}
* @return an instance of {@link TursoDB}
* @throws SQLException if there is an error opening the connection
* @throws IllegalArgumentException if the fileName is empty
*/

View File

@@ -57,7 +57,7 @@ public final class TursoResultSet {
}
/**
* Moves the cursor forward one row from its current position. A {@link tursoResultSet} cursor is
* Moves the cursor forward one row from its current position. A {@link TursoResultSet} cursor is
* initially positioned before the first fow; the first call to the method <code>next</code> makes
* the first row the current row; the second call makes the second row the current row, and so on.
* When a call to the <code>next</code> method returns <code>false</code>, the cursor is
@@ -65,6 +65,9 @@ public final class TursoResultSet {
*
* <p>Note that turso only supports <code>ResultSet.TYPE_FORWARD_ONLY</code>, which means that the
* cursor can only move forward.
*
* @return true if the new current row is valid; false if there are no more rows
* @throws SQLException if a database access error occurs
*/
public boolean next() throws SQLException {
if (!open) {

View File

@@ -91,8 +91,8 @@ public final class TursoStatement {
private native void _close(long statementPointer);
/**
* Initializes the column metadata, such as the names of the columns. Since {@link tursoStatement}
* can only have a single {@link tursoResultSet}, it is appropriate to place the initialization of
* Initializes the column metadata, such as the names of the columns. Since {@link TursoStatement}
* can only have a single {@link TursoResultSet}, it is appropriate to place the initialization of
* column metadata here.
*
* @throws SQLException if a database access error occurs while retrieving column names

View File

@@ -9,20 +9,43 @@ import tech.turso.annotations.SkipNullableCheck;
import tech.turso.core.TursoConnection;
import tech.turso.core.TursoStatement;
/** JDBC 4 Connection implementation for Turso databases. */
public final class JDBC4Connection implements Connection {
private final TursoConnection connection;
private Map<String, Class<?>> typeMap = new HashMap<>();
/**
* Creates a new JDBC4 connection.
*
* @param url the database URL
* @param filePath the database file path
* @throws SQLException if a database access error occurs
*/
public JDBC4Connection(String url, String filePath) throws SQLException {
this.connection = new TursoConnection(url, filePath);
}
/**
* Creates a new JDBC4 connection with properties.
*
* @param url the database URL
* @param filePath the database file path
* @param properties connection properties
* @throws SQLException if a database access error occurs
*/
public JDBC4Connection(String url, String filePath, Properties properties) throws SQLException {
this.connection = new TursoConnection(url, filePath, properties);
}
/**
* Prepares a SQL statement for execution.
*
* @param sql the SQL statement to prepare
* @return the prepared statement
* @throws SQLException if a database access error occurs
*/
public TursoStatement prepare(String sql) throws SQLException {
final TursoStatement statement = connection.prepare(sql);
statement.initializeColumnMetadata();
@@ -357,6 +380,11 @@ public final class JDBC4Connection implements Connection {
return false;
}
/**
* Sets the busy timeout for the connection.
*
* @param busyTimeout the timeout in milliseconds
*/
public void setBusyTimeout(int busyTimeout) {
// TODO: add support for busy timeout
}
@@ -367,10 +395,20 @@ public final class JDBC4Connection implements Connection {
return 0;
}
/**
* Gets the database URL.
*
* @return the database URL
*/
public String getUrl() {
return this.connection.getUrl();
}
/**
* Checks if the connection is open.
*
* @throws SQLException if the connection is closed
*/
public void checkOpen() throws SQLException {
connection.checkOpen();
}

View File

@@ -13,6 +13,7 @@ import tech.turso.core.TursoPropertiesHolder;
import tech.turso.utils.Logger;
import tech.turso.utils.LoggerFactory;
/** JDBC 4 DatabaseMetaData implementation for Turso databases. */
public final class JDBC4DatabaseMetaData implements DatabaseMetaData {
private static final Logger logger = LoggerFactory.getLogger(JDBC4DatabaseMetaData.class);
@@ -51,6 +52,11 @@ public final class JDBC4DatabaseMetaData implements DatabaseMetaData {
@Nullable private PreparedStatement getColumnPrivileges = null;
/**
* Creates a new JDBC4DatabaseMetaData instance.
*
* @param connection the database connection
*/
public JDBC4DatabaseMetaData(JDBC4Connection connection) {
this.connection = connection;
}

View File

@@ -26,11 +26,19 @@ import java.util.Calendar;
import tech.turso.annotations.SkipNullableCheck;
import tech.turso.core.TursoResultSet;
/** JDBC 4 PreparedStatement implementation for Turso databases. */
public final class JDBC4PreparedStatement extends JDBC4Statement implements PreparedStatement {
private final String sql;
private final JDBC4ResultSet resultSet;
/**
* Creates a new JDBC4PreparedStatement.
*
* @param connection the database connection
* @param sql the SQL statement to prepare
* @throws SQLException if a database access error occurs
*/
public JDBC4PreparedStatement(JDBC4Connection connection, String sql) throws SQLException {
super(connection);
this.sql = sql;

View File

@@ -28,10 +28,16 @@ import tech.turso.annotations.Nullable;
import tech.turso.annotations.SkipNullableCheck;
import tech.turso.core.TursoResultSet;
/** JDBC 4 ResultSet implementation for Turso databases. */
public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData {
private final TursoResultSet resultSet;
/**
* Creates a new JDBC4ResultSet.
*
* @param resultSet the underlying Turso result set
*/
public JDBC4ResultSet(TursoResultSet resultSet) {
this.resultSet = resultSet;
}
@@ -1328,8 +1334,19 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData {
throw new UnsupportedOperationException("not implemented");
}
/**
* Functional interface for result set value suppliers.
*
* @param <T> the type of value to supply
*/
@FunctionalInterface
public interface ResultSetSupplier<T> {
/**
* Gets a result from the result set.
*
* @return the result value
* @throws Exception if an error occurs
*/
T get() throws Exception;
}

View File

@@ -17,6 +17,7 @@ import tech.turso.annotations.SkipNullableCheck;
import tech.turso.core.TursoResultSet;
import tech.turso.core.TursoStatement;
/** JDBC 4 Statement implementation for Turso databases. */
public class JDBC4Statement implements Statement {
private static final Pattern BATCH_COMPATIBLE_PATTERN =
@@ -35,7 +36,10 @@ public class JDBC4Statement implements Statement {
private final JDBC4Connection connection;
/** The underlying Turso statement. */
@Nullable protected TursoStatement statement = null;
/** The number of rows affected by the last update operation. */
protected long updateCount;
// Because JDBC4Statement has different life cycle in compared to tursoStatement, let's use this
@@ -475,8 +479,19 @@ public class JDBC4Statement implements Statement {
}
}
/**
* Functional interface for SQL callable operations.
*
* @param <T> the return type
*/
@FunctionalInterface
protected interface SQLCallable<T> {
/**
* Executes the SQL operation.
*
* @return the result of the operation
* @throws SQLException if a database access error occurs
*/
T call() throws SQLException;
}

View File

@@ -3,7 +3,14 @@ package tech.turso.utils;
import java.nio.charset.StandardCharsets;
import tech.turso.annotations.Nullable;
/** Utility class for converting between byte arrays and strings using UTF-8 encoding. */
public final class ByteArrayUtils {
/**
* Converts a UTF-8 encoded byte array to a string.
*
* @param buffer the byte array to convert, may be null
* @return the string representation, or null if the input is null
*/
@Nullable
public static String utf8ByteBufferToString(@Nullable byte[] buffer) {
if (buffer == null) {
@@ -13,6 +20,12 @@ public final class ByteArrayUtils {
return new String(buffer, StandardCharsets.UTF_8);
}
/**
* Converts a string to a UTF-8 encoded byte array.
*
* @param str the string to convert, may be null
* @return the byte array representation, or null if the input is null
*/
@Nullable
public static byte[] stringToUtf8ByteArray(@Nullable String str) {
if (str == null) {