Skip to content

Polymorphic relationship breaks Decimal values with Postgres #1487

@sslotnick

Description

@sslotnick

Description and expected behavior

Using delegates causes the response object to get decorated differently from when using the prisma client directly. This seems to affect all models with any relationship to the polymorphic model, even if it is not being queried directly.

Test Case

import { createPostgresDb, dropPostgresDb, loadSchema } from '@zenstackhq/testtools';
import Decimal from 'decimal.js';

describe('new-issue', () => {
    it('regression1', async () => {
        const dbUrl = await createPostgresDb('new-issue-1');
        let prisma: any;

        try {
            const r = await loadSchema(
                `
                model LineItem {
                    id Int @id @default(autoincrement())
                    price Decimal

                    orderId Int
                    order Order @relation(fields: [orderId], references: [id])
                }
                model Order {
                    id Int @id @default(autoincrement())
                    total Decimal
                    lineItems LineItem[]
                }
                `,
                {
                    provider: 'postgresql',
                    dbUrl,
                    enhancements: ['omit', 'delegate'],
                }
            );

            prisma = r.prisma;
            const db = r.enhance();

            const create = await db.Order.create({
                data: {
                    total: new Decimal(100_100.99),
                    lineItems: { create: [{ price: 90_000.66 }, { price: 20_100.33 }] },
                },
            });

            const order = await db.Order.findFirst({ where: { id: create.id }, include: { lineItems: true } });
            expect(Decimal.isDecimal(order.total)).toBe(true);
            expect(order.total.toString()).toEqual('100100.99');
            order.lineItems.forEach((item: any) => {
                expect(Decimal.isDecimal(item.price)).toBe(true);
                expect(item.price.toString()).not.toEqual('[object Object]');
            });
        } finally {
            if (prisma) {
                await prisma.$disconnect();
            }
            await dropPostgresDb('new-issue-1');
        }
    });

    it('regression2', async () => {
        const dbUrl = await createPostgresDb('new-issue-2');
        let prisma: any;

        try {
            const r = await loadSchema(
                `
                model LineItem {
                    id Int @id @default(autoincrement())
                    price Decimal

                    orderId Int
                    order Order @relation(fields: [orderId], references: [id])
                }
                model Order extends BaseType {
                    total Decimal
                    lineItems LineItem[]
                }
                model BaseType {
                    id Int @id @default(autoincrement())
                    entityType String

                    @@delegate(entityType)
                }
                `,
                {
                    provider: 'postgresql',
                    dbUrl,
                    enhancements: ['omit', 'delegate'],
                }
            );

            prisma = r.prisma;
            const db = r.enhance();

            const create = await db.Order.create({
                data: {
                    total: new Decimal(100_100.99),
                    lineItems: { create: [{ price: 90_000.66 }, { price: 20_100.33 }] },
                },
            });

            const order = await db.Order.findFirst({ where: { id: create.id }, include: { lineItems: true } });
            expect(Decimal.isDecimal(order.total)).toBe(true); // FAILS!
            expect(order.total.toString()).toEqual('100100.99'); // FAILS!
            order.lineItems.forEach((item: any) => {
                expect(Decimal.isDecimal(item.price)).toBe(true); // FAILS!
                expect(item.price.toString()).not.toEqual('[object Object]'); // ALSO FAILS!
            });

            const lineItems = await db.LineItem.findMany();
            lineItems.forEach((item: any) => {
                expect(Decimal.isDecimal(item.price)).toBe(true); // ALSO FAILS!
                expect(item.price.toString()).not.toEqual('[object Object]'); // ALSO FAILS!
            });
        } finally {
            if (prisma) {
                await prisma.$disconnect();
            }
            await dropPostgresDb('new-issue-2');
        }
    });
});

That the Order model exhibits the behavior made sense once I narrowed this down to polymorphic models. However, seeing the issue again when querying LineItem was definitely unexpected.

Environment (please complete the following information):

  • zenstack 2.1.2
  • @zenstackhq/runtime 2.1.2
  • Prisma version: 5.15.0
  • Database type: Postgresql

Additional context
Add any other context about the problem here.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions