Numeric casts round instead of truncating, `(byte)` casts don't wrap, and `BigInteger` division isn't integer division

Abierto
#4,095 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
3/5
Tiempo estimado
1-2 días
Aptitud para principiantes
70/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
csharp, postgresql
Área
database

Línea de trabajo

Start in NpgsqlQuerySqlGenerator.cs (L603-626) where integer casts render as ::int; switch to trunc(x)::int to match .NET truncation. Then fix NpgsqlConvertTranslator.cs L36 and NpgsqlBigIntegerTypeMapping.cs L30 (use div for division). Run the issue's repro to confirm all four LINQ queries return identical .NET and database results.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

Environment
PostgreSQL 18.6 (postgres:18), session TimeZone Etc/UTC
.NET 10 runtime 10.0.11, Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3, EF Core 10.0.4
.NET 11 runtime 11.0.0-rc.2, Npgsql.EntityFrameworkCore.PostgreSQL 11.0.0-rc.2, EF Core 11.0.0-rc.2
Also Npgsql 10.0.3 on both lines, macOS arm64

Both lines print the same output. main at 150011d has the same code.

LINQ Row .NET PostgreSQL SQL
(int)r.Decimal 2.7m 2 3 r."Decimal"::int
(long)r.Double 2.7 2 3 r."Double"::bigint
(byte)r.Int == 249 −7 match no match r."Int"::smallint = 249
r.Big / r.Big2 == 3 7 / 2 match no match r."Big" / r."Big2" = 3
Repro
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System.Numerics;

await using var context = new TestContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();

context.Rows.Add(new Row { Decimal = 2.7m, Double = 2.7, Int = -7, Big = 7, Big2 = 2 });
await context.SaveChangesAsync();
var row = await context.Rows.AsNoTracking().SingleAsync();

// 1. C# casts truncate toward zero; ::int rounds
Console.WriteLine($"(int)2.7m: .NET {(int)row.Decimal}, database {await context.Rows.Select(r => (int)r.Decimal).SingleAsync()}");
Console.WriteLine($"(long)2.7: .NET {(long)row.Double}, database {await context.Rows.Select(r => (long)r.Double).SingleAsync()}");

// 2. An unchecked byte cast wraps; ::smallint keeps -7
Console.WriteLine($"(byte)-7 == 249: .NET {((byte)row.Int == 249 ? 1 : 0)}, database {await context.Rows.CountAsync(r => (byte)r.Int == 249)}");

// 3. BigInteger division is integer division
Console.WriteLine($"Big / Big2 == 3 (7 / 2): .NET {(row.Big / row.Big2 == 3 ? 1 : 0)}, database {await context.Rows.CountAsync(r => r.Big / r.Big2 == 3)}");

public class TestContext : DbContext
{
    public DbSet<Row> Rows => Set<Row>();

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseNpgsql(Environment.GetEnvironmentVariable("Test__Npgsql__DefaultConnection"), o => o.UseNodaTime())
            .LogTo(Console.WriteLine, LogLevel.Information)
            .EnableSensitiveDataLogging();
}

public class Row
{
    public int Id { get; set; }
    public decimal Decimal { get; set; }
    public double Double { get; set; }
    public int Int { get; set; }
    public BigInteger Big { get; set; }
    public BigInteger Big2 { get; set; }
}

Output, the same on both lines:

(int)2.7m: .NET 2, database 3
(long)2.7: .NET 2, database 3
(byte)-7 == 249: .NET 1, database 0
Big / Big2 == 3 (7 / 2): .NET 1, database 0
  • Casts. C# casts from decimal, double and float to an integer type truncate toward zero. PostgreSQL's casts round: half away from zero for numeric, half to even for float8. SQL Server's CAST truncates, so this is specific to this provider. The cast is rendered as ::int (NpgsqlQuerySqlGenerator.cs#L603-L626); trunc(x)::int would match .NET. Convert.ToInt32(decimal) goes the same way (NpgsqlConvertTranslator.cs#L36) and rounds half away from zero, where .NET rounds half to even.
  • byte maps to smallint, so an unchecked (byte) cast keeps −7 instead of wrapping to 249.
  • BigInteger maps to numeric (NpgsqlBigIntegerTypeMapping.cs#L30), so / is numeric division (3.5). div(a, b) matches BigInteger's truncating division.
Lenguaje dominante
C#
Estrellas
1.8k
Forks
268
Merge medio
2 h 35 min
PR fusionados (30 d)
136

Preparar el entorno

Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de npgsql/efcore.pg

Todos los issues de npgsql/efcore.pg

Issues similares

Más issues de C#

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.