PhoenixKitSync.SchemaInspector (PhoenixKitSync v0.1.2)

Copy Markdown View Source

Inspects database schema for DB Sync module.

Discovers available tables, their columns, and metadata. Uses PostgreSQL information_schema for introspection.

Security Considerations

  • Only returns tables in the public schema by default
  • System tables (pg_*, information_schema) are excluded
  • Admin can configure allowed/blocked tables (future feature)

Example

iex> SchemaInspector.list_tables()
{:ok, [
  %{name: "users", estimated_count: 150},
  %{name: "posts", estimated_count: 1200},
  ...
]}

iex> SchemaInspector.get_schema("users")
{:ok, %{
  table: "users",
  columns: [
    %{name: "id", type: "bigint", nullable: false, primary_key: true},
    %{name: "email", type: "character varying", nullable: false},
    ...
  ],
  primary_key: ["id"]
}}

Summary

Functions

Creates a table based on a schema definition from another database.

Returns a map of table_name => list of referenced table names (FK dependencies). Only includes tables in the public schema.

Returns FK column details for a table: list of %{column, referenced_table, referenced_column}.

Gets the exact count of records in a local table.

Gets the primary key columns for a table.

Gets the schema (columns, types, constraints) for a specific table.

Returns a content checksum for a table (MD5 hash of all rows cast to text). Used to detect actual data differences beyond row count.

Returns unique constraint columns for a table (excluding primary key). Used to match records by unique fields (e.g., match users by email). Returns list of lists (each inner list is a set of columns forming a unique constraint).

Lists all available tables with row counts.

Checks if a table exists.

Validates a PostgreSQL identifier (table or column name).

Functions

create_table(table_name, schema_def, opts \\ [])

@spec create_table(String.t(), map(), keyword()) :: :ok | {:error, any()}

Creates a table based on a schema definition from another database.

Used by DB Sync to create tables that exist on sender but not on receiver.

Parameters

  • table_name - Name of the table to create
  • schema_def - Schema definition map with columns and primary_key

Example

schema_def = %{
  "columns" => [
    %{"name" => "id", "type" => "bigint", "nullable" => false, "primary_key" => true},
    %{"name" => "name", "type" => "character varying", "nullable" => true}
  ],
  "primary_key" => ["id"]
}
SchemaInspector.create_table("users", schema_def)

get_all_foreign_keys(opts \\ [])

@spec get_all_foreign_keys(keyword()) :: {:ok, map()} | {:error, any()}

Returns a map of table_name => list of referenced table names (FK dependencies). Only includes tables in the public schema.

get_foreign_key_columns(table_name, opts \\ [])

@spec get_foreign_key_columns(
  String.t(),
  keyword()
) :: {:ok, [map()]} | {:error, any()}

Returns FK column details for a table: list of %{column, referenced_table, referenced_column}.

get_local_count(table_name, opts \\ [])

@spec get_local_count(
  String.t(),
  keyword()
) :: {:ok, non_neg_integer()} | {:error, any()}

Gets the exact count of records in a local table.

This is used by the receiver to compare local vs sender record counts.

get_primary_key(table_name, opts \\ [])

@spec get_primary_key(
  String.t(),
  keyword()
) :: {:ok, [String.t()]} | {:error, any()}

Gets the primary key columns for a table.

get_schema(table_name, opts \\ [])

@spec get_schema(
  String.t(),
  keyword()
) :: {:ok, map()} | {:error, any()}

Gets the schema (columns, types, constraints) for a specific table.

Returns

  • {:ok, schema} - Map with table info and columns
  • {:error, :not_found} - Table doesn't exist
  • {:error, reason} - Database error

get_table_checksum(table_name, opts \\ [])

@spec get_table_checksum(
  String.t(),
  keyword()
) :: {:ok, String.t()} | {:error, any()}

Returns a content checksum for a table (MD5 hash of all rows cast to text). Used to detect actual data differences beyond row count.

Skips checksumming for tables with more than max_rows rows (default: 10_000) to avoid expensive full-table scans. Returns {:ok, :too_large} in that case.

get_unique_columns(table_name, opts \\ [])

@spec get_unique_columns(
  String.t(),
  keyword()
) :: {:ok, [[String.t()]]} | {:error, any()}

Returns unique constraint columns for a table (excluding primary key). Used to match records by unique fields (e.g., match users by email). Returns list of lists (each inner list is a set of columns forming a unique constraint).

list_tables(opts \\ [])

@spec list_tables(keyword()) :: {:ok, [map()]} | {:error, any()}

Lists all available tables with row counts.

Returns tables from the public schema, excluding system tables and security-sensitive tables.

Options

  • :include_phoenix_kit - Include phoenixkit* tables (default: true)
  • :schema - Database schema to inspect (default: "public")
  • :exact_counts - Use exact COUNT(*) instead of pg_stat estimates (default: true)

table_exists?(table_name, opts \\ [])

@spec table_exists?(
  String.t(),
  keyword()
) :: boolean()

Checks if a table exists.

valid_identifier?(name)

@spec valid_identifier?(term()) :: boolean()

Validates a PostgreSQL identifier (table or column name).

Only accepts names that start with a letter or underscore and contain only alphanumerics and underscores. Anything else — including SQL metacharacters, whitespace, or quotes — is rejected.

Use this to guard any dynamic identifier that will be interpolated into raw SQL, before quoting it with double quotes.