Skip to content

Add opt-in domain-based CLR type mapping (boolean/guid)#1267

Open
sencagri wants to merge 1 commit intoFirebirdSQL:masterfrom
sencagri:feature/domain-type-mapping
Open

Add opt-in domain-based CLR type mapping (boolean/guid)#1267
sencagri wants to merge 1 commit intoFirebirdSQL:masterfrom
sencagri:feature/domain-type-mapping

Conversation

@sencagri
Copy link
Copy Markdown

@sencagri sencagri commented May 5, 2026

PR review for #1266 (proposal). Restores IBProvider-style behaviour for codebases that store booleans as SMALLINT and guids as CHAR(16) OCTETS behind named domains.

Connection string keys (both default to empty - feature is fully off when unset; no extra catalog query, no DbField allocation change):

boolean domains = D_BOOL%,IS_%
guid domains = D_GUID%

Patterns use SQL LIKE syntax. Matching is case-insensitive against RDB$FIELD_SOURCE; system domains (RDB$*) are never matched.

Design

  • RawDbDataType vs DbDataType. DbField gains RawDbDataType (the actual SQL/wire type, computed from sqltype/subtype/length/charset as before) and OverrideDataType (CLR-level reporting only). DbDataType returns the override when set, otherwise RawDbDataType. All wire serialisation paths (GdsStatement.WriteRawParameter / ReadRawValue, XsqldaMarshaler, DbValue.GetBytes, DbField.FixNull) explicitly use RawDbDataType so the override cannot leak into the protocol layer.

  • DomainNameResolver. Per-FbConnectionInternal cache of (relation, field) -> domain name. Populated lazily on Prepare via a single SELECT on RDB$RELATION_FIELDS (one round-trip per batch of unseen columns). Guarded by an _isResolving flag so its own internal query cannot recurse. Negative entries are cached so a missing or failed lookup does not retry every prepare. Fetch failures are swallowed (best-effort): an opt-in feature must never break user's normal queries.

  • Parameter binding. FbCommand.NormalizeDomainParameterValue converts bool inputs to the underlying numeric wire type (SmallInt/Integer/ BigInt/Numeric/Decimal) using RawDbDataType, so callers can pass true or false to a SMALLINT-backed boolean column directly. Guid handling reuses the provider's existing CHAR(16) OCTETS <-> Guid path.

  • DbValue.GetBoolean. Tightened from Convert.ToBoolean to explicit short/int/long/decimal -> bool with a fallback, so reading a SMALLINT with a Boolean override is safe and culture-independent.

Opt-out path

When neither connection-string key is set,
ConnectionString.HasDomainTypeMappings is false and FbCommand.Prepare returns before touching the resolver. RDB$RELATION_FIELDS is never queried, no per-field state changes, behaviour is identical to master.

Tests

  • DomainPatternListTests - 12 unit tests for LIKE pattern parsing (escape handling, case-insensitivity, RDB$ exclusion, edge cases).
  • DomainTypeMappingTests - 12 integration tests covering schema-level reporting, parameter round-trip (bool -> SMALLINT -> bool), guid round-trip, NULL handling, non-matching patterns staying as raw types, DataAdapter round-trip, GetSchemaTable, and resolver caching / re-entry.
  • ConnectionStringTests - parsing and builder coverage for the two new keys.

Compatibility

No breaking changes. Public API surface adds two properties on FbConnectionStringBuilder (BooleanDomains, GuidDomains) and is backward-compatible at the wire level.

Closes FirebirdSQL#1266 (proposal). Restores IBProvider-style behaviour for
codebases that store booleans as SMALLINT and guids as CHAR(16) OCTETS
behind named domains.

Connection string keys (both default to empty - feature is fully off
when unset; no extra catalog query, no DbField allocation change):

  boolean domains = D_BOOL%,IS\_%
  guid domains    = D_GUID%

Patterns use SQL LIKE syntax. Matching is case-insensitive against
RDB$FIELD_SOURCE; system domains (RDB$*) are never matched.

Design

* RawDbDataType vs DbDataType. DbField gains RawDbDataType (the actual
  SQL/wire type, computed from sqltype/subtype/length/charset as before)
  and OverrideDataType (CLR-level reporting only). DbDataType returns
  the override when set, otherwise RawDbDataType. All wire serialisation
  paths (GdsStatement.WriteRawParameter / ReadRawValue,
  XsqldaMarshaler, DbValue.GetBytes, DbField.FixNull) explicitly use
  RawDbDataType so the override cannot leak into the protocol layer.

* DomainNameResolver. Per-FbConnectionInternal cache of
  (relation, field) -> domain name. Populated lazily on Prepare via a
  single SELECT on RDB\$RELATION_FIELDS (one round-trip per batch of
  unseen columns). Guarded by an _isResolving flag so its own internal
  query cannot recurse. Negative entries are cached so a missing or
  failed lookup does not retry every prepare. Fetch failures are
  swallowed (best-effort): an opt-in feature must never break user's
  normal queries.

* Parameter binding. FbCommand.NormalizeDomainParameterValue converts
  bool inputs to the underlying numeric wire type (SmallInt/Integer/
  BigInt/Numeric/Decimal) using RawDbDataType, so callers can pass true
  or false to a SMALLINT-backed boolean column directly. Guid handling
  reuses the provider's existing CHAR(16) OCTETS <-> Guid path.

* DbValue.GetBoolean. Tightened from Convert.ToBoolean to explicit
  short/int/long/decimal -> bool with a fallback, so reading a SMALLINT
  with a Boolean override is safe and culture-independent.

Opt-out path

When neither connection-string key is set,
ConnectionString.HasDomainTypeMappings is false and FbCommand.Prepare
returns before touching the resolver. RDB\$RELATION_FIELDS is never
queried, no per-field state changes, behaviour is identical to master.

Tests

* DomainPatternListTests - 12 unit tests for LIKE pattern parsing
  (escape handling, case-insensitivity, RDB\$ exclusion, edge cases).
* DomainTypeMappingTests - 12 integration tests covering schema-level
  reporting, parameter round-trip (bool -> SMALLINT -> bool), guid
  round-trip, NULL handling, non-matching patterns staying as raw
  types, DataAdapter round-trip, GetSchemaTable, and resolver caching
  / re-entry.
* ConnectionStringTests - parsing and builder coverage for the two
  new keys.

Compatibility

No breaking changes. Public API surface adds two properties on
FbConnectionStringBuilder (BooleanDomains, GuidDomains) and is
backward-compatible at the wire level.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant