java.lang.Object
com.darkcollective.relix.connectors.std.internal.DataSourceRegistry
All Implemented Interfaces:
ConnectionProvider

public final class DataSourceRegistry extends Object implements ConnectionProvider
Binds connection names to live JDBC DataSource handles, so a program can hand the engine a database it already has open.

The AST models a connection declaratively — a name, a connector type and a property map — and the engine's own modules cannot name java.sql at all. A live handle therefore cannot ride in the AST, and does not need to: the AST names the connection and this registry maps that name to the handle, the same shape PlanEstimates uses to sit beside a plan rather than on it.

It is a ConnectionProvider, so it drops into both places that need a connection. A name it does not hold is delegated to ConnectionProvider.FROM_URL, which is what lets one session mix injected handles with connections a script declares in the ordinary way.

Why a DataSource and not a Connection

A single Connection is not accepted, including as a convenience. The JDBC connector's row stream is lazy over a live ResultSet and holds its connection until the stream closes, so any join with both sides on one source opens two scans concurrently: one connection would either serialize or corrupt them. The failure would appear on the first join, long after the call site that chose it. A DataSource hands out as many as the query needs and does its own pooling — which is why managesPooling(com.darkcollective.relix.lang.ast.ConnectionDeclaration) is true for every registered name, and releasing closes rather than retaining.

Dialect

ConnectionDeclarations normally get their dialect from a declared dialect: or from the JDBC URL, and an injected handle has neither. It is read instead from DatabaseMetaData.getDatabaseProductName() — once per name, memoized, because it costs a connection. Getting it wrong costs pushdown, never correctness, so a probe that fails is simply no answer and the generic dialect applies.

Thread safety

Registration is expected during setup and lookup during execution; the maps are concurrent, so a late registration is safe but races with an in-flight query the way any late configuration change would.

  • Constructor Details

    • DataSourceRegistry

      public DataSourceRegistry()
      Creates an empty registry delegating unregistered names to ConnectionProvider.FROM_URL.
    • DataSourceRegistry

      public DataSourceRegistry(ConnectionProvider fallback)
      Creates an empty registry delegating unregistered names to fallback.
      Parameters:
      fallback - the provider for names this registry does not hold; must not be null
  • Method Details

    • register

      public DataSourceRegistry register(String name, DataSource dataSource)
      Binds name to dataSource, replacing any previous binding.
      Parameters:
      name - the connection name as the script or session spells it; must not be blank
      dataSource - the live handle; must not be null
      Returns:
      this registry, for chaining
    • register

      public DataSourceRegistry register(String name, DataSource dataSource, String dialect)
      Binds name to dataSource with an explicit dialect, skipping the metadata probe.
      Parameters:
      name - the connection name; must not be blank
      dataSource - the live handle; must not be null
      dialect - the dialect token (e.g. "postgres"); must not be null
      Returns:
      this registry, for chaining
    • holds

      public boolean holds(String name)
      Returns whether name is bound to a handle here.
      Parameters:
      name - the connection name
      Returns:
      whether this registry serves that name
    • connectionFor

      public Connection connectionFor(ConnectionDeclaration connection) throws SQLException
      Description copied from interface: ConnectionProvider
      Opens a connection for the given declaration.
      Specified by:
      connectionFor in interface ConnectionProvider
      Parameters:
      connection - the declared connection; must not be null
      Returns:
      an open connection the caller owns
      Throws:
      SQLException - if no connection can be opened for this declaration
    • managesPooling

      public boolean managesPooling(ConnectionDeclaration connection)
      Description copied from interface: ConnectionProvider
      Whether connections this provider returns for connection are pooled by something other than ConnectionPool, so the engine must not retain them.
      Specified by:
      managesPooling in interface ConnectionProvider
      Parameters:
      connection - the declared connection
      Returns:
      true to bypass engine-side pooling; false by default
    • dialectFor

      public Optional<String> dialectFor(String name)
      The dialect token for name: the one registered explicitly, else one read from the database's own metadata, else empty.

      The probe opens a connection, so it runs once per name and its answer — including "could not tell" — is remembered.

      Parameters:
      name - the connection name
      Returns:
      the dialect token, or empty when unknown
    • declarationFor

      public ConnectionDeclaration declarationFor(String name)
      Builds the ConnectionDeclaration that names name to the engine, carrying the resolved dialect so the planner reaches the same answer it would for a declared connection.

      It carries no url: there is none, and that is the case the ConnectionProvider seam exists to make representable.

      Parameters:
      name - the connection name; must be registered
      Returns:
      the declaration to install in a session
      Throws:
      IllegalArgumentException - if no handle is bound to that name