Skip to content

New code-based type handler resolution architecture #3953

@roji

Description

@roji

Our current type handler resolution works with a set of dictionaries - by NpgsqlDbType, by DbType, by CLR type, etc. Type mapping management is a matter of modifying the mapping tables, which are then consulted by our generic code which will create the handler and register it in all the handler dictionaries.

We can switch to a different, code-based approach, where a resolver would simply have code directly for resolving by NpgsqlDbType, DbType, ClrType, etc. Adding a type mapping plugin would no longer be a mapper of updating the internal mapping tables, but rather adding the plugin's resolver type to a simple list of resolver plugin which are consulted in order. If the first resolver can't resolve a given ClrType, the next resolver is tried.

Efficiency

  • A code-based resolver can do a simple switch over NpgsqlDbType, DbType, or the type. This is better than a dictionary lookup.
  • When using generic parameters (NpgsqlParameter<T>), the JIT can even elide our checks altogether - zero overhead.

Flexibility/cleanliness

We can cleanly and easily support mapping scenarios which are very difficult to do currently:

  • The new timestamp logic (Redo timestamp handling #3947) requires that the handler be chosen based on the actual value (DateTime.Kind), and not just based on the type; our current system doesn't support this (so I'm hacking it). If we just wrote resolution in code, it's much cleaner.
  • We can easily have multiple type handlers for writing the same PG type. For example, even when the NodaTime plugin is used, we still want to be able to write BCL DateTime, but that's not possible. We've hacked around this limitation in Update noda time plugin to support bcl types by forwarding these to the existing bcl type handlers. #3124 by making the NodaTime handler know about the BCL types (and forward to the BCL handler internally). With code-based resolution, the NodaTime plugin would fail to resolve DateTime, and we'd go on to resolve it from the built-in resolver.
    • Unfortunately this doesn't help with this, since resolvers still need to return a single type handler for a given OID or NpgsqlDbType, without knowing the associated ClrType.
  • Connection-based management (e.g. add NodaTime but only for a specific connection) becomes much simpler and more efficient. For example, today we need to reset all mappings (and handlers) the moment a connection is returned to the pool. With code-based resolution we'd just have to reset its resolver to point at the global one (related: Speed up connection-specific mapping changes #2263)
  • Can probably do something for RangeHandler should handle all the types of its subtype's handler #3563 as well

Additional subtasks:

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions