uptrace/bun

Unexpected Data Truncation in CTE with Type Casting During Bulk Update

Open

#1,093 创建于 2025年1月1日

在 GitHub 查看
 (7 评论) (0 反应) (1 负责人)Go (4,826 star) (279 fork)user submission
enhancementhelp wanted

描述

Hello,

I have encountered an issue with data truncation caused by type casting for a bulk update with CTEs. Below is a minimal reproducible example and the SQL query generated by the ORM (this code is in internal/dbtest/pg_test.go ):

func TestBulkUpdate(t *testing.T) {
    type Model struct {
        ID   int64
        Name string `bun:",type:varchar(3)"`
    }

    db := pg(t)
    t.Cleanup(func() { db.Close() })

    mustResetModel(t, ctx, db, (*Model)(nil))

    models := []*Model{
        {ID: 1, Name: "foo"},
        {ID: 2, Name: "bar"},
    }

    _, err := db.NewInsert().Model(&models).Exec(ctx)
    require.NoError(t, err)

    models[0].Name = "abcd"

    res, err := db.NewUpdate().
        With("_data", db.NewValues(&models)).
        Model((*Model)(nil)).
        TableExpr("_data").
        Set("name = _data.name").
        Where("model.id = _data.id").
        Exec(ctx)
    require.NoError(t, err) // pass

    require.Equal(t, models[0].Name, updatedModels[0].Name) // failed (expected: abcd, actual: abc)
}

generated sql:

WITH "_data" ("id", "name") AS (
    VALUES (1::BIGINT, 'abcd'::varchar(3)), (2::BIGINT, 'bar'::varchar(3))
) 
UPDATE "models" AS "model" 
SET name = _data.name 
FROM _data 
WHERE (model.id = _data.id)

The issue occurs because of the type casting in the VALUES clause ('abcd'::varchar(3)), which results in data truncation ('abcd' becomes 'abc'). This behavior seems problematic for the following reasons:

  • The ORM silently truncates data without throwing any error or warning.
  • This could lead to unexpected data loss, especially if the developer is unaware of the database type constraints.
  • It does not align with the general expectation of ORMs preventing silent data corruption.

Could the ORM be enhanced to either:

  • Avoid type casting in CTEs when it's not strictly necessary?
  • Add a configuration option to handle this behavior explicitly?

Thank you for your time and consideration. Let me know if you need any further details or clarifications.

贡献者指南