SeaSchema

repository·master·Indexed 42 days ago

https://github.com/seaql/sea-schema

A Rust library for managing, representing, and discovering SQL database schemas for MySQL, Postgres, and SQLite. It provides type definitions that map database schemas to native structures and utilities to connect to live databases for schema discovery. Version 0.18.1.

Tokens
18.1K
Snippets
63
Records
82
Agent score
53%

What's inside SeaSchema

  1. What is SeaSchema

    master

    SeaSchema is a Rust library designed for managing database schemas across MySQL, Postgres, and SQLite. It serves two primary purposes:

    1. Schema Representation: It provides type definitions that map database schemas closely to their native structures.
    2. Schema Discovery: It provides utilities to connect to live databases and discover their existing schemas.

    You can enable JSON serialization/deserialization for the type definitions by enabling the with-serde feature.

  2. Understand the SeaSchema architecture

    master

    SeaSchema is organized into several specialized modules:

    • def: Contains the core type definitions for representing database schemas.
    • query & parser: Handles querying and parsing information_schema data.
    • discovery: Provides the logic to connect to a live database and discover a Schema object.
    • writer: Enables exporting a Schema into SeaQuery objects or raw SQL statements.

    If you need to serialize or deserialize schema definitions to/from JSON, ensure you enable the with-serde feature in your Cargo.toml.

  3. Use sea-schema-sync for database schema synchronization

    master
    The sea-schema-sync crate provides modules for discovering and synchronizing database schemas across different database engines. It supports mysql, postgres, and sqlite via feature flags. The crate re-exports sea_query for query building and provides specialized modules for connection management and name handling.
  4. Use TableDef for SQLite schema discovery

    master

    The TableDef struct is the primary representation of a SQLite table in sea-schema. It holds metadata about the table's name, columns, foreign keys, indexes, and constraints. You can use it to discover the existing schema of a SQLite database by calling its discovery methods against a Connection.

    Key fields in TableDef:

    • name: The table name.
    • columns: A list of ColumnInfo objects.
    • foreign_keys: A list of ForeignKeysInfo objects.
    • indexes: A list of IndexInfo objects.
    • constraints: A list of IndexInfo objects representing UNIQUE and PRIMARY KEY constraints.
    • auto_increment: A boolean indicating if the primary key is set to autoincrement.
    use sea_schema::sqlite::TableDef;
    
    // Assuming 'conn' is an active Connection to a SQLite database
    let mut table_def = TableDef { 
        name: "users".to_string(), 
        ..Default::default() 
    };
    
    // Discover columns
    table_def.get_column_info(&conn).await?;
    
    // Discover foreign keys
    table_def.get_foreign_keys(&conn).await?;
    
    // Discover indexes
    table_def.get_indexes(&conn).await?;
    
    // Discover constraints (UNIQUE/PK)
    table_def.get_constraints(&conn).await?;
    
    // Check for autoincrement
    table_def.pk_is_autoincrement(&conn).await?;
  5. Use TableDef to discover SQLite schema

    master

    The TableDef struct is used to represent and discover the schema of an existing SQLite table. You can use it to inspect columns, indexes, foreign keys, and constraints by calling discovery methods that interact with a Connection.

    To fully populate a TableDef from an existing database, you typically need to call several discovery methods in sequence:

    1. get_column_info(conn): Retrieves column names, types, and nullability.
    2. get_foreign_keys(conn): Retrieves foreign key relationships.
    3. get_indexes(conn): Retrieves standard indexes.
    4. get_constraints(conn): Retrieves UNIQUE and PRIMARY KEY constraints.
    5. pk_is_autoincrement(conn): Specifically checks if the primary key is set to autoincrement.

    Note that many of these methods return a DiscoveryResult<&mut Self>, allowing for method chaining.

    // Example workflow for discovering a table's schema
    let mut table_def = TableDef { 
        name: "users".to_string(), 
        ..Default::default() 
    };
    
    // Chain discovery methods
    table_def
        .get_column_info(&conn)?
        .get_foreign_keys(&conn)?
        .get_indexes(&conn)?
        .get_constraints(&conn)?
        .pk_is_autoincrement(&conn)?;
  6. Example of discovered schema structure

    master

    When using the discovery module, SeaSchema produces a structured representation of your database tables. For a MySQL table like film_actor, the discovered TableDef includes detailed information about the table's engine, character set, columns (including types, nullability, and primary key status), indexes, and foreign key constraints.

    TableDef {
        info: TableInfo {
            name: "film_actor",
            engine: InnoDb,
            auto_increment: None,
            char_set: Utf8Mb4,
            collation: Utf8Mb40900AiCi,
            comment: "",
        },
        columns: [
            ColumnInfo {
                name: "actor_id",
                col_type: SmallInt(
                    NumericAttr {
                        maximum: None,
                        decimal: None,
                        unsigned: Some(true),
                        zero_fill: None,
                    },
                ),
                null: false,
                key: Primary,
                default: None,
                extra: ColumnExtra {
                    auto_increment: false,
                    on_update_current_timestamp: false,
                    generated: false,
                    default_generated: false,
                },
                expression: None,
                comment: "",
            },
            // ... other columns
        ],
        indexes: [
            // ... index definitions
        ],
        foreign_keys: [
            // ... foreign key definitions
        ],
    }
  7. Parse PostgreSQL types from strings with `Type::from_str`

    master

    Use Type::from_str to convert PostgreSQL type names (as strings) into the Type enum. This is useful when discovering schema information from the database.

    Parameters:

    • column_type: The name of the column type (e.g., `
  8. Represent SQLite index information with IndexInfo

    master

    The IndexInfo struct captures metadata for an SQLite index, typically derived from PRAGMA index_list(table_name) and SELECT * FROM sqlite_master. It contains the index type, name, table name, uniqueness, origin, and the list of columns included in the index.

    You can use the write() method to convert an IndexInfo instance into a sea_query::IndexCreateStatement, which can then be used to generate SQL to recreate the index.

    // Convert discovered index info into a SeaQuery statement
    let create_statement = index_info.write();
  9. Map SQLite hidden field to ColumnVisibility

    master

    When inspecting SQLite tables via PRAGMA table_xinfo, the hidden field indicates the visibility or nature of a column. The ColumnVisibility enum maps these integer values to meaningful states:

    ValueColumnVisibilityDescription
    0VisibleOrdinary column
    1HiddenVirtualHidden column in a virtual table
    2GeneratedVirtualGenerated VIRTUAL column
    3GeneratedStoredGenerated STORED column

    Use ColumnVisibility::from_hidden(value: i8) to perform this conversion.

    let visibility = ColumnVisibility::from_hidden(2); // Returns ColumnVisibility::GeneratedVirtual
  10. Check for type attributes with Type helper methods

    master

    The Type enum provides several helper methods to quickly check if a type possesses specific attributes (like precision or length). This is useful for determining how to handle or serialize data.

    • has_numeric_attr(): Returns true for Numeric or Decimal.
    • has_string_attr(): Returns true for Varchar or Char.
    • has_time_attr(): Returns true for Timestamp, TimestampWithTimeZone, Time, or TimeWithTimeZone.
    • has_interval_attr(): Returns true for Interval.
    • has_bit_attr(): Returns true for Bit or VarBit.
    • has_enum_attr(): Returns true for Enum.
    • has_array_attr(): Returns true for Array.
    • has_vector_attr(): Returns true for Vector (Requires postgres-vector feature).