Native SIGSEGV in DuckDB JDBC when repeatedly executing a split query with scalar subqueries and LIMIT 0
- 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