Skip to content

Bug: RepoDb.SqlServer truncates the content of models' string primary key fields when executing an overload of Insert[All] #1204

@claudiaw797

Description

@claudiaw797

Bug Description

Preconditions
  • A table with a primary key field of type [N][VAR]CHAR and a length exceeding 30 characters.
  • Models to insert into the table, containing ID values that also exceed this limit.
Faulty behavior

After executing any overload of SqlConnection.Insert[All][Async], the models' field values are truncated to 30 characters.

Reason

Internally, the library executes a query containing SELECT CONVERT(NVARCHAR, @IdField) and assigns the result to the model's ID field.
Since the default length for [N][VAR]CHAR when casting or converting is 30, characters beyond that are cut off.

https://learn.microsoft.com/en-us/sql/t-sql/data-types/char-and-varchar-transact-sql?view=sql-server-ver16

"If n isn't specified when using the CAST and CONVERT functions, the default length is 30."

https://learn.microsoft.com/en-us/sql/t-sql/data-types/nchar-and-nvarchar-transact-sql?view=sql-server-ver16

"When n isn't specified with the CAST function, the default length is 30."

Even though the latter quote does not mention CONVERT, the same behavior can be observed for all four data types.

Circumvention

Perform any required setup before inserting the models.
The bug only has an impact if the models are reused after the insert operation.

Proposed simple solution for variable-size string data

Add MAX as the string size for [N]VARCHAR, changing three lines in RepoDb.Resolvers.DbTypeToSqlServerStringNameResolver.Resolve(DbType):

  • DbType.String => "NVARCHAR(MAX)",
  • DbType.AnsiString => "VARCHAR(MAX)",
  • _ => "NVARCHAR(MAX)",

This cannot be applied to [N]CHAR, as MAX is not allowed for fixed-size string data.

Proposed solution

Set the string size in the above locations based on the database schema and/or the model's [Size(n)] attribute instead of using MAX.

RepoDb.DbField.Size currently reflects the value from [sys].[columns].[max_length] (which is twice the string length; the correct value could be retrieved from [INFORMATION_SCHEMA].[COLUMNS].[CHARACTER_MAXIMUM_LENGTH]).
However, this value is dismissed on the way to constructing the insert statement.

Schema and Model:

CREATE TABLE [dbo].[NVarCharTable] (
    [Id] NVARCHAR(36)    NOT NULL,
    [Value] NVARCHAR(36) NULL,
    CONSTRAINT [PK_NVarCharTable] PRIMARY KEY CLUSTERED
    (
        [Id] ASC
    )
) ON [PRIMARY]
public record StringPkTable
{
    //[Size(36)]
    public string Id { get; init; } = string.Empty;
    public string Value { get; init; } = string.Empty;

    public int IdLength => Id.Length;
    public int ValueLength => Value.Length;
}

Library Version:
RepoDb v1.13.1 and RepoDb.SqlServer v1.13.1

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions