Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion lib/ecto/adapters/myxql/connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -995,7 +995,9 @@ if Code.ensure_loaded?(MyXQL) do

[
[
"CREATE TABLE ",
"CREATE ",
modifiers_expr(table.modifiers),
"TABLE ",
if_do(command == :create_if_not_exists, "IF NOT EXISTS "),
quote_table(table.prefix, table.name),
table_structure,
Expand Down Expand Up @@ -1376,6 +1378,10 @@ if Code.ensure_loaded?(MyXQL) do
defp engine_expr(storage_engine),
do: [" ENGINE = ", String.upcase(to_string(storage_engine || "INNODB"))]

defp modifiers_expr(nil), do: []
defp modifiers_expr(modifiers) when is_binary(modifiers), do: [modifiers, ?\s]
defp modifiers_expr(other), do: error!(nil, "MySQL adapter expects :modifiers to be a string or nil, got #{inspect(other)}")

defp options_expr(nil),
do: []

Expand Down
8 changes: 7 additions & 1 deletion lib/ecto/adapters/postgres/connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1269,7 +1269,9 @@ if Code.ensure_loaded?(Postgrex) do
table_name = quote_name(table.prefix, table.name)

query = [
"CREATE TABLE ",
"CREATE ",
modifiers_expr(table.modifiers),
"TABLE ",
if_do(command == :create_if_not_exists, "IF NOT EXISTS "),
table_name,
?\s,
Expand Down Expand Up @@ -1760,6 +1762,10 @@ if Code.ensure_loaded?(Postgrex) do
defp include_expr(literal),
do: quote_name(literal)

defp modifiers_expr(nil), do: []
defp modifiers_expr(modifiers) when is_binary(modifiers), do: [modifiers, ?\s]
defp modifiers_expr(other), do: error!(nil, "PostgreSQL adapter expects :modifiers to be a string or nil, got #{inspect(other)}")

defp options_expr(nil),
do: []

Expand Down
4 changes: 4 additions & 0 deletions lib/ecto/adapters/tds/connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,10 @@ if Code.ensure_loaded?(Tds) do

@impl true
def execute_ddl({command, %Table{} = table, columns}) when command in @creates do
if table.modifiers do
error!(nil, "MSSQL adapter does not support :modifiers in the create table statement")
end

prefix = table.prefix

pk_name =
Expand Down
9 changes: 7 additions & 2 deletions lib/ecto/migration.ex
Original file line number Diff line number Diff line change
Expand Up @@ -479,15 +479,16 @@ defmodule Ecto.Migration do

To define a table in a migration, see `Ecto.Migration.table/2`.
"""
defstruct name: nil, prefix: nil, comment: nil, primary_key: true, engine: nil, options: nil
defstruct name: nil, prefix: nil, comment: nil, primary_key: true, engine: nil, options: nil, modifiers: nil

@type t :: %__MODULE__{
name: String.t(),
prefix: String.t() | nil,
comment: String.t() | nil,
primary_key: boolean | keyword(),
engine: atom,
options: String.t()
options: String.t(),
modifiers: String.t() | nil
}
end

Expand Down Expand Up @@ -824,6 +825,10 @@ defmodule Ecto.Migration do
* `:options` - provide custom options that will be appended after the generated
statement. For example, "WITH", "INHERITS", or "ON COMMIT" clauses. "PARTITION BY"
can be provided for databases that support table partitioning.
* `:modifiers` - provide custom modifiers that should be inserted to the
table creation statement, between the tokens "CREATE" and "TABLE". For
example, "UNLOGGED", "GLOBAL", "TEMPORARY", or "GLOBAL TEMPORARY" in
PostgreSQL.

"""
def table(name, opts \\ [])
Expand Down
5 changes: 5 additions & 0 deletions lib/ecto/migration/runner.ex
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,9 @@ defmodule Ecto.Migration.Runner do
defp command(ddl) when is_binary(ddl) or is_list(ddl),
do: "execute #{inspect(ddl)}"

defp command({:create, %Table{modifiers: <<_::binary>>} = table, _}),
do: "create #{render_modifiers(table.modifiers)} table #{quote_name(table.prefix, table.name)}"

defp command({:create, %Table{} = table, _}),
do: "create table #{quote_name(table.prefix, table.name)}"

Expand Down Expand Up @@ -502,4 +505,6 @@ defmodule Ecto.Migration.Runner do
defp quote_name(prefix, name), do: quote_name(prefix) <> "." <> quote_name(name)
defp quote_name(name) when is_atom(name), do: quote_name(Atom.to_string(name))
defp quote_name(name), do: name

defp render_modifiers(value), do: String.downcase(String.trim(value))
end
16 changes: 16 additions & 0 deletions test/ecto/adapters/myxql_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -1824,6 +1824,22 @@ defmodule Ecto.Adapters.MyXQLTest do
]
end

test "create table with modifiers" do
create =
{:create, table(:posts, modifiers: "TEMPORARY"),
[
{:add, :id, :serial, [primary_key: true]},
{:add, :created_at, :naive_datetime, []}
]}

assert execute_ddl(create) == [
"""
CREATE TEMPORARY TABLE `posts` (`id` bigint unsigned not null auto_increment, `created_at` datetime, PRIMARY KEY (`id`)) ENGINE = INNODB
"""
|> remove_newlines
]
end

test "create table with composite key" do
create =
{:create, table(:posts),
Expand Down
16 changes: 16 additions & 0 deletions test/ecto/adapters/postgres_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2285,6 +2285,22 @@ defmodule Ecto.Adapters.PostgresTest do
]
end

test "create table with modifiers" do
create =
{:create, table(:posts, modifiers: "UNLOGGED"),
[
{:add, :id, :serial, [primary_key: true]},
{:add, :created_at, :naive_datetime, []}
]}

assert execute_ddl(create) == [
"""
CREATE UNLOGGED TABLE "posts" ("id" serial, "created_at" timestamp(0), PRIMARY KEY ("id"))
"""
|> remove_newlines
]
end

test "create table with composite key" do
create =
{:create, table(:posts),
Expand Down
9 changes: 9 additions & 0 deletions test/ecto/adapters/tds_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -1553,6 +1553,15 @@ defmodule Ecto.Adapters.TdsTest do
]
end

test "create table with modifiers should raise" do
create =
{:create, table(:posts, modifiers: "UNLOGGED"), [{:add, :id, :serial, [primary_key: true]}]}

assert_raise ArgumentError,
"MSSQL adapter does not support :modifiers in the create table statement",
fn -> execute_ddl(create) end
end

test "create table with composite key" do
create =
{:create, table(:posts),
Expand Down
19 changes: 19 additions & 0 deletions test/ecto/migrator_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,16 @@ defmodule Ecto.MigratorTest do
def change, do: flush()
end

defmodule CreateTableWithModifiersMigration do
use Ecto.Migration

def change do
create table(:sessions, modifiers: "UNLOGGED") do
add :id, :id
end
end
end

@moduletag migrated_versions: [{1, nil}, {2, nil}, {3, nil}]

setup context do
Expand Down Expand Up @@ -339,6 +349,15 @@ defmodule Ecto.MigratorTest do
assert output =~ "== Running 12 Ecto.MigratorTest.UpDownMigration.down/0"
assert output =~ "execute \"foo\""
assert output =~ ~r"== Migrated 12 in \d.\ds"

output =
capture_log(fn ->
:ok = up(TestRepo, 13, CreateTableWithModifiersMigration)
end)

assert output =~ "== Running 13 Ecto.MigratorTest.CreateTableWithModifiersMigration.change/0"
assert output =~ "create unlogged table sessions"
assert output =~ ~r"== Migrated 13 in \d.\ds"
end

test "logs ddl notices" do
Expand Down
Loading