Open b0o opened 1 year ago
I just ran into the problem with a bit of a simpler setup:
export const addresses = pgTable('addresses', {
id: uuid('id').defaultRandom().primaryKey(),
latitude: decimal('latitude', {
precision: 8,
scale: 6,
}).notNull(),
longitude: decimal('longitude', {
precision: 9,
scale: 6,
}).notNull()
});
export const userAddresses = pgTable(
'user_addresses',
{
addressId: uuid('address_id')
.notNull()
.references(() => addresses.id, {
onDelete: 'cascade',
}),
name: text('name'),
},
(table) => ({
addressIdUserIdNamePk: primaryKey(table.addressId)
}),
);
With this setup, if I just query addresses
like
const regular = (
await this.drizzleService.db.select().from(addresses).limit(1)
)[0];
I get
{
"id": "21c0ff1e-3065-4457-bd48-ab5ad05afc55",
"latitude": "30.241401",
"longitude": "-97.752413"
}
Using the RQB like
const rqb = await this.drizzleService.db.query.addresses.findFirst();
also gives the same response. However, when you query using the RQB relation, like
const rqb = await this.drizzleService.db.query.userAddresses.findFirst({
with: {
address: true,
},
});
you get
{
"addressId": "97ee01f0-2ab1-479b-9dc9-bdfeba2607d8",
"address": {
"id": "97ee01f0-2ab1-479b-9dc9-bdfeba2607d8",
"latitude": 39.952406,
"longitude": -75.187326
}
}
This issue is very interesting. The main problem is that mysql will encode decimal as regular numbers when serializing to json. Since the RQB relies on json aggregation to be fast, it ends up receiving a number instead of a string. I don't think there's a workaround right now, other that just using the regular crud API.
I just opened a PR that fixes this issue for decimal in #1688, in your case my fix won't work because your column is not exactly decimal. My fix is going to look specifically for decimal columns and will cast it to char at the database, that way it's serialized to json properly.
In your case you are going to have to wait for #1694. That's PR that fixes another issue when using the extras columns in RQB. When that PR is merged, you'll have to manually cast your column to char and user mapWith
to parse it with you column decoder accordingly. Like this:
const bobToAliceWithRelations = await db.query.transfers.findFirst({
where: eq(transfers.id, parseInt(bobToAliceId)),
with: {
fromAccount: {
columns: { balance: false },
extras: { balance: sql`cast(${account.balance} to char)`.mapWith(account.balance).as('balance') }
},
toAccount: {
columns: { balance: false },
extras: { balance: sql`cast(${account.balance} to char)`.mapWith(account.balance).as('balance') }
},
},
})
What version of
drizzle-orm
are you using?v0.27.0
What version of
drizzle-kit
are you using?v0.19.2
Describe the Bug
Here's a minimal reproduction of the issue:
This is the console output (note the
cast called
messages, produced by the customcast
function we passed to Planetscale'sdatabase-js
, which allows us to see the underlying values before they're cast in JS land):Important things to note in the output:
toDriver
/fromDriver
work fine when used in normal queriesbobToAliceWithRelations
, drizzle seems to usecast(toAccount as json)
to get the related recordamount
in the resulting JSON object has been implicitly cast to a floating point JSON number, which defeats the purpose of using aDECIMAL
fromDriver
is called on theamount
from the JSON-encoded version oftoAccount
, which is now a numberfromDriver(value: string)
expects, based on the monetary customType'sdriverData
being astring
fromDriver
tries and fails to callsplit()
onvalue
, which is actually a numberExpected behavior
Custom types should behave the same in both normal and relational queries.
Environment & setup
No response