Summary
Since 0.19.0 — and still in 0.19.1 — querying an attached database is broken in two ways that both worked in 0.18.3:
USE <alias> no longer switches the default catalog. It still answers Used database successfully, but subsequent unqualified queries fail with Table <X> does not exist.
- Relationship tables in an attached database are unreachable. The qualified form is rejected as unimplemented, the unqualified form cannot find the table — and the anonymous form silently returns 0 rows instead of erroring.
That last one — the silent 0 — is the reason I'm filing this rather than working around it. A traversal over an attached database returns an empty result with no error, so an application reading a correct database gets a wrong answer that looks like a legitimate "nothing found". Node-only queries keep returning correct counts, so nothing signals that anything is wrong.
Reproduction
Self-contained, no extensions and no object storage — two local databases:
// repro.mjs — node repro.mjs
import lbug from '@ladybugdb/core';
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
const root = mkdtempSync(join(tmpdir(), 'lbug-attach-'));
const target = join(root, 'target.lbdb');
const shell = join(root, 'shell.lbdb');
const q = async (conn, sql) => {
const r = await conn.query(sql);
return await (Array.isArray(r) ? r[r.length - 1] : r).getAll();
};
const n = (v) => (typeof v === 'bigint' ? Number(v) : v);
// Database to be attached: 3 nodes, 2 relationships.
{
const db = new lbug.Database(target);
const conn = new lbug.Connection(db);
await q(conn, 'CREATE NODE TABLE Person(name STRING, PRIMARY KEY(name))');
await q(conn, 'CREATE REL TABLE Knows(FROM Person TO Person)');
await q(conn, "CREATE (:Person {name:'a'}), (:Person {name:'b'}), (:Person {name:'c'})");
await q(conn, "MATCH (x:Person {name:'a'}), (y:Person {name:'b'}) CREATE (x)-[:Knows]->(y)");
await q(conn, "MATCH (x:Person {name:'b'}), (y:Person {name:'c'}) CREATE (x)-[:Knows]->(y)");
await q(conn, 'CHECKPOINT');
await conn.close?.();
await db.close?.();
}
const db = new lbug.Database(shell);
const conn = new lbug.Connection(db);
const t = async (label, sql) => {
try {
console.log(` OK ${label.padEnd(38)} ${JSON.stringify((await q(conn, sql))[0], (_k, v) => n(v))}`);
} catch (e) {
console.log(` FAIL ${label.padEnd(38)} ${String(e).split('\n')[0]}`);
}
};
console.log(`version: ${JSON.stringify((await q(conn, 'CALL db_version() RETURN *'))[0])}`);
await q(conn, `ATTACH '${target}' AS db1 (dbtype lbug)`);
await t('MATCH (p:db1.Person)', 'MATCH (p:db1.Person) RETURN count(p) AS n');
await t('-[e:db1.Knows]->', 'MATCH (a:db1.Person)-[e:db1.Knows]->(b:db1.Person) RETURN count(e) AS n');
await t('-[e:Knows]-> with db1.Person', 'MATCH (a:db1.Person)-[e:Knows]->(b:db1.Person) RETURN count(e) AS n');
await t('-[e]-> anonymous', 'MATCH (a:db1.Person)-[e]->(b:db1.Person) RETURN count(e) AS n');
await t('USE db1', 'USE db1');
await t('MATCH (p:Person)', 'MATCH (p:Person) RETURN count(p) AS n');
await t('-[e:Knows]->', 'MATCH (a:Person)-[e:Knows]->(b:Person) RETURN count(e) AS n');
await conn.close?.();
await db.close?.();
rmSync(root, { recursive: true, force: true });
Expected throughout: 3 nodes, 2 relationships.
Result
| query |
0.18.3 |
0.19.0 |
MATCH (p:db1.Person) |
3 |
3 |
-[e:db1.Knows]-> |
Table db1.Knows does not exist |
Qualified relationship patterns (e.g. -[r:db.rel]->) are not supported yet |
-[e:Knows]-> with db1.Person endpoints |
2 |
Table Knows does not exist |
-[e]-> anonymous |
2 |
0 ← no error |
USE db1 |
Used database successfully |
Used database successfully |
MATCH (p:Person) after USE |
3 |
Table Person does not exist |
-[e:Knows]-> after USE |
2 |
Table Person does not exist |
So on 0.18.3 there are two working ways to reach an attached database — USE plus unqualified names, or qualified node labels with an unqualified relationship label. On 0.19.0 neither works, and one of them fails silently.
Impact
We use an attached database as a read-only view over a graph produced elsewhere. Node lookups still behave correctly on 0.19.0, so the failure is invisible until a traversal runs — and then it reports "no relationships" for a database that has them. For an application that answers questions like "what depends on this", an empty result is indistinguishable from a legitimate answer.
Pinning to 0.18.x is a workaround, but it is not one we can hold indefinitely.
Environment
| status |
version |
platform |
| broken |
@ladybugdb/core 0.19.1 (CALL db_version() → 0.19.1) |
darwin/arm64, Node 22 |
| broken |
@ladybugdb/core 0.19.0 (CALL db_version() → 0.19.0) |
linux/arm64, Node 22 |
| working |
@ladybugdb/core 0.18.3 (CALL db_version() → 0.18.3) |
darwin/arm64, Node 22 |
0.19.0 and 0.19.1 produce byte-identical output on the reproduction above, on two different platforms. I have not tested the releases between 0.18.3 and 0.19.0, so the regression is bracketed as "somewhere after 0.18.3, present in both 0.19.0 and 0.19.1".
The same behaviour appears with a database attached over httpfs from object storage, which is how we hit it; the reproduction above avoids that path entirely to keep the report minimal.
Question
Is USE intended to keep working for attached databases, or is qualified addressing meant to become the only supported form? If it is the latter, -[r:alias.Rel]-> presumably needs to be implemented first — otherwise relationship tables in an attached database have no reachable syntax at all. And in either case, the anonymous pattern returning 0 rather than raising would be worth fixing on its own.
Summary
Since 0.19.0 — and still in 0.19.1 — querying an attached database is broken in two ways that both worked in 0.18.3:
USE <alias>no longer switches the default catalog. It still answersUsed database successfully, but subsequent unqualified queries fail withTable <X> does not exist.That last one — the silent
0— is the reason I'm filing this rather than working around it. A traversal over an attached database returns an empty result with no error, so an application reading a correct database gets a wrong answer that looks like a legitimate "nothing found". Node-only queries keep returning correct counts, so nothing signals that anything is wrong.Reproduction
Self-contained, no extensions and no object storage — two local databases:
Expected throughout: 3 nodes, 2 relationships.
Result
MATCH (p:db1.Person)33-[e:db1.Knows]->Table db1.Knows does not existQualified relationship patterns (e.g. -[r:db.rel]->) are not supported yet-[e:Knows]->withdb1.Personendpoints2Table Knows does not exist-[e]->anonymous20← no errorUSE db1Used database successfullyUsed database successfullyMATCH (p:Person)afterUSE3Table Person does not exist-[e:Knows]->afterUSE2Table Person does not existSo on 0.18.3 there are two working ways to reach an attached database —
USEplus unqualified names, or qualified node labels with an unqualified relationship label. On 0.19.0 neither works, and one of them fails silently.Impact
We use an attached database as a read-only view over a graph produced elsewhere. Node lookups still behave correctly on 0.19.0, so the failure is invisible until a traversal runs — and then it reports "no relationships" for a database that has them. For an application that answers questions like "what depends on this", an empty result is indistinguishable from a legitimate answer.
Pinning to 0.18.x is a workaround, but it is not one we can hold indefinitely.
Environment
@ladybugdb/core0.19.1 (CALL db_version()→0.19.1)@ladybugdb/core0.19.0 (CALL db_version()→0.19.0)@ladybugdb/core0.18.3 (CALL db_version()→0.18.3)0.19.0 and 0.19.1 produce byte-identical output on the reproduction above, on two different platforms. I have not tested the releases between 0.18.3 and 0.19.0, so the regression is bracketed as "somewhere after 0.18.3, present in both 0.19.0 and 0.19.1".
The same behaviour appears with a database attached over
httpfsfrom object storage, which is how we hit it; the reproduction above avoids that path entirely to keep the report minimal.Question
Is
USEintended to keep working for attached databases, or is qualified addressing meant to become the only supported form? If it is the latter,-[r:alias.Rel]->presumably needs to be implemented first — otherwise relationship tables in an attached database have no reachable syntax at all. And in either case, the anonymous pattern returning0rather than raising would be worth fixing on its own.