# Migrations

{% hint style="info" %}
This section is based on [Jennifer Docs](https://github.com/imdrasil/jennifer.cr/blob/master/docs/index.md).
{% endhint %}

To generate a migration run `crystal src/sam.cr generate:migration your_migration_name`

## Migration DSL
The generator will create template file for you with a consistent name pattern **timestamp\_migration\_name.cr**. 
The empty file looks like this:

```crystal
class YourCamelcasedMigrationName20170119011451314 < Jennifer::Migration::Base
  def up
  end

  def down
  end
end
```

The `up` method is where your database changes go, whereas, `down` is used for reverting your changes back.

Example for creating table:

```crystal
create_table(:addresses) do |t|
  t.reference :contact # creates field contact_id with Int type and allows null values
  t.string :street, {:size => 20, :sql_type => "char"} # creates string field with CHAR(20) db type
  t.bool :main, {:default => false} # sets false as default value
end
```

## Data Types and Mappings

| internal alias | PostgreSQL | MySql | Crystal type |
| :--- | :--- | :--- | :--- |
| `#integer` | `int` | `int` | `Int32` |
| `#string` | `varchar(254)` | `varchar(254)` | `String` |
| `#bool` | `boolean` | `bool` | `Bool` |
| `#char` | `char` | - | `String` |
| `#float` | `real` | `float` | `Float32` |
| `#double` | `double precision` | `double` | `Float64` |
| `#short` | `smallint` | `smallint` | `Int16` |
| `#timestamp` | `timestamp` | `timestamp` | `Time` |
| `#date_time` | `datetime` | `datetime` | `Time` |
| `#blob` | `blob` | `blob` | `Bytes` |
| `#var_string` | `varchar(254)` | `varstring` | `String` |
| `#json` | `json` | `json` | `JSON::Any` |
| `#enum` | `enum` | `enum` | `String` |

Also if you use PostgreSQL array types are available a well: `Array(Int32)`, `Array(Char)`, `Array(Float32)`, `Array(Float64)`,`Array(Int16)`, `Array(Int32)`, `Array(Int64)`, `Array(String)`.

All of them accepts additional options:

* `:sql_type`- gets exact \(except size\) field type;
* `:null`- represent nullable if field \(by default is`false`for all types and field\);
* `:primary`- marks field as primary key field \(could be several ones but this provides some bugs with query generation for such model - for now try to avoid this\).
* `:default`- default value for field
* `:auto_increment`- marks field to use auto increment \(properly works only with`Int32`fields, another crystal types have cut functionality for it\);
* `:array`- mark field to be array type \(Postgres only\)

Also there is`#field`method which allows to directly define sql type \(very suitable for enums in Postgres\).

To drop table just write

```crystal
drop_table(:addresses) # drops if exists
```

To alter existing table use next methods:

* `#change_column(name, [new_name], options)`- to change column definition; Postgres has slighly another implementation of this than mysql one - check source code for details;
* `#add_column(name, type, options)`- add new column;
* `#drop_column(name)`- drops existing column
* `#add_index(name : String, field : Symbol, type : Symbol, order : Symbol?, length : Int32?)`- adds new index \(Postgres doesn't support length parameter and only support`:unique`type\);
* `#drop_index(name : String)`- drops existing index;
* `#rename_table(new_name)`- renames table.

Also next support methods are available:

* `#table_exists?(name)`
* `#index_exists?(table, name)`
* `#column_exists?(table, name)`
* `#data_type_exists?(name)`for Postgres ENUM

Also plain SQL could be executed as well:

```crystal
execute("ALTER TABLE addresses CHANGE street st VARCHAR(20)")
```

All changes are executed one by one so you also could add data changes here \(in`up`method\) but if execution of`up`method fails -`down`method will be called and all process will stop - be ready for such behavior.

To be sure that your db is up to date before run tests of your application, add:

```text
Jennifer::Migration::Runner.migrate
```

## Enum

Now enums are supported as well but it has different implementation for adapters. For mysql is enough just write down all values:

```crystal
create_table(:contacts) do |t|
  t.enum(:gender, values: ["male", "female"])
end
```

Postgres provide much more flexible and complex behavior. Using it you need to create it firstly:

```crystal
create_enum(:gender_enum, ["male", "female"])
create_table(:contacts) do |t|
  t.string :name, {:size => 30}
  t.integer :age
  t.field :gender, :gender_enum
  t.timestamps
end
change_enum(:gender_enum, {:add_values => ["unknown"]})
change_enum(:gender_enum, {:rename_values => ["unknown", "other"]})
change_enum(:gender_enum, {:remove_values => ["other"]})
```

For more details check source code and PostgreSQL docs.