SpacetimeDB icon indicating copy to clipboard operation
SpacetimeDB copied to clipboard

[Client] Inconsistent/missing query results with [PrimaryKey] vs. [Unique] columns

Open puttehi opened this issue 9 months ago • 0 comments

Hey, I have hit some really strange case with client SDK queries and I'm not actually sure if this issue should be in the TypeScript SDK repo, but here goes.

For some reason, querying against the local cache does return results for the primary key column that happens to line up with the query value ("unexpected happy accident"), but does not return results for its designated unique column, it's always undefined...?

Weird thing is, that this exact same pattern works elsewhere (player object), even the IDs happen to match similarly there too.

And an even weirder thing is, that the row clearly exists in the local cache as is seen in the later logs screenshot. By all logic, both queries should return exactly the same row. One is querying against the primary key column, one is querying against the unique column. Both share the same value.

I am calling onProjectileInserted(...) on SpacetimeDB.conn.projectiles.onInsert(...):

    onProjectileInserted(_ctx: EventContext, row: DBProjectile) {
        // TODO: temp testing
        const query = `SELECT * FROM transforms WHERE gameObjectId = ${row.gameObjectId}`
        SpacetimeDB.subscribeTo([
            query
        ], this.todoCacheProjectile(row).bind(this)) // onApplied cb, see below
    }

    // (onApplied)
    todoCacheProjectile(row: DBProjectile) {
        return (ctx: SubscriptionEventContext) => {
            const t1 = SpacetimeDB.conn.db.transforms.id.find(row.gameObjectId); // Happens to line up with gameObjectIds value, works OK
            const t2 = SpacetimeDB.conn.db.transforms.game_object_id.find(row.gameObjectId); // Real game object ID, always returns undefined?
            console.info("projectile id:", row.gameObjectId) // xxxx, (game object id, bad log message)
            console.dir({ "DEBUG": "new projectile", ...row }) // just inserted row
            console.dir({ "DEBUG": "id.find (PK, AutoInc)", ...t1 }) // returns correct value
            console.dir({ "DEBUG": "gameObjectId.find (\"FK\", Unique)", ...t2, "DEBUG_is_undefined": t2 === undefined }) // undefined??
            console.dir([...SpacetimeDB.conn.db.transforms.iter()]) // but it clearly exists here?
            console.dir([...ctx.db.transforms.iter()]) // and here?
            // console.dir(SpacetimeDB.conn.db.transforms.game_object_id.find(row.gameObjectId)) // undefined
            if (!t2) {
                console.error(`Projectile ${row.id} had no local Transform for its GameObject ${row.gameObjectId}`)
                return
            }
            this.projectiles.set(row.id, new Projectile(this, t2.pos.x, t2.pos.y, row.gameObjectId))
        }
    }

Results:

So, doesn't it clearly exist in the local table, it is even findable with the primary key that happens to line up (t1) but not for the unique key for some reason which is always undefined...? Am I missing something stupidly obvious? Even my trusted rubber ducky cannot save me.

Here are the table definitions:

    [Table(Name = "transforms", Public = true)]
    public partial struct Transform
    {
        [PrimaryKey, AutoInc]
        public uint id;

        [Unique]
        public uint gameObjectId;

        public Vector2 pos; // NOTE: position is a keyword, requires quoting in queries
    }

    [Table(Name = "projectiles", Public = true)]
    public partial struct Projectile
    {
        [PrimaryKey, AutoInc]
        public uint id;

        [Unique]
        public uint gameObjectId;

        public uint damageComponentId;
        public ulong lifetimeScheduledId;
    }

On a related note, in TypeScript SDK the conn.db table handle is snake_cased but it is camelCased everywhere else.

puttehi avatar Mar 18 '25 18:03 puttehi