SelectoMix.Introspector behaviour (selecto_mix v0.4.7)

Facade for introspecting different schema sources.

This is a plain dispatch facade (not an Elixir defprotocol/@behaviour pair) that provides a single introspect/2 entry point over several explicit backends, dispatching on the shape of source:

  • Ecto schema modules (atoms) - delegates to SelectoMix.Introspector.Ecto, which reads Ecto's __schema__/1 callbacks
  • {:db, adapter, connection, table_name, opts} tuples - delegates to the given adapter module's introspect_table/3 (any selecto_db_* adapter satisfying that contract; see AdapterResolver), then normalizes the result (table/schema/source/primary-key defaults)
  • {:postgrex, connection, table_name} / {:postgrex, connection, table_name, schema} tuples - delegates to SelectoMix.Introspector.Postgres, which queries Postgrex/PostgreSQL system catalogs directly
  • Future: Other database types (MySQL, SQLite, etc.)

The @callback below documents the standardized introspect/2 contract that this facade's own dispatch honors; no module is required to declare @behaviour SelectoMix.Introspector since dispatch here is a plain case on the shape of source, not dynamic behaviour invocation.

Usage

# Ecto schema
{:ok, metadata} = SelectoMix.Introspector.introspect(MyApp.User, [])

# Postgrex connection
{:ok, conn} = Postgrex.start_link(...)
{:ok, metadata} = SelectoMix.Introspector.introspect(
  {:postgrex, conn, "users"},
  []
)

Metadata Format

All introspectors return a standardized metadata map:

%{
  table_name: "users",
  schema: "public",
  fields: [:id, :name, :email, :inserted_at, :updated_at],
  field_types: %{
    id: :integer,
    name: :string,
    email: :string,
    inserted_at: :naive_datetime,
    updated_at: :naive_datetime
  },
  primary_key: :id,  # or [:id, :tenant_id] for composite
  associations: %{
    posts: %{
      type: :has_many,
      ...
    },
    profile: %{
      type: :has_one,
      ...
    }
  },
  columns: %{
    id: %{type: :integer, nullable: false, ...},
    name: %{type: :string, nullable: false, ...},
    ...
  },
  source: :ecto  # or :postgres, :mysql, etc.
}

Summary

Callbacks

Introspect a schema source and return standardized metadata.

Functions

Introspects source, dispatching to the appropriate backend based on its shape (Ecto module, {:db, ...} adapter tuple, or {:postgrex, ...} tuple).

Types

metadata()

@type metadata() :: %{
  table_name: String.t(),
  schema: String.t(),
  fields: [atom()],
  field_types: %{required(atom()) => atom()},
  primary_key: atom() | [atom()] | nil,
  associations: %{required(atom()) => map()},
  columns: %{required(atom()) => map()},
  source: :ecto | :postgres | atom()
}

opts()

@type opts() :: keyword()

source()

@type source() ::
  module()
  | {:db, module(), term(), table_name :: String.t()}
  | {:db, module(), term(), table_name :: String.t(), keyword()}
  | {:postgrex, pid() | atom(), table_name :: String.t()}
  | {:postgrex, pid() | atom(), table_name :: String.t(), schema :: String.t()}

Callbacks

introspect(source, opts)

@callback introspect(source(), opts()) :: {:ok, metadata()} | {:error, term()}

Introspect a schema source and return standardized metadata.

Parameters

  • source - Schema source (Ecto module, Postgrex connection tuple, etc.)
  • opts - Options passed to the specific introspector

Returns

  • {:ok, metadata} - Standardized metadata map
  • {:error, reason} - Error details

Functions

introspect(source, opts \\ [])

Introspects source, dispatching to the appropriate backend based on its shape (Ecto module, {:db, ...} adapter tuple, or {:postgrex, ...} tuple).