fix(sql-dialect): enforce Postgres/Oracle identifier ceilings in UTF-8 bytes (Driver.Sql.Dialect-001)

NAMEDATALEN-1 (63) and Oracle 12.2's 128 are byte limits in the database
encoding, not character counts, so a 32-character Cyrillic name passed
ident.Length and was then silently truncated (Postgres) or rejected by the
server (Oracle). Both dialects now measure Encoding.UTF8.GetByteCount and say
'bytes' in the rejection. SqlServerDialect deliberately keeps ident.Length —
sysname is nvarchar(128), a real character limit — pinned by a control test.
This commit is contained in:
Joseph Doherty
2026-08-10 07:58:04 -04:00
parent 94b8633e68
commit 74594e6083
6 changed files with 102 additions and 8 deletions
@@ -165,13 +165,31 @@ public class OracleDialectTests
=> Should.Throw<ArgumentException>(() => new OracleDialect().QuoteIdentifier(ident));
[Fact]
public void QuoteIdentifier_acceptsExactly128Chars_andRejects129()
public void QuoteIdentifier_acceptsExactly128Bytes_andRejects129()
{
var d = new OracleDialect();
d.QuoteIdentifier(new string('a', 128)).ShouldBe("\"" + new string('a', 128) + "\"");
Should.Throw<ArgumentException>(() => d.QuoteIdentifier(new string('a', 129)));
}
/// <summary>
/// Oracle 12.2's 128 is a <b>byte</b> ceiling in the database character set (UTF-8 on any modern
/// estate), so a 65-character Cyrillic name is 130 bytes and the server rejects it with
/// <c>ORA-00972</c>. Measuring UTF-16 code units here would pass it through to fail at the server
/// instead — for a reason this dialect could have named.
/// </summary>
[Fact]
public void QuoteIdentifier_measuresTheCeilingInUtf8Bytes_notCharacters()
{
var d = new OracleDialect();
// 64 Cyrillic characters = 128 UTF-8 bytes — exactly at the limit.
d.QuoteIdentifier(new string('б', 64)).ShouldBe("\"" + new string('б', 64) + "\"");
// 65 characters = 130 bytes. A character-counting ceiling waves this through.
Should.Throw<ArgumentException>(() => d.QuoteIdentifier(new string('б', 65)));
}
// ---- liveness + row limit ----
[Fact]