-
-
Notifications
You must be signed in to change notification settings - Fork 94
Closed
Milestone
Description
For simplicity I will use bun.js.
helpers file: https://github.com/mindreframer/arcadex/blob/main/jstester/tests/helpers.js
import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
import {
createDatabase,
cleanupDatabase,
uniqueDbName,
command,
query
} from './helpers.js';
describe('ULTRA MINIMAL ArcadeDB Bug - Plain SQL Only', () => {
const dbName = uniqueDbName('ultra_minimal');
beforeAll(async () => {
await cleanupDatabase(dbName);
await createDatabase(dbName);
});
afterAll(async () => {
await cleanupDatabase(dbName);
});
test('absolute minimal reproduction', async () => {
console.log('\n=== SETUP ===');
// 1. Create two types with LINK and COMPOSITE INDEX
await command(dbName, `CREATE DOCUMENT TYPE Parent`);
await command(dbName, `CREATE DOCUMENT TYPE Child`);
await command(dbName, `CREATE PROPERTY Child.uid STRING`);
await command(dbName, `CREATE PROPERTY Child.status STRING (default 'synced')`);
await command(dbName, `CREATE PROPERTY Child.version INTEGER (default 1)`);
await command(dbName, `CREATE PROPERTY Child.parent LINK Parent`);
// THE KEY: Composite index on (status, version)
await command(dbName, `CREATE INDEX ON Child (status, version) NOTUNIQUE`);
// 2. Create parent
const p = await command(dbName, `INSERT INTO Parent SET name = 'p1' RETURN @this`);
const pRid = p.result[0]['@rid'];
// 3. Insert 3 children WITHOUT explicit status (use default)
await command(dbName, `INSERT INTO Child SET uid = 'c1', parent = ${pRid}`);
await command(dbName, `INSERT INTO Child SET uid = 'c2', parent = ${pRid}`);
await command(dbName, `INSERT INTO Child SET uid = 'c3', parent = ${pRid}`);
console.log('Created 3 children with default status=synced');
// 4. Mark c1 and c2 as pending
await command(dbName, `UPDATE Child SET status = 'pending' WHERE uid = 'c1'`);
await command(dbName, `UPDATE Child SET status = 'pending' WHERE uid = 'c2'`);
console.log('\n=== BEFORE BUG ===');
// 5. Verify WHERE works - should find 2 pending
const before = await query(dbName, `SELECT uid, status FROM Child WHERE status = 'pending'`);
console.log('Pending (WHERE):', before.result.length, '→', before.result.map(r => r.uid));
expect(before.result.length).toBe(2);
console.log('\n=== TRIGGER BUG ===');
// 6. Update c1 with parameterized multi-field UPDATE (including version field in composite index)
await command(dbName, `UPDATE Child SET version = :version, status = :status WHERE uid = :uid`, {
uid: 'c1',
version: 2,
status: 'synced'
});
console.log('Updated c1 to synced (multi-field parameterized UPDATE)');
console.log('\n=== AFTER BUG ===');
// 7. Check without WHERE - should show c2 pending, c1+c3 synced
const all = await query(dbName, `SELECT uid, status FROM Child ORDER BY uid`);
console.log('All (no WHERE):', all.result);
// 8. BUG: WHERE status='pending' should find c2 but finds 0
const pending = await query(dbName, `SELECT uid, status FROM Child WHERE status = 'pending'`);
console.log('Pending (WHERE):', pending.result.length, '→', pending.result.map(r => r.uid));
// 9. BUG: WHERE status='synced' should find c1+c3 but finds only c1
const synced = await query(dbName, `SELECT uid, status FROM Child WHERE status = 'synced'`);
console.log('Synced (WHERE):', synced.result.length, '→', synced.result.map(r => r.uid));
console.log('\n=== RESULT ===');
console.log(`Pending: expected 1 (c2), got ${pending.result.length}`);
console.log(`Synced: expected 2 (c1,c3), got ${synced.result.length}`);
expect(pending.result.length).toBe(1);
expect(synced.result.length).toBe(2);
});
});
=== SETUP ===
CREATE DOCUMENT TYPE Parent;
CREATE DOCUMENT TYPE Child;
CREATE PROPERTY Child.uid STRING;
CREATE PROPERTY Child.status STRING (default 'synced');
CREATE PROPERTY Child.version INTEGER (default 1);
CREATE PROPERTY Child.parent LINK Parent;
CREATE INDEX ON Child (status, version) NOTUNIQUE;
INSERT INTO Parent SET name = 'p1' RETURN @this;
INSERT INTO Child SET uid = 'c1', parent = #1:0;
INSERT INTO Child SET uid = 'c2', parent = #1:0;
INSERT INTO Child SET uid = 'c3', parent = #1:0;
Created 3 children with default status=synced
UPDATE Child SET status = 'pending' WHERE uid = 'c1';
UPDATE Child SET status = 'pending' WHERE uid = 'c2';
=== BEFORE BUG ===
SELECT uid, status FROM Child WHERE status = 'pending';
Pending (WHERE): 2 → [ "c1", "c2" ]
=== TRIGGER BUG ===
UPDATE Child SET version = :version, status = :status WHERE uid = :uid;
Updated c1 to synced (multi-field parameterized UPDATE)
=== AFTER BUG ===
SELECT uid, status FROM Child ORDER BY uid;
All (no WHERE): [
{
uid: "c1",
status: "synced",
}, {
uid: "c2",
status: "pending",
}, {
uid: "c3",
status: "synced",
}
]
SELECT uid, status FROM Child WHERE status = 'pending';
Pending (WHERE): 0 → []
SELECT uid, status FROM Child WHERE status = 'synced';
Synced (WHERE): 1 → [ "c1" ]
=== RESULT ===
Pending: expected 1 (c2), got 0
Synced: expected 2 (c1,c3), got 1
I suspect this has something to do with the parameterized multi-field UPDATE .
Good thing: it's consistent and quite reproducible.
Cheers!
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels