Skip to content

Bug: SIGSEGV re-executing a parameterized write query string (0.20.0 regression) #862

Description

@zahariash

Ladybug version

0.20.0. Clean on 0.18.3, 0.19.0 and 0.19.1.

What operating system are you using?

Linux x86-64, Python 3.10–3.14.

What happened?

Calling Connection.execute(<query string>, <params>) twice with the same
string for a write query segfaults the process on the second call. No exception,
no message — the interpreter dies with SIGSEGV (exit 139).

conn.execute("CREATE (:Log {id: $id, value: $val})", {"id": 1, "val": "a"})   # ok
conn.execute("CREATE (:Log {id: $id, value: $val})", {"id": 2, "val": "b"})   # SIGSEGV

Expected: two Log rows. Actual: the process dies on the second call.

Variants, all on 0.20.0:

variant
same string twice, parameterized CREATE SIGSEGV
one extra space in the second string ok
prepare() once, execute the statement twice ok
same string twice inside BEGIN TRANSACTION SIGSEGV
same string twice, parameterized MATCH ... SET SIGSEGV
same string twice, parameterized read-only MATCH ... RETURN ok
two writes with literal values, no parameters ok

Keyed by query text (a single extra space avoids it), the explicit
PreparedStatement path is unaffected, writes only, and transactions change
nothing — which points at the cached physical plan reused on the parameterized
write path.

The reproducer needs no third-party packages, and crashes identically on every
Python ladybug supports (3.10–3.14).

Suspected origin, not bisected. Two 0.20.0 changes interact here: #841 made
repeated executions of a cached physical plan correct while the thread-local
ResultSet cache was still inert, and #850 then activated that cache shortly
before the release. The shipping combination — a cached plan re-executed with a
live TLS ResultSet cache on a write pipeline — postdates #841's verification,
and resetForReuse() may not cover a write pipeline's per-execution state.

Workaround. Prepare once and execute the statement:

stmt = conn.prepare("CREATE (:Log {id: $id, value: $val})")
conn.execute(stmt, {"id": 1, "val": "a"})
conn.execute(stmt, {"id": 2, "val": "b"})

Are there known steps to reproduce?

#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.10,<3.15"
# dependencies = ["ladybug==0.20.0"]
# ///
"""ladybug 0.20.0: SIGSEGV on the second execute() of the same parameterized
write query string.  Clean: 0.18.3, 0.19.0, 0.19.1.

On 0.20.0 this script is expected to die with SIGSEGV (exit 139) at the second
execute() -- that is the bug, not a script defect.

    uv run cached_plan_param_write_segv.py
"""
import os
import sys
import tempfile

import ladybug as kuzu

tmp = tempfile.mkdtemp(prefix="lbug_planseg_")
conn = kuzu.Connection(kuzu.Database(os.path.join(tmp, "db")))
conn.execute("CREATE NODE TABLE Log(id INT64, value STRING, PRIMARY KEY(id));")
print(f"ladybug {kuzu.__version__}", flush=True)

query = "CREATE (:Log {id: $id, value: $val})"
conn.execute(query, {"id": 1, "val": "a"})
print("first execute:  ok", flush=True)
conn.execute(query, {"id": 2, "val": "b"})  # 0.20.0: SIGSEGV here
print("second execute: ok", flush=True)

n = conn.execute("MATCH (l:Log) RETURN count(l)").get_next()[0]
print(f"rows: {n} (expected 2)")
sys.exit(0 if n == 2 else 1)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions