duckdb / duckdb/duckdb-java

Native SIGSEGV in DuckDB JDBC when repeatedly executing a split query with scalar subqueries and LIMIT 0

Open
#872 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
C++
Stars
127
Forks
80
Avg merge
13h 49m
Merged PRs (30d)
48

Description

### What happens?

Repeatedly executing a rewritten (“split”) query that reconstructs a source table via scalar subqueries and ends with `LIMIT 0` causes a native `SIGSEGV` crash in DuckDB JDBC after a small number of iterations. The crash occurs in `org.duckdb.DuckDBNative.duckdb_jdbc_execute_pending`, and the JVM is aborted with exit code 134. The equivalent single-table (`source`) query executes successfully and returns an empty result set.

### To Reproduce

### Driver

- groupId: `org.duckdb`
- artifactId: `duckdb_jdbc`
- version: `1.5.5.1`
- SHA-256: `22343dd258db1b0b51d37afc776c8dff5b19282829fa5b47f7d5d6fe02b3377a`

Maven dependency:

```xml

org.duckdb
duckdb_jdbc
1.5.5.1

```

Or download directly:

```bash
mvn dependency:get \
-Dartifact=org.duckdb:duckdb_jdbc:1.5.5.1
```

### Reproducer

Save the following as `repro/DuckDBVpCrash3281876.java`:

```java
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

/**
* Standalone reproducer extracted from logs/duckdb/database0-cur.log for
* hs_err_pid3281876.log. Run with DuckDB JDBC 1.5.5.x on the class path.
*/
public final class DuckDBVpCrash3281876 {

private static final String SOURCE_QUERY = """
SELECT vp_database0_16_source.c7
FROM "vp_database0_16_source" "vp_database0_16_source"
WHERE "c7" IS NOT NULL
ORDER BY vp_database0_16_source.vp_rowid
LIMIT 0
""";

private static final String SPLIT_QUERY = """
SELECT vp_database0_16_source.c7
FROM (
SELECT l."vp_rowid" AS "vp_rowid",
(SELECT r."c0" FROM "vp_database0_16_r" r
WHERE r."vp_rowid" = l."vp_rowid") AS "c0",
(SELECT r."c1" FROM "vp_database0_16_r" r
WHERE r."vp_rowid" = l."vp_rowid") AS "c1",
(SELECT r."c2" FROM "vp_database0_16_r" r
WHERE r."vp_rowid" = l."vp_rowid") AS "c2",
(SELECT r."c3" FROM "vp_database0_16_r" r
WHERE r."vp_rowid" = l."vp_rowid") AS "c3",
(SELECT r."c4" FROM "vp_database0_16_r" r
WHERE r."vp_rowid" = l."vp_rowid") AS "c4",
(SELECT r."c5" FROM "vp_database0_16_r" r
WHERE r."vp_rowid" = l."vp_rowid") AS "c5",
l."c6" AS "c6",
(SELECT r."c7" FROM "vp_database0_16_r" r
WHERE r."vp_rowid" = l."vp_rowid") AS "c7",
(SELECT r."c8" FROM "vp_database0_16_r" r
WHERE r."vp_rowid" = l."vp_rowid") AS "c8",
(SELECT r."c9" FROM "vp_database0_16_r" r
WHERE r."vp_rowid" = l."vp_rowid") AS "c9",
(SELECT r."c10" FROM "vp_database0_16_r" r
WHERE r."vp_rowid" = l."vp_rowid") AS "c10",
(SELECT r."c11" FROM "vp_database0_16_r" r
WHERE r."vp_rowid" = l."vp_rowid") AS "c11"
FROM "vp_database0_16_l" l
WHERE l."vp_rowid" IN (
SELECT r."vp_rowid" FROM "vp_database0_16_r" r
)
) "vp_database0_16_source"
WHERE "c7" IS NOT NULL
ORDER BY vp_database0_16_source.vp_rowid
LIMIT 0
""";

private DuckDBVpCrash3281876() {
}

public static void main(String[] args) throws Exception {
int repetitions = args.length == 0 ? 1000 : Integer.parseInt(args[0]);
Path tempDirectory = Files.createTempDirectory("duckdb-vp-repro-3281876-");
String url = "jdbc:duckdb:";
try (Connection connection = DriverManager.getConnection(url);
Statement statement = connection.createStatement()) {
statement.execute("SET threads=1");
statement.execute("SET temp_directory='"
+ tempDirectory.toAbsolutePath().toString().replace("'", "''") + "'");
setup(statement);

assertZeroRows(statement, SOURCE_QUERY, "source");
statement.execute("SET disabled_optimizers='filter_pushdown,join_order'");
System.out.println("setup complete; threads=1; repetitions=" + repetitions);
for (int i = 1; i <= repetitions; i++) {
assertZeroRows(statement, SPLIT_QUERY, "split");
if (i == 1 || i % 100 == 0) {
System.out.println("split repetitions=" + i);
}
}
System.out.println("completed without native crash");
}
}

private static void setup(Statement statement) throws Exception {
statement.execute("""
CREATE TABLE "t0" (
c0 BIGINT, c1 INTEGER, c2 DOUBLE, c3 VARCHAR,
c4 VARCHAR COLLATE NOCASE, c5 BOOLEAN,
c6 DECIMAL(18,3), c7 TIMESTAMP, c8 INTERVAL,
c9 INTEGER[], c10 STRUCT(f0 INTEGER, f1 VARCHAR),
c11 MAP(VARCHAR, INTEGER)
)
""");
statement.execute("""
INSERT INTO "t0"
SELECT CASE WHEN i % 17 = 0 THEN NULL
ELSE CASE WHEN i % 29 = 0 THEN 9223372036854775807
ELSE CAST(random() * 2000000000 - 1000000000 AS BIGINT) END END,
CASE WHEN i % 11 = 0 THEN NULL ELSE CAST(random() * 97 AS INTEGER) END,
CASE WHEN i % 13 = 0 THEN NULL ELSE (random() * 2e9 - 1e9) END,
CASE WHEN i % 19 = 0 THEN NULL
ELSE concat('v-', CAST(random() * 31 AS INTEGER), '-',
repeat('x', CAST(random() * 97 AS INTEGER))) END,
CASE WHEN i % 7 = 0 THEN 'A' WHEN i % 7 = 1 THEN 'a'
ELSE concat('k', CAST(random() * 23 AS INTEGER)) END,
random() < 0.5,
CAST(random() * 100000 AS DECIMAL(18,3)),
TIMESTAMP '2000-01-01' + CAST(random() * 100000 AS INTEGER) * INTERVAL '1 second',
CAST(random() * 1000 AS INTEGER) * INTERVAL '1 millisecond',
[CAST(random() * 5 AS INTEGER), NULL,
CASE WHEN random() < 0.33 THEN CAST(random() * 10000 AS INTEGER) ELSE NULL END],
struct_pack(f0 := CAST(random() * 37 AS INTEGER),
f1 := CASE WHEN i % 5 = 0 THEN NULL
ELSE concat('u', CAST(random() * 17 AS INTEGER)) END),
map([concat('m', CAST(random() * 9 AS INTEGER))],
[CAST(random() * 101 AS INTEGER)])
FROM range(4096) t(i)
""");
statement.execute("""
CREATE TABLE "vp_database0_16_source" AS
SELECT rowid AS vp_rowid, c0, c1, c2, c3 COLLATE C AS c3,
c4 COLLATE C AS c4, c5, c6, c7, c8, c9, c10, c11
FROM "t0"
ORDER BY c0, c1, c3, c4, c5, c6, c7
LIMIT 2047
""");
statement.execute("""
CREATE TABLE "vp_database0_16_l" AS
SELECT vp_rowid, c6 FROM "vp_database0_16_source"
""");
statement.execute("""
CREATE TABLE "vp_database0_16_r" AS
SELECT vp_rowid, c4, c5, c11, c10, c3, c7, c2, c9, c1, c0, c8
FROM "vp_database0_16_source"
""");
statement.execute("""
CREATE INDEX "vp_database0_16_r_asym" ON "vp_database0_16_r"(c1, vp_rowid)
""");
statement.execute("""
CREATE INDEX "vp_database0_16_idx_r" ON "vp_database0_16_r"(c3, c4)
""");
}

private static void assertZeroRows(Statement statement, String query, String label) throws Exception {
try (ResultSet resultSet = statement.executeQuery(query)) {
if (resultSet.next()) {
throw new AssertionError(label + " unexpectedly returned a row");
}
}
}
}
```

