diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 8f9e0a3..70b6596 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -11,7 +11,7 @@ permissions:
id-token: write
jobs:
- release:
+ release-sql-to-logsql:
runs-on: ubuntu-latest
steps:
@@ -90,3 +90,50 @@ jobs:
args: release --clean --verbose --timeout 60m -f .goreleaser.yaml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ release-logsql-jdbc:
+ runs-on: ubuntu-latest
+ steps:
+
+ - name: Checkout
+ uses: actions/checkout@v5
+ with:
+ fetch-depth: '0'
+ fetch-tags: 'true'
+
+ - name: Install JDK and Maven
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: '11'
+ cache: 'maven'
+
+ - name: Fetch tags
+ run: |
+ if [[ "${{ github.ref_type }}" != "tag" ]]; then
+ git fetch --tags
+ else
+ echo "Skipping tag fetch - already on tag ${{ github.ref_name }}"
+ fi
+
+ - name: Update Configuration
+ working-directory: logsql-jdbc
+ run: |
+ if [[ "${{ github.ref_type }}" == "tag" ]]; then
+ TAG_VERSION=$(echo "${{ github.ref_name }}" | sed 's/^v//')
+ else
+ LATEST_TAG=$(git tag --sort=-version:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-.*)?$' | head -n 1)
+ [ -z "$LATEST_TAG" ] && { echo "No release tag found"; exit 1; }
+ TAG_VERSION=$(echo "$LATEST_TAG" | sed 's/^v//')
+ echo "Using latest tag: $LATEST_TAG"
+ fi
+ mvn versions:set versions:commit -DnewVersion="${TAG_VERSION}";
+
+ - name: Publish package
+ working-directory: logsql-jdbc
+ run: |
+ mvn -s ${{ github.workspace }}/.github/workflows/settings.xml -DskipTests --batch-mode deploy
+ rm -rf */target/logsql-jdbc-*.jar
+ env:
+ USERNAME: ${{ secrets.USERNAME }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/settings.xml b/.github/workflows/settings.xml
new file mode 100644
index 0000000..40ed124
--- /dev/null
+++ b/.github/workflows/settings.xml
@@ -0,0 +1,30 @@
+
+
+
+
+ github
+
+
+
+
+ github
+
+
+ github
+ https://maven.pkg.github.com/victoriametrics/sql-to-logsql
+
+ true
+
+
+
+
+
+
+
+
+ github
+ ${env.USERNAME}
+ ${env.GITHUB_TOKEN}
+
+
+
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 426b595..91dc5df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,4 +22,5 @@ dist-ssr
*.njsproj
*.sln
*.sw?
-/sql-to-logsql
\ No newline at end of file
+/sql-to-logsql
+/logsql-jdbc/target/
diff --git a/.goreleaser.yaml b/.goreleaser.yaml
index 39e81df..585d0b8 100644
--- a/.goreleaser.yaml
+++ b/.goreleaser.yaml
@@ -1,10 +1,6 @@
version: 2
project_name: sql-to-logsql
-#before:
-# hooks:
-# - make ui-build
-
builds:
- env:
- CGO_ENABLED=0
@@ -17,8 +13,6 @@ builds:
- arm64
- arm
- "386"
-# hooks:
-# pre: make ui-build
main: ./cmd/sql-to-logsql/main.go
binary: sql-to-logsql
ldflags:
diff --git a/Makefile b/Makefile
index 7c1c017..a74e31f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
UI_DIR=cmd/sql-to-logsql/web/ui
-.PHONY: ui-install ui-build build backend-build run test
+.PHONY: ui-install ui-build build backend-build run test all jdbc-build jdbc-test
ui-install:
cd $(UI_DIR) && npm install
@@ -26,3 +26,9 @@ lint:
bash ./scripts/lint-all.sh
all: test check lint build
+
+jdbc-build:
+ bash ./scripts/jdbc-build.sh
+
+jdbc-test:
+ bash ./scripts/jdbc-build.sh
diff --git a/logsql-jdbc/README.md b/logsql-jdbc/README.md
new file mode 100644
index 0000000..841fda1
--- /dev/null
+++ b/logsql-jdbc/README.md
@@ -0,0 +1,50 @@
+# LogSQL JDBC Driver
+
+This module provides a JDBC 4.0 compatible driver for the VictoriaLogs (via `sql-to-logsql` service).
+The driver interacts http(s) with sql-to-logsql API and exposes query results as regular JDBC result sets,
+making it possible to integrate VictoriaLogs with the broader JVM ecosystem (BI tools, JDBC-based frameworks, etc.).
+
+## Connection URL
+
+```
+jdbc:logsql://host[:port][/basePath]?property=value&...
+```
+
+Supported properties:
+
+- `scheme` – `http` (default) or `https`.
+- `endpoint` – optional VictoriaLogs endpoint URL override.
+- `bearerToken` – optional bearer token sent to the translation service.
+- `timeout` – request timeout in milliseconds (default 60000).
+- `verify` – when `false`, TLS certificate validation is disabled.
+- `header.` – additional HTTP headers to include with every request.
+
+Example:
+
+```
+jdbc:logsql://localhost:8080?scheme=https&endpoint=https%3A%2F%2Fvictorialogs.example.com&bearerToken=secret
+```
+
+Properties provided through `java.util.Properties` when creating the connection are merged with the URL query parameters (query parameters take precedence).
+
+## Building
+
+```
+mvn -DskipTests package
+```
+
+The standard artifact is placed in `target/logsql-jdbc-.jar`, and a fat jar with all dependencies is available as `target/logsql-jdbc--all.jar`.
+
+## Testing
+
+```
+mvn test
+```
+
+These integration tests connect to https://play-sql.victoriametrics.com. They will be marked as skipped automatically if the playground cannot be reached (for example, when outbound network access is disabled).
+
+## Notes
+
+- The driver performs a health check against `/healthz` when establishing a connection.
+- Result sets are fully buffered in memory to simplify cursor navigation and metadata reporting. Avoid query patterns that return unbounded result sets.
+- HTTPS certificate verification can be disabled for testing by setting `verify=false`, but this is not recommended for production use.
diff --git a/logsql-jdbc/pom.xml b/logsql-jdbc/pom.xml
new file mode 100644
index 0000000..d044a20
--- /dev/null
+++ b/logsql-jdbc/pom.xml
@@ -0,0 +1,150 @@
+
+ 4.0.0
+
+ com.victoriametrics
+ logsql-jdbc
+ 0.0.0-SNAPSHOT
+ jar
+ LogsQL JDBC Driver
+ JDBC driver for VictoriaLogs (via sql-to-logsql service)
+ https://github.com/VictoriaMetrics/sql-to-logsql/tree/main/logsql-jdbc
+
+
+ VictoriaMetrics Inc.
+ https://victoriametrics.com/
+
+
+
+
+ Apache License 2.0
+ https://www.apache.org/licenses/LICENSE-2.0
+
+
+
+
+
+ VictoriaMetrics
+ https://victoriametrics.com
+
+
+
+
+ Github
+ https://github.com/VictoriaMetrics/sql-to-logsql/issues
+
+
+
+ Github
+ https://github.com/VictoriaMetrics/sql-to-logsql/actions
+
+
+
+
+ github
+ GitHub VictoriaMetrics Apache Maven Packages
+ https://maven.pkg.github.com/VictoriaMetrics/sql-to-logsql
+
+
+
+
+ JDBC
+ 4.2
+ 11
+ 11
+ UTF-8
+ 2.17.2
+ 5.10.2
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.version}
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${junit.jupiter.version}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit.jupiter.version}
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.11.0
+
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.3.0
+
+
+
+ com.victoriametrics.logsql.jdbc
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.5.3
+
+
+ package
+
+ shade
+
+
+ false
+ true
+ all
+
+
+ *:*
+
+ META-INF/*.SF
+ META-INF/*.DSA
+ META-INF/*.RSA
+
+
+
+
+
+
+
+ com.victoriametrics.logsql.jdbc
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.2.5
+
+ false
+
+
+
+
+
diff --git a/logsql-jdbc/src/main/java/com/victoriametrics/logsql/jdbc/LogsqlConnection.java b/logsql-jdbc/src/main/java/com/victoriametrics/logsql/jdbc/LogsqlConnection.java
new file mode 100644
index 0000000..714869f
--- /dev/null
+++ b/logsql-jdbc/src/main/java/com/victoriametrics/logsql/jdbc/LogsqlConnection.java
@@ -0,0 +1,1000 @@
+package com.victoriametrics.logsql.jdbc;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.CallableStatement;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.NClob;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.SQLWarning;
+import java.sql.SQLXML;
+import java.sql.Savepoint;
+import java.sql.Statement;
+import java.sql.Struct;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Locale;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.regex.Pattern;
+
+final class LogsqlConnection implements Connection {
+
+ private static final TypeReference> MAP_TYPE =
+ new TypeReference>() {
+ };
+
+ private static final class TableEntry {
+ final String name;
+ final String type;
+ final String remarks;
+
+ TableEntry(String name, String type, String remarks) {
+ this.name = name;
+ this.type = type;
+ this.remarks = remarks;
+ }
+ }
+
+ private final LogsqlConnectionConfig config;
+ private final HttpClient httpClient;
+ private final ObjectMapper mapper = new ObjectMapper();
+ private final String baseUrl;
+ private final DatabaseMetaData metadata;
+ private boolean closed;
+ private boolean readOnly = true;
+ private boolean autoCommit = true;
+
+ LogsqlConnection(LogsqlConnectionConfig config) throws SQLException {
+ this.config = Objects.requireNonNull(config, "config");
+ this.httpClient = createHttpClient(config);
+ this.baseUrl = buildBaseUrl(config);
+ this.metadata = createMetadata();
+ performHealthCheck();
+ }
+
+ LogsqlQueryResult executeQuery(String sql, int maxRows) throws SQLException {
+ ensureOpen();
+ if (sql == null) {
+ throw new SQLException("SQL must not be null");
+ }
+ String payload;
+ try {
+ Map body = new LinkedHashMap<>();
+ body.put("sql", sql);
+ if (config.getEndpoint() != null) {
+ body.put("endpoint", config.getEndpoint());
+ }
+ if (config.getBearerToken() != null) {
+ body.put("bearerToken", config.getBearerToken());
+ }
+ payload = mapper.writeValueAsString(body);
+ } catch (JsonProcessingException e) {
+ throw new SQLException("Failed to serialize request payload", e);
+ }
+
+ HttpRequest request = baseRequestBuilder(buildUri("/api/v1/sql-to-logsql"))
+ .timeout(config.getTimeout())
+ .header("Content-Type", "application/json")
+ .POST(HttpRequest.BodyPublishers.ofString(payload))
+ .build();
+
+ HttpResponse response = send(request);
+ if (response.statusCode() >= 400) {
+ throw new SQLException("Query execution failed: " + extractErrorMessage(response));
+ }
+
+ Map resultMap = parseJson(response.body());
+ String translated = (String) resultMap.getOrDefault("logsql", null);
+ String data = (String) resultMap.getOrDefault("data", "");
+
+ List