edgedb / edgedb-js

The official TypeScript/JS client library and query builder for EdgeDB
https://edgedb.com
Apache License 2.0
513 stars 65 forks source link

Selecting field used in filter_single causes "cardinality MANY which does not match the expected cardinality ONE" #708

Open hbriese opened 1 year ago

hbriese commented 1 year ago

When selecting a link that was selected in filter_single you get a ResultCardinalityMismatchError: the query has cardinality MANY which does not match the expected cardinality ONE error.

Copying the edgeql query (i.e. .toEdgeQL()) and executing it directly does not error, so the issue appears to be with the query builder.

The query looks silly, but makes more sense when selecting the account initially from another exclusive constraint, or when the query is constructed from something like graphql

Code

e.select(e.Item, () => ({
    filter_single: { 
        account: e.select(e.Account, () => ({ filter_single: { name: 'abc' } })),  
        key: 123
     },
     account: {
         id: true,
         name: true,
     },
}).run(client);

Schema

module default {
    type Account {
        required name: str { constraint exclusive; }  
    }

    type Item {
        required account: Account;
        requred key: int32;

        constraint exclusive on ((.account, .key));
    }
}

Error or desired behavior

ResultCardinalityMismatchError: the query has cardinality MANY which does not match the expected cardinality ONE
    at RawConnection._parseErrorMessage (/***/node_modules/edgedb/dist/baseConn.js:333:21)
    at RawConnection._executeFlow (/***/node_modules/edgedb/dist/baseConn.js:940:34)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at RawConnection.fetch (/***/node_modules/edgedb/dist/baseConn.js:992:17)
    at ClientConnectionHolder.retryingFetch (/***/node_modules/edgedb/dist/baseClient.js:182:26)
    at Client.querySingle (/***/node_modules/edgedb/dist/baseClient.js:509:20)

Workaround

Using e.assert_single and filter doesn't suffer from this issue

Versions (please complete the following information):

scotttrinh commented 1 year ago

We should be inferring the same cardinality as the EdgeQL compiler, which is the real bug here. We detect the cardinality of the query here as ONE (based on the use of filter_single), but for some reason the database is indicating that the cardinality ought to be MANY, and since we use querySingle on queries that we think ought to be EMPTY, ONE, and AT_MOST_ONE, we throw this error at query time.

It feels like the query ought to have a cardinality of ONE, so the real question here is why the server is responding with a MANY cardinality option here: is it a bug? If it is indeed MANY, we ought to detect this in the filter_single case and (ideally) make it a TypeScript compile-time error instead of a runtime error.