Skip to content

Commit b238323

Browse files
committed
Allow inserting modifiers on create table statement
These modifiers are keywords that can be inserted between the tokens "CREATE" and "TABLE". See https://www.postgresql.org/docs/current/sql-createtable.html. For example, in postgres we can use :modifiers to create unlogged tables: create table(:sessions, modifiers: UNLOGGED) The statement above now produces the following DDL: CREATE UNLOGGED TABLE sessions ...
1 parent a703c2e commit b238323

File tree

9 files changed

+90
-4
lines changed

9 files changed

+90
-4
lines changed

lib/ecto/adapters/myxql/connection.ex

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -995,7 +995,9 @@ if Code.ensure_loaded?(MyXQL) do
995995

996996
[
997997
[
998-
"CREATE TABLE ",
998+
"CREATE ",
999+
modifiers_expr(table.modifiers),
1000+
"TABLE ",
9991001
if_do(command == :create_if_not_exists, "IF NOT EXISTS "),
10001002
quote_table(table.prefix, table.name),
10011003
table_structure,
@@ -1376,6 +1378,10 @@ if Code.ensure_loaded?(MyXQL) do
13761378
defp engine_expr(storage_engine),
13771379
do: [" ENGINE = ", String.upcase(to_string(storage_engine || "INNODB"))]
13781380

1381+
defp modifiers_expr(nil), do: []
1382+
defp modifiers_expr(modifiers) when is_binary(modifiers), do: [modifiers, ?\s]
1383+
defp modifiers_expr(other), do: error!(nil, "MySQL adapter expects :modifiers to be a string or nil, got #{inspect(other)}")
1384+
13791385
defp options_expr(nil),
13801386
do: []
13811387

lib/ecto/adapters/postgres/connection.ex

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1269,7 +1269,9 @@ if Code.ensure_loaded?(Postgrex) do
12691269
table_name = quote_name(table.prefix, table.name)
12701270

12711271
query = [
1272-
"CREATE TABLE ",
1272+
"CREATE ",
1273+
modifiers_expr(table.modifiers),
1274+
"TABLE ",
12731275
if_do(command == :create_if_not_exists, "IF NOT EXISTS "),
12741276
table_name,
12751277
?\s,
@@ -1760,6 +1762,10 @@ if Code.ensure_loaded?(Postgrex) do
17601762
defp include_expr(literal),
17611763
do: quote_name(literal)
17621764

1765+
defp modifiers_expr(nil), do: []
1766+
defp modifiers_expr(modifiers) when is_binary(modifiers), do: [modifiers, ?\s]
1767+
defp modifiers_expr(other), do: error!(nil, "PostgreSQL adapter expects :modifiers to be a string or nil, got #{inspect(other)}")
1768+
17631769
defp options_expr(nil),
17641770
do: []
17651771

lib/ecto/adapters/tds/connection.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,10 @@ if Code.ensure_loaded?(Tds) do
11091109

11101110
@impl true
11111111
def execute_ddl({command, %Table{} = table, columns}) when command in @creates do
1112+
unless is_nil(table.modifiers) do
1113+
error!(nil, "MSSQL adapter does not support :modifiers in the create table statement")
1114+
end
1115+
11121116
prefix = table.prefix
11131117

11141118
pk_name =

lib/ecto/migration.ex

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -479,15 +479,16 @@ defmodule Ecto.Migration do
479479
480480
To define a table in a migration, see `Ecto.Migration.table/2`.
481481
"""
482-
defstruct name: nil, prefix: nil, comment: nil, primary_key: true, engine: nil, options: nil
482+
defstruct name: nil, prefix: nil, comment: nil, primary_key: true, engine: nil, options: nil, modifiers: nil
483483

484484
@type t :: %__MODULE__{
485485
name: String.t(),
486486
prefix: String.t() | nil,
487487
comment: String.t() | nil,
488488
primary_key: boolean | keyword(),
489489
engine: atom,
490-
options: String.t()
490+
options: String.t(),
491+
modifiers: String.t() | nil
491492
}
492493
end
493494

@@ -824,6 +825,10 @@ defmodule Ecto.Migration do
824825
* `:options` - provide custom options that will be appended after the generated
825826
statement. For example, "WITH", "INHERITS", or "ON COMMIT" clauses. "PARTITION BY"
826827
can be provided for databases that support table partitioning.
828+
* `:modifiers` - provide custom modifiers that should be inserted to the
829+
table creation statement, between the tokens "CREATE" and "TABLE". For
830+
example, "UNLOGGED", "GLOBAL", "TEMPORARY", or "GLOBAL TEMPORARY" in
831+
PostgreSQL.
827832
828833
"""
829834
def table(name, opts \\ [])

lib/ecto/migration/runner.ex

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,9 @@ defmodule Ecto.Migration.Runner do
434434
defp command(ddl) when is_binary(ddl) or is_list(ddl),
435435
do: "execute #{inspect(ddl)}"
436436

437+
defp command({:create, %Table{modifiers: <<_::binary>>} = table, _}),
438+
do: "create #{render_modifiers(table.modifiers)} table #{quote_name(table.prefix, table.name)}"
439+
437440
defp command({:create, %Table{} = table, _}),
438441
do: "create table #{quote_name(table.prefix, table.name)}"
439442

@@ -502,4 +505,6 @@ defmodule Ecto.Migration.Runner do
502505
defp quote_name(prefix, name), do: quote_name(prefix) <> "." <> quote_name(name)
503506
defp quote_name(name) when is_atom(name), do: quote_name(Atom.to_string(name))
504507
defp quote_name(name), do: name
508+
509+
defp render_modifiers(value), do: String.downcase(String.trim(value))
505510
end

test/ecto/adapters/myxql_test.exs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1824,6 +1824,22 @@ defmodule Ecto.Adapters.MyXQLTest do
18241824
]
18251825
end
18261826

1827+
test "create table with modifiers" do
1828+
create =
1829+
{:create, table(:posts, modifiers: "TEMPORARY"),
1830+
[
1831+
{:add, :id, :serial, [primary_key: true]},
1832+
{:add, :created_at, :naive_datetime, []}
1833+
]}
1834+
1835+
assert execute_ddl(create) == [
1836+
"""
1837+
CREATE TEMPORARY TABLE `posts` (`id` bigint unsigned not null auto_increment, `created_at` datetime, PRIMARY KEY (`id`)) ENGINE = INNODB
1838+
"""
1839+
|> remove_newlines
1840+
]
1841+
end
1842+
18271843
test "create table with composite key" do
18281844
create =
18291845
{:create, table(:posts),

test/ecto/adapters/postgres_test.exs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2285,6 +2285,22 @@ defmodule Ecto.Adapters.PostgresTest do
22852285
]
22862286
end
22872287

2288+
test "create table with modifiers" do
2289+
create =
2290+
{:create, table(:posts, modifiers: "UNLOGGED"),
2291+
[
2292+
{:add, :id, :serial, [primary_key: true]},
2293+
{:add, :created_at, :naive_datetime, []}
2294+
]}
2295+
2296+
assert execute_ddl(create) == [
2297+
"""
2298+
CREATE UNLOGGED TABLE "posts" ("id" serial, "created_at" timestamp(0), PRIMARY KEY ("id"))
2299+
"""
2300+
|> remove_newlines
2301+
]
2302+
end
2303+
22882304
test "create table with composite key" do
22892305
create =
22902306
{:create, table(:posts),

test/ecto/adapters/tds_test.exs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,6 +1553,15 @@ defmodule Ecto.Adapters.TdsTest do
15531553
]
15541554
end
15551555

1556+
test "create table with modifiers should raise" do
1557+
create =
1558+
{:create, table(:posts, modifiers: "UNLOGGED"), [{:add, :id, :serial, [primary_key: true]}]}
1559+
1560+
assert_raise ArgumentError,
1561+
"MSSQL adapter does not support :modifiers in the create table statement",
1562+
fn -> execute_ddl(create) end
1563+
end
1564+
15561565
test "create table with composite key" do
15571566
create =
15581567
{:create, table(:posts),

test/ecto/migrator_test.exs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,16 @@ defmodule Ecto.MigratorTest do
188188
def change, do: flush()
189189
end
190190

191+
defmodule CreateTableWithModifiersMigration do
192+
use Ecto.Migration
193+
194+
def change do
195+
create table(:sessions, modifiers: "UNLOGGED") do
196+
add :id, :id
197+
end
198+
end
199+
end
200+
191201
@moduletag migrated_versions: [{1, nil}, {2, nil}, {3, nil}]
192202

193203
setup context do
@@ -339,6 +349,15 @@ defmodule Ecto.MigratorTest do
339349
assert output =~ "== Running 12 Ecto.MigratorTest.UpDownMigration.down/0"
340350
assert output =~ "execute \"foo\""
341351
assert output =~ ~r"== Migrated 12 in \d.\ds"
352+
353+
output =
354+
capture_log(fn ->
355+
:ok = up(TestRepo, 13, CreateTableWithModifiersMigration)
356+
end)
357+
358+
assert output =~ "== Running 13 Ecto.MigratorTest.CreateTableWithModifiersMigration.change/0"
359+
assert output =~ "create unlogged table sessions"
360+
assert output =~ ~r"== Migrated 13 in \d.\ds"
342361
end
343362

344363
test "logs ddl notices" do

0 commit comments

Comments
 (0)