duckdb / duckdb/duckdb-java

Native SIGSEGV in DuckDB JDBC when repeatedly executing queries with window functions and LIMIT 0

Open
#871 3 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 query that contains a window function (`RANK() OVER (...)`), an `ORDER BY`, and a `LIMIT 0` causes a native `SIGSEGV` crash in DuckDB JDBC. The JVM process is aborted with exit code 134. The crash occurs in `org.duckdb.DuckDBNative.duckdb_jdbc_execute_pending`.

The crash is reproducible in **both** the `source` mode (single-table query) and the `split` mode (split query), running single-threaded with `threads=1`. Both queries use `LIMIT 0` and should return an empty result set without crashing.

### To Reproduce

### Driver

Use DuckDB JDBC:

```xml

org.duckdb
duckdb_jdbc
1.5.5.1

```

### Reproducer

Save the following as `repro/DuckDBVpCrash1279455Stable.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;

/**
* Stable minimized reproducer for the DuckDB JDBC native crash observed in
* hs_err_pid1279455.log from SQLancer's VP split side.
*
* Shape preserved from the crash context:
* - split-side query
* - disabled_optimizers='filter_pushdown,join_order'
* - RANK() OVER (...) over a reconstructed VP relation
* - right side pre-aggregated by row id and joined back
* - ORDER BY ... LIMIT 0
*
* Tested with org.duckdb:duckdb_jdbc:1.5.5.1 on OpenJDK 17.
*/
public final class DuckDBVpCrash1279455Stable {

private static String sourceQuery(String sourceTable) {
return """
SELECT RANK() OVER (ORDER BY vp_rowid, vp_rowid)
FROM %s
ORDER BY vp_rowid
LIMIT 0
""".formatted(sourceTable);
}

private static String splitQuery(String leftTable, String rightTable) {
return """
SELECT RANK() OVER (ORDER BY vp_rowid, vp_rowid)
FROM (
SELECT l.vp_rowid AS vp_rowid, r.c7 AS c7
FROM %s l
JOIN (SELECT vp_rowid, COUNT(*) AS vp_count FROM %s GROUP BY vp_rowid) rg
ON l.vp_rowid = rg.vp_rowid
JOIN %s r
ON r.vp_rowid = rg.vp_rowid
) vp_source
ORDER BY vp_source.vp_rowid
LIMIT 0
""".formatted(leftTable, rightTable, rightTable);
}

private DuckDBVpCrash1279455Stable() {
}

public static void main(String[] args) throws Exception {
int repetitions = args.length == 0 ? 200 : Integer.parseInt(args[0]);
String logId = args.length < 2 ? "1279455" : sanitize(args[1]);
String mode = args.length < 3 ? "split" : args[2].toLowerCase();
if (!mode.equals("source") && !mode.equals("split")) {
throw new IllegalArgumentException("mode must be 'source' or 'split'");
}
boolean sourceOnly = mode.equals("source");
Path databaseFile = Path.of("repro_duckdb" + logId + "_db_min.duckdb").toAbsolutePath();
String sourceTable = "source_" + logId;
String leftTable = "left_" + logId;
String rightTable = "right_" + logId;
Path tempDirectory = Files.createTempDirectory("duckdb-vp-crash-1279455-");
// Start from a fresh, named DuckDB database file so the developer only
// needs to compile and run this JDBC file.
Files.deleteIfExists(databaseFile);
Files.deleteIfExists(Path.of(databaseFile + ".wal"));
try (Connection connection = DriverManager.getConnection("jdbc:duckdb:" + databaseFile);
Statement statement = connection.createStatement()) {
statement.execute("SET threads=1");
statement.execute("SET temp_directory='"
+ tempDirectory.toAbsolutePath().toString().replace("'", "''") + "'");
statement.execute("""
CREATE TABLE %s AS
SELECT i AS vp_rowid,
TIMESTAMP '2000-01-01' + CAST(i AS INTEGER) * INTERVAL '1 second' AS c7
FROM range(100) t(i)
""".formatted(sourceTable));
statement.execute("CREATE TABLE " + leftTable + " AS SELECT vp_rowid FROM " + sourceTable);
statement.execute("CREATE TABLE " + rightTable + " AS SELECT vp_rowid, c7 FROM " + sourceTable);
statement.execute("SET disabled_optimizers='filter_pushdown,join_order'");
String splitQuery = splitQuery(leftTable, rightTable);
String query = sourceOnly ? sourceQuery(sourceTable) : splitQuery;

for (int i = 1; i <= repetitions; i++) {
try (ResultSet resultSet = statement.executeQuery(query)) {
if (resultSet.next()) {
throw new AssertionError("LIMIT 0 query unexpectedly returned a row");
}
}
if (i == 1 || i % 10 == 0) {
System.out.println(mode + " repetitions=" + i);
}
}
System.out.println("completed without native crash");
}
}

private static String sanitize(String value) {
String sanitized = value.replaceAll("[^A-Za-z0-9_]", "_");
return sanitized.isEmpty() ? "1279455" : sanitized;
}
}
```

### Compile

```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/DuckDBVpCrash1279455Stable.java
```

### Run in `source` mode

```bash
timeout 45s java \
-cp '/tmp/duckdb-vp-repro-classes:target/lib/duckdb_jdbc-1.5.5.1.jar' \
DuckDBVpCrash1279455Stable 200 1279455 source
```

### Run in `split` mode

```bash
timeout 45s java \
-cp '/tmp/duckdb-vp-repro-classes:target/lib/duckdb_jdbc-1.5.5.1.jar' \
DuckDBVpCrash1279455Stable 200 1279455 split
```

Argument meanings:

```text
200 repeat the query 200 times
1279455 log / case id, used to generate database and table names
source run the source query
split run the VP split query
```

The reproducer automatically:

- creates the database file;
- creates the test tables;
- inserts 100 rows of deterministic data;
- sets `threads=1`;
- sets `SET disabled_optimizers='filter_pushdown,join_order'`;
- repeatedly executes the query.

### Actual result

Both `source` and `split` modes cause the JVM to crash inside DuckDB native code:

```text
SIGSEGV (0xb)
Problematic frame:
C [libc.so.6+...]
```

Typical Java stack:

```text
org.duckdb.DuckDBNative.duckdb_jdbc_execute_pending
org.duckdb.DuckDBPreparedStatement.execute
org.duckdb.DuckDBPreparedStatement.executeQuery
DuckDBVpCrash1279455Stable.main
```

Process exit code is typically:

```text
134
```

### Expected result

Because the queries use `LIMIT 0`, they should:

- return an empty result set normally;
- let the Java program exit cleanly;

and should **not** cause:

```text
SIGSEGV
Aborted (core dumped)
JVM native crash
```

### OS:

Ubuntu 20.04.6 LTS, x86_64

### DuckDB Version:

v1.5.5

### 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

Start by compiling repro/DuckDBVpCrash1279455Stable.java with the provided JDBC jar and run it in both source and split modes. Confirm the repeated LIMIT 0 queries crash in duckdb_jdbc_execute_pending rather than returning an empty result set, then trace the JDBC/native execution path to identify the failure and verify clean completion in both modes.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, java, sql
Domain
database
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.