Skip to content

Bug: untyped relationship patterns return wrong results on icebug-disk tables #1066

Description

@jfrench9

Ladybug version

0.21.0 (also 0.18.1, with different wrong answers)

What operating system are you using?

macOS 15 arm64, PyPI wheels

What happened?

On a graph mounted from icebug-disk (EXPORT DATABASE output, then its schema.cypher), relationship patterns with no rel type give wrong results. The same queries against the native database the export came from are correct, and every typed pattern on the icebug side is correct too, so the data in the tree looks fine. It's the untyped scan.

Using the graph below (3 rel tables with 60, 300 and 300 edges, plus one empty rel table):

query native icebug-disk, 0.21.0 icebug-disk, 0.18.1
MATCH ()-[r]->() RETURN count(*) 660 180 660
MATCH (i:Item {id: 3})-[r]-() RETURN label(r), count(*) LINK_TO_ITEM 1, OBS_OF_ITEM 8 LINK_TO_ITEM 1, OBS_OF_ITEM 1 []
MATCH ()-[r]->() RETURN label(r), count(*) 60 / 300 / 300 60 / 300 / 300 60 / 300 / 300

No error in any case, just wrong numbers. On a larger graph (22 rel tables, several empty) the grouped form was wrong as well: empty rel tables came back with thousands of rows on 0.21.0, and some non-empty ones were missing entirely on 0.18.1.

Expected: untyped patterns over icebug-disk tables return the same rows as typed patterns unioned, i.e. what the native database returns.

Are there known steps to reproduce?

Build a native graph and export it (0.21.0):

import shutil, ladybug as lb

shutil.rmtree("g.lbug", ignore_errors=True)
shutil.rmtree("g_ice", ignore_errors=True)
db = lb.Database("g.lbug")
c = lb.Connection(db)
c.execute("CREATE NODE TABLE Link(id INT64, PRIMARY KEY(id))")
c.execute("CREATE NODE TABLE Item(id INT64, name STRING, PRIMARY KEY(id))")
c.execute("CREATE NODE TABLE Obs(id INT64, value DOUBLE, PRIMARY KEY(id))")
c.execute("CREATE NODE TABLE Day(id INT64, date STRING, PRIMARY KEY(id))")
c.execute("CREATE REL TABLE LINK_TO_ITEM(FROM Link TO Item)")
c.execute("CREATE REL TABLE OBS_OF_ITEM(FROM Obs TO Item)")
c.execute("CREATE REL TABLE OBS_ON_DAY(FROM Obs TO Day)")
c.execute("CREATE REL TABLE ITEM_ON_DAY(FROM Item TO Day)")  # left empty
c.execute("UNWIND range(0, 39) AS i CREATE (:Item {id: i, name: 'item' + CAST(i + 10 AS STRING)})")
c.execute("UNWIND range(0, 3) AS k CREATE (:Day {id: k, date: '2026-0' + CAST(k + 1 AS STRING) + '-01'})")
c.execute("UNWIND range(0, 59) AS l CREATE (:Link {id: l})")
c.execute("UNWIND range(0, 299) AS o CREATE (:Obs {id: o, value: CAST((o * 7919) % 97 AS DOUBLE) * 1.0e6})")
c.execute("MATCH (l:Link), (i:Item) WHERE i.id = (l.id * 13) % 40 CREATE (l)-[:LINK_TO_ITEM]->(i)")
c.execute("MATCH (o:Obs), (i:Item) WHERE i.id = (o.id * 17) % 40 CREATE (o)-[:OBS_OF_ITEM]->(i)")
c.execute("MATCH (o:Obs), (k:Day) WHERE k.id = (o.id * 5) % 4 CREATE (o)-[:OBS_ON_DAY]->(k)")
c.execute("EXPORT DATABASE 'g_ice'")  # 0.21.0 writes icebug-disk
c.close(); db.close()

Mount the export and compare:

import sys, ladybug as lb

def native():
    return lb.Connection(lb.Database("g.lbug", read_only=True))

def icebug():
    c = lb.Connection(lb.Database(":memory:"))
    for stmt in open("g_ice/schema.cypher").read().split(";\n"):
        if stmt.strip():
            c.execute(stmt)
    return c

for q in ["MATCH ()-[r]->() RETURN count(*)",
          "MATCH (i:Item {id: 3})-[r]-() RETURN label(r), count(*) ORDER BY label(r)"]:
    print(q)
    print("  native:", native().execute(q).get_all())
    print("  icebug:", icebug().execute(q).get_all())
MATCH ()-[r]->() RETURN count(*)
  native: [[660]]
  icebug: [[180]]
MATCH (i:Item {id: 3})-[r]-() RETURN label(r), count(*) ORDER BY label(r)
  native: [['LINK_TO_ITEM', 1], ['OBS_OF_ITEM', 8]]
  icebug: [['LINK_TO_ITEM', 1], ['OBS_OF_ITEM', 1]]

Possibly the same family as #943 (anti-edge count rewrite gated to native storage), though the undirected case above returns wrong rows, not just a wrong count.

Activity

  1. added a commit that references this issue on Sep 29, 2026
    09354d3
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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions