mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-20 00:24:20 +01:00
Merge branch 'main' into feature/result-set-was-null
This commit is contained in:
1
bindings/java/.gitignore
vendored
1
bindings/java/.gitignore
vendored
@@ -41,3 +41,4 @@ bin/
|
||||
|
||||
### turso builds ###
|
||||
libs
|
||||
temp
|
||||
@@ -10,7 +10,7 @@ LINUX_X86_DIR := $(RELEASE_DIR)/linux_x86
|
||||
|
||||
.PHONY: libs macos_x86 macos_arm64 windows lint lint_apply test build_test
|
||||
|
||||
libs: macos_x86 macos_arm64 windows
|
||||
libs: macos_x86 macos_arm64 windows linux_x86
|
||||
|
||||
macos_x86:
|
||||
@echo "Building release version for macOS x86_64..."
|
||||
|
||||
@@ -8,27 +8,35 @@ plugins {
|
||||
application
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
signing
|
||||
id("net.ltgt.errorprone") version "3.1.0"
|
||||
|
||||
// If you're stuck on JRE 8, use id 'com.diffplug.spotless' version '6.13.0' or older.
|
||||
id("com.diffplug.spotless") version "6.13.0"
|
||||
}
|
||||
|
||||
group = properties["projectGroup"]!!
|
||||
version = properties["projectVersion"]!!
|
||||
// Apply publishing configuration
|
||||
apply(from = "gradle/publish.gradle.kts")
|
||||
|
||||
// Helper function to read properties with defaults
|
||||
fun prop(key: String, default: String? = null): String? =
|
||||
findProperty(key)?.toString() ?: default
|
||||
|
||||
group = prop("projectGroup") ?: error("projectGroup must be set in gradle.properties")
|
||||
version = prop("projectVersion") ?: error("projectVersion must be set in gradle.properties")
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
withJavadocJar()
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("mavenJava") {
|
||||
from(components["java"])
|
||||
groupId = "tech.turso"
|
||||
artifactId = "turso"
|
||||
version = "0.0.1-SNAPSHOT"
|
||||
// TODO: Add javadoc to required class and methods. After that, let's remove this settings
|
||||
tasks.withType<Javadoc> {
|
||||
options {
|
||||
(this as StandardJavadocDocletOptions).apply {
|
||||
addStringOption("Xdoclint:none", "-quiet")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,16 @@
|
||||
projectGroup="tech.turso"
|
||||
projectVersion=0.0.1-SNAPSHOT
|
||||
projectGroup=tech.turso
|
||||
projectVersion=0.0.1
|
||||
projectArtifactId=turso
|
||||
|
||||
# POM metadata
|
||||
pomName=Turso JDBC Driver
|
||||
pomDescription=Turso JDBC driver for Java applications
|
||||
pomUrl=https://github.com/tursodatabase/turso
|
||||
pomLicenseName=MIT License
|
||||
pomLicenseUrl=https://opensource.org/licenses/MIT
|
||||
pomDeveloperId=turso
|
||||
pomDeveloperName=Turso
|
||||
pomDeveloperEmail=penberg@iki.fi
|
||||
pomScmConnection=scm:git:git://github.com/tursodatabase/turso.git
|
||||
pomScmDeveloperConnection=scm:git:ssh://github.com:tursodatabase/turso.git
|
||||
pomScmUrl=https://github.com/tursodatabase/turso
|
||||
250
bindings/java/gradle/publish.gradle.kts
Normal file
250
bindings/java/gradle/publish.gradle.kts
Normal file
@@ -0,0 +1,250 @@
|
||||
import java.security.MessageDigest
|
||||
import org.gradle.api.publish.PublishingExtension
|
||||
import org.gradle.api.publish.maven.MavenPublication
|
||||
import org.gradle.plugins.signing.SigningExtension
|
||||
|
||||
// Helper function to read properties with defaults
|
||||
fun prop(key: String, default: String? = null): String? =
|
||||
project.findProperty(key)?.toString() ?: default
|
||||
|
||||
// Maven Publishing Configuration
|
||||
configure<PublishingExtension> {
|
||||
publications {
|
||||
create<MavenPublication>("mavenJava") {
|
||||
from(components["java"])
|
||||
groupId = prop("projectGroup")!!
|
||||
artifactId = prop("projectArtifactId")!!
|
||||
version = prop("projectVersion")!!
|
||||
|
||||
pom {
|
||||
name.set(prop("pomName"))
|
||||
description.set(prop("pomDescription"))
|
||||
url.set(prop("pomUrl"))
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name.set(prop("pomLicenseName"))
|
||||
url.set(prop("pomLicenseUrl"))
|
||||
}
|
||||
}
|
||||
|
||||
developers {
|
||||
developer {
|
||||
id.set(prop("pomDeveloperId"))
|
||||
name.set(prop("pomDeveloperName"))
|
||||
email.set(prop("pomDeveloperEmail"))
|
||||
}
|
||||
}
|
||||
|
||||
scm {
|
||||
connection.set(prop("pomScmConnection"))
|
||||
developerConnection.set(prop("pomScmDeveloperConnection"))
|
||||
url.set(prop("pomScmUrl"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GPG Signing Configuration
|
||||
configure<SigningExtension> {
|
||||
// Make signing required for publishing
|
||||
setRequired(true)
|
||||
|
||||
// For CI/GitHub Actions: use in-memory keys
|
||||
val signingKey = providers.environmentVariable("MAVEN_SIGNING_KEY").orNull
|
||||
val signingPassword = providers.environmentVariable("MAVEN_SIGNING_PASSPHRASE").orNull
|
||||
|
||||
if (signingKey != null && signingPassword != null) {
|
||||
// CI mode: use in-memory keys
|
||||
useInMemoryPgpKeys(signingKey, signingPassword)
|
||||
} else {
|
||||
// Local mode: use GPG command from system
|
||||
useGpgCmd()
|
||||
}
|
||||
|
||||
sign(the<PublishingExtension>().publications["mavenJava"])
|
||||
}
|
||||
|
||||
// Helper task to generate checksums
|
||||
val generateChecksums by tasks.registering {
|
||||
dependsOn("jar", "sourcesJar", "javadocJar", "generatePomFileForMavenJavaPublication")
|
||||
|
||||
val checksumDir = layout.buildDirectory.dir("checksums")
|
||||
|
||||
doLast {
|
||||
val files = listOf(
|
||||
tasks.named("jar").get().outputs.files.singleFile,
|
||||
tasks.named("sourcesJar").get().outputs.files.singleFile,
|
||||
tasks.named("javadocJar").get().outputs.files.singleFile,
|
||||
layout.buildDirectory.file("publications/mavenJava/pom-default.xml").get().asFile
|
||||
)
|
||||
|
||||
checksumDir.get().asFile.mkdirs()
|
||||
|
||||
files.forEach { file ->
|
||||
if (file.exists()) {
|
||||
// MD5
|
||||
val md5 = MessageDigest.getInstance("MD5")
|
||||
.digest(file.readBytes())
|
||||
.joinToString("") { "%02x".format(it) }
|
||||
file("${file.absolutePath}.md5").writeText(md5)
|
||||
|
||||
// SHA1
|
||||
val sha1 = MessageDigest.getInstance("SHA-1")
|
||||
.digest(file.readBytes())
|
||||
.joinToString("") { "%02x".format(it) }
|
||||
file("${file.absolutePath}.sha1").writeText(sha1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Task to create a bundle zip for Maven Central Portal
|
||||
val createMavenCentralBundle by tasks.registering(Zip::class) {
|
||||
group = "publishing"
|
||||
description = "Creates a bundle zip for Maven Central Portal upload"
|
||||
|
||||
dependsOn("generatePomFileForMavenJavaPublication", "jar", "sourcesJar", "javadocJar", "signMavenJavaPublication", generateChecksums)
|
||||
|
||||
// Ensure signing happens before bundle creation
|
||||
mustRunAfter("signMavenJavaPublication")
|
||||
|
||||
val groupId = prop("projectGroup")!!.replace(".", "/")
|
||||
val artifactId = prop("projectArtifactId")!!
|
||||
val projectVer = project.version.toString()
|
||||
|
||||
// Validate version is not SNAPSHOT for Maven Central
|
||||
doFirst {
|
||||
if (projectVer.contains("SNAPSHOT")) {
|
||||
throw GradleException(
|
||||
"Cannot publish SNAPSHOT version to Maven Central. " +
|
||||
"Please change projectVersion in gradle.properties to a release version (e.g., 0.0.1)"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
archiveFileName.set("$artifactId-$projectVer-bundle.zip")
|
||||
destinationDirectory.set(layout.buildDirectory.dir("maven-central"))
|
||||
|
||||
// Maven Central expects files in groupId/artifactId/version/ structure
|
||||
val basePath = "$groupId/$artifactId/$projectVer"
|
||||
|
||||
// Main JAR + checksums + signature
|
||||
from(tasks.named("jar").get().outputs.files) {
|
||||
into(basePath)
|
||||
rename { "$artifactId-$projectVer.jar" }
|
||||
}
|
||||
from(tasks.named("jar").get().outputs.files.singleFile.absolutePath + ".md5") {
|
||||
into(basePath)
|
||||
rename { "$artifactId-$projectVer.jar.md5" }
|
||||
}
|
||||
from(tasks.named("jar").get().outputs.files.singleFile.absolutePath + ".sha1") {
|
||||
into(basePath)
|
||||
rename { "$artifactId-$projectVer.jar.sha1" }
|
||||
}
|
||||
|
||||
// Sources JAR + checksums + signature
|
||||
from(tasks.named("sourcesJar").get().outputs.files) {
|
||||
into(basePath)
|
||||
rename { "$artifactId-$projectVer-sources.jar" }
|
||||
}
|
||||
from(tasks.named("sourcesJar").get().outputs.files.singleFile.absolutePath + ".md5") {
|
||||
into(basePath)
|
||||
rename { "$artifactId-$projectVer-sources.jar.md5" }
|
||||
}
|
||||
from(tasks.named("sourcesJar").get().outputs.files.singleFile.absolutePath + ".sha1") {
|
||||
into(basePath)
|
||||
rename { "$artifactId-$projectVer-sources.jar.sha1" }
|
||||
}
|
||||
|
||||
// Javadoc JAR + checksums + signature
|
||||
from(tasks.named("javadocJar").get().outputs.files) {
|
||||
into(basePath)
|
||||
rename { "$artifactId-$projectVer-javadoc.jar" }
|
||||
}
|
||||
from(tasks.named("javadocJar").get().outputs.files.singleFile.absolutePath + ".md5") {
|
||||
into(basePath)
|
||||
rename { "$artifactId-$projectVer-javadoc.jar.md5" }
|
||||
}
|
||||
from(tasks.named("javadocJar").get().outputs.files.singleFile.absolutePath + ".sha1") {
|
||||
into(basePath)
|
||||
rename { "$artifactId-$projectVer-javadoc.jar.sha1" }
|
||||
}
|
||||
|
||||
// POM + checksums + signature
|
||||
from(layout.buildDirectory.file("publications/mavenJava/pom-default.xml")) {
|
||||
into(basePath)
|
||||
rename { "$artifactId-$projectVer.pom" }
|
||||
}
|
||||
from(layout.buildDirectory.file("publications/mavenJava/pom-default.xml").get().asFile.absolutePath + ".md5") {
|
||||
into(basePath)
|
||||
rename { "$artifactId-$projectVer.pom.md5" }
|
||||
}
|
||||
from(layout.buildDirectory.file("publications/mavenJava/pom-default.xml").get().asFile.absolutePath + ".sha1") {
|
||||
into(basePath)
|
||||
rename { "$artifactId-$projectVer.pom.sha1" }
|
||||
}
|
||||
|
||||
// Signature files - get them from the signing task outputs
|
||||
doFirst {
|
||||
val signingTask = tasks.named("signMavenJavaPublication").get()
|
||||
logger.lifecycle("Signing task outputs: ${signingTask.outputs.files.files}")
|
||||
}
|
||||
|
||||
// Include signature files generated by the signing plugin
|
||||
from(tasks.named("signMavenJavaPublication").get().outputs.files) {
|
||||
into(basePath)
|
||||
include("*.jar.asc", "pom-default.xml.asc")
|
||||
exclude("module.json.asc") // Exclude gradle module metadata signature
|
||||
rename { name ->
|
||||
// Only rename the POM signature file
|
||||
// JAR signatures are already correctly named by the signing plugin
|
||||
if (name == "pom-default.xml.asc") {
|
||||
"$artifactId-$projectVer.pom.asc"
|
||||
} else {
|
||||
name // Keep original name (already correct)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Task to upload bundle to Maven Central Portal
|
||||
tasks.register("publishToMavenCentral") {
|
||||
group = "publishing"
|
||||
description = "Publishes artifacts to Maven Central Portal"
|
||||
|
||||
// Run publish first to generate signatures, then create bundle
|
||||
dependsOn("publish")
|
||||
dependsOn(createMavenCentralBundle)
|
||||
|
||||
// Make sure bundle creation happens after publish
|
||||
createMavenCentralBundle.get().mustRunAfter("publish")
|
||||
|
||||
doLast {
|
||||
val username = providers.environmentVariable("MAVEN_UPLOAD_USERNAME").orNull
|
||||
val password = providers.environmentVariable("MAVEN_UPLOAD_PASSWORD").orNull
|
||||
val bundleFile = createMavenCentralBundle.get().archiveFile.get().asFile
|
||||
|
||||
require(username != null) { "MAVEN_UPLOAD_USERNAME environment variable must be set" }
|
||||
require(password != null) { "MAVEN_UPLOAD_PASSWORD environment variable must be set" }
|
||||
require(bundleFile.exists()) { "Bundle file does not exist: ${bundleFile.absolutePath}" }
|
||||
|
||||
logger.lifecycle("Uploading bundle to Maven Central Portal...")
|
||||
logger.lifecycle("Bundle: ${bundleFile.absolutePath}")
|
||||
logger.lifecycle("Size: ${bundleFile.length() / 1024} KB")
|
||||
|
||||
// Use curl for uploading (simple and available on most systems)
|
||||
exec {
|
||||
commandLine(
|
||||
"curl",
|
||||
"-X", "POST",
|
||||
"-u", "$username:$password",
|
||||
"-F", "bundle=@${bundleFile.absolutePath}",
|
||||
"https://central.sonatype.com/api/v1/publisher/upload?name=${bundleFile.name}&publishingType=AUTOMATIC"
|
||||
)
|
||||
}
|
||||
|
||||
logger.lifecycle("Upload completed. Check https://central.sonatype.com/publishing for status.")
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -29,11 +29,17 @@ 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;
|
||||
private boolean wasNull = false;
|
||||
|
||||
/**
|
||||
* Creates a new JDBC4ResultSet.
|
||||
*
|
||||
* @param resultSet the underlying Turso result set
|
||||
*/
|
||||
public JDBC4ResultSet(TursoResultSet resultSet) {
|
||||
this.resultSet = resultSet;
|
||||
}
|
||||
@@ -1361,8 +1367,19 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData {
|
||||
- localCal.getTimeZone().getOffset(timeMillis);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user