### Compile and run

```bash
mkdir -p /tmp/duckdb-vp-repro-classes

javac \
-cp target/lib/duckdb_jdbc-1.5.5.1.jar \
-d /tmp/duckdb-vp-repro-classes \
repro/DuckDBVpCrash3281876.java

timeout 30s java \
-cp "/tmp/duckdb-vp-repro-classes:target/lib/duckdb_jdbc-1.5.5.1.jar" \
DuckDBVpCrash3281876 1000
```

### Actual result

```text
Source:
query succeeds, returns empty result set (0 rows).

VP:
native SIGSEGV during query execution; JVM is aborted; no result set returned.
Process exit code: 134.
Crash location: org.duckdb.DuckDBNative.duckdb_jdbc_execute_pending.

Typical output:

setup complete; threads=1; repetitions=1000
split repetitions=1
SIGSEGV
```

### Expected result

```text
Source:
empty result set (0 rows).

VP:
empty result set (0 rows).
```

Both queries end with `LIMIT 0` at the outermost level, and the VP query is an equivalent split-and-reconstruct of the source table. Both should execute successfully and return an empty result set, without causing a DuckDB/JVM native crash.

## Notes on reproducibility

The test data is generated using DuckDB `random()`, so the number of iterations required to trigger the crash may vary across machines. It is recommended to keep the loop at 1000 iterations; if a single run does not trigger the crash, try running it a few more times. For consistency, please use DuckDB JDBC 1.5.5.1 and do not substitute it with another version.

### OS:

Ubuntu 20.04.6 LTS, x86_64

### DuckDB Version:

1.5.5.1

### DuckDB Client:

Java JDBC

### Hardware:

Intel Xeon Gold 5218R, 80 cores, 1486G RAM

### Full Name:

Annie liu

### Affiliation:

ECNU

### Did you include all relevant configuration (e.g., CPU architecture, Linux distribution) to reproduce the issue?

- [x] Yes, I have

### Did you include all code required to reproduce the issue?

- [x] Yes, I have

### Did you include all relevant data sets for reproducing the issue?

Yes

Contributor guide

No contributing guide indexed for this repository

Research direction

Compile and run repro/DuckDBVpCrash3281876.java with DuckDB JDBC 1.5.5.1 to reproduce the repeated SPLIT_QUERY crash at duckdb_jdbc_execute_pending, then trace the JDBC/native execution path involved. Done means 1000 repetitions complete without SIGSEGV and both source and split queries return zero rows.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, sql
Domain
databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.