Skip to content

Commit deace19

Browse files
committed
WIP
1 parent 195be91 commit deace19

File tree

7 files changed

+162
-43
lines changed

7 files changed

+162
-43
lines changed

integration_test/myxql/migrations_test.exs

+19-9
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,31 @@ defmodule Ecto.Integration.MigrationsTest do
2929
end
3030
end
3131

32-
text_types = ~w/tinytext text mediumtext longtext/a
33-
@text_types text_types
32+
text_variants = ~w/tinytext text mediumtext longtext/a
33+
@text_variants text_variants
34+
35+
collation = "utf8mb4_bin"
36+
@collation collation
3437

3538
defmodule CollateMigration do
3639
use Ecto.Migration
3740

38-
@text_types text_types
41+
@text_variants text_variants
3942
def change do
43+
create table(:collate_reference) do
44+
add :name, :string, collation: @collation
45+
end
46+
47+
create unique_index(:collate_reference, :name)
48+
4049
create table(:collate) do
41-
add :string, :string, collation: "utf8mb4_bin"
42-
add :varchar, :varchar, size: 255, collation: "utf8mb4_bin"
43-
add :integer, :integer, collation: "utf8mb4_bin"
50+
add :string, :string, collation: @collation
51+
add :varchar, :varchar, size: 255, collation: @collation
52+
add :integer, :integer, collation: @collation
53+
add :name_string, references(:collate_reference, type: :string, column: :name), collation: @collation
4454

45-
for type <- @text_types do
46-
add type, type, collation: "utf8mb4_bin"
55+
for type <- @text_variants do
56+
add type, type, collation: @collation
4757
end
4858
end
4959

@@ -150,7 +160,7 @@ defmodule Ecto.Integration.MigrationsTest do
150160
rows: [["utf8mb4_bin"]]
151161
} = Ecto.Adapters.SQL.query!(PoolRepo, query.("text"), [])
152162

153-
for type <- @text_types do
163+
for type <- @text_variants do
154164
assert %{
155165
rows: [["utf8mb4_bin"]]
156166
} = Ecto.Adapters.SQL.query!(PoolRepo, query.(to_string(type)), [])

integration_test/pg/migrations_test.exs

+28-17
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,33 @@ defmodule Ecto.Integration.MigrationsTest do
4040
end
4141
end
4242

43+
collation = "POSIX"
44+
@collation collation
45+
46+
text_types = ~w/char varchar text/a
47+
@text_types text_types
48+
4349
defmodule CollateMigration do
4450
use Ecto.Migration
4551

52+
@collation collation
53+
@text_types text_types
54+
4655
def change do
56+
create table(:collate_reference) do
57+
add :name, :string, primary_key: true, collation: @collation
58+
end
59+
create unique_index(:collate_reference, :name)
60+
4761
create table(:collate) do
48-
add :string, :string, collation: "POSIX"
49-
add :char, :char, collation: "POSIX"
50-
add :varchar, :varchar, collation: "POSIX"
51-
add :text, :text, collation: "POSIX"
52-
add :integer, :integer, collation: "POSIX"
62+
add :string, :string, collation: @collation
63+
64+
for type <- @text_types do
65+
add type, type, collation: @collation
66+
end
67+
68+
add :integer, :integer, collation: @collation
69+
add :name_string, references(:collate_reference, type: :string, column: :name), collation: @collation
5370
end
5471

5572
alter table(:collate) do
@@ -178,18 +195,12 @@ defmodule Ecto.Integration.MigrationsTest do
178195
rows: [["C"]]
179196
} = Ecto.Adapters.SQL.query!(PoolRepo, query.("string"), [])
180197

181-
assert %{
182-
rows: [["POSIX"]]
183-
} = Ecto.Adapters.SQL.query!(PoolRepo, query.("varchar"), [])
184-
185-
assert %{
186-
rows: [["POSIX"]]
187-
} = Ecto.Adapters.SQL.query!(PoolRepo, query.("char"), [])
188-
189-
assert %{
190-
rows: [["POSIX"]]
191-
} = Ecto.Adapters.SQL.query!(PoolRepo, query.("text"), [])
192-
198+
for type <- @text_types do
199+
assert %{
200+
rows: [[@collation]]
201+
} = Ecto.Adapters.SQL.query!(PoolRepo, query.(type), [])
202+
end
203+
193204
assert %{
194205
rows: [[nil]]
195206
} = Ecto.Adapters.SQL.query!(PoolRepo, query.("integer"), [])

integration_test/tds/migrations_test.exs

+77
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,38 @@ defmodule Ecto.Integration.MigrationsTest do
1515
end
1616
end
1717

18+
collation = "Latin1_General_CS_AS"
19+
@collation collation
20+
21+
defmodule CollateMigration do
22+
use Ecto.Migration
23+
@collation collation
24+
25+
def change do
26+
create table(:collate_reference) do
27+
add :name, :string, collation: @collation
28+
end
29+
30+
create unique_index(:collate_reference, :name)
31+
32+
create table(:collate) do
33+
add :string, :string, collation: @collation
34+
add :char, :char, size: 255, collation: @collation
35+
add :nchar, :nchar, size: 255, collation: @collation
36+
add :varchar, :varchar, size: 255, collation: @collation
37+
add :nvarchar, :nvarchar, size: 255, collation: @collation
38+
add :text, :text, collation: @collation
39+
add :ntext, :ntext, collation: @collation
40+
add :integer, :integer, collation: @collation
41+
add :name_string, references(:collate_reference, type: :string, column: :name), collation: @collation
42+
end
43+
44+
alter table(:collate) do
45+
modify :string, :string, collation: "Japanese_Bushu_Kakusu_100_CS_AS_KS_WS"
46+
end
47+
end
48+
end
49+
1850
describe "Migrator" do
1951
@get_lock_command ~s(sp_getapplock @Resource = 'ecto_Ecto.Integration.PoolRepo', @LockMode = 'Exclusive', @LockOwner = 'Transaction', @LockTimeout = -1)
2052
@create_table_sql ~s(CREATE TABLE [log_mode_table])
@@ -77,5 +109,50 @@ defmodule Ecto.Integration.MigrationsTest do
77109
refute down_log =~ @version_delete
78110
refute down_log =~ "commit []"
79111
end
112+
113+
test "collation can be set on a column" do
114+
num = @base_migration + System.unique_integer([:positive])
115+
up_log = capture_log(fn ->
116+
Ecto.Migrator.up(PoolRepo, num, CollateMigration, log: :info)
117+
end)
118+
IO.puts(up_log)
119+
query = fn column -> """
120+
SELECT collation_name
121+
FROM information_schema.columns
122+
WHERE table_name = 'collate' AND column_name = '#{column}';
123+
"""
124+
end
125+
126+
assert %{
127+
rows: [["Japanese_Bushu_Kakusu_100_CS_AS_KS_WS"]]
128+
} = Ecto.Adapters.SQL.query!(PoolRepo, query.("string"), [])
129+
130+
assert %{
131+
rows: [[@collation]]
132+
} = Ecto.Adapters.SQL.query!(PoolRepo, query.("char"), [])
133+
assert %{
134+
rows: [[@collation]]
135+
} = Ecto.Adapters.SQL.query!(PoolRepo, query.("varchar"), [])
136+
137+
assert %{
138+
rows: [[@collation]]
139+
} = Ecto.Adapters.SQL.query!(PoolRepo, query.("nchar"), [])
140+
141+
assert %{
142+
rows: [[@collation]]
143+
} = Ecto.Adapters.SQL.query!(PoolRepo, query.("nvarchar"), [])
144+
145+
assert %{
146+
rows: [[@collation]]
147+
} = Ecto.Adapters.SQL.query!(PoolRepo, query.("text"), [])
148+
149+
assert %{
150+
rows: [[@collation]]
151+
} = Ecto.Adapters.SQL.query!(PoolRepo, query.("ntext"), [])
152+
153+
assert %{
154+
rows: [[nil]]
155+
} = Ecto.Adapters.SQL.query!(PoolRepo, query.("integer"), [])
156+
end
80157
end
81158
end

lib/ecto/adapters/myxql/connection.ex

+3-3
Original file line numberDiff line numberDiff line change
@@ -1274,7 +1274,7 @@ if Code.ensure_loaded?(MyXQL) do
12741274

12751275
[
12761276
default_expr(default),
1277-
collation(collation, type),
1277+
collation_expr(collation, type),
12781278
null_expr(null),
12791279
comment_expr(comment),
12801280
after_expr(after_column)
@@ -1299,12 +1299,12 @@ if Code.ensure_loaded?(MyXQL) do
12991299
defp null_expr(true), do: " NULL"
13001300
defp null_expr(_), do: []
13011301

1302-
defp collation({:ok, collation_name}, text_type)
1302+
defp collation_expr({:ok, collation_name}, text_type)
13031303
when text_type in ~w/string char varchar text tinytext mediumtext longtext/a do
13041304
" COLLATE \"#{collation_name}\""
13051305
end
13061306

1307-
defp collation(_, _), do: []
1307+
defp collation_expr(_, _), do: []
13081308

13091309
defp new_constraint_expr(%Constraint{check: check} = constraint) when is_binary(check) do
13101310
[

lib/ecto/adapters/postgres/connection.ex

+5-5
Original file line numberDiff line numberDiff line change
@@ -1567,7 +1567,7 @@ if Code.ensure_loaded?(Postgrex) do
15671567
reference_expr(ref, table, name),
15681568
modify_null(name, opts),
15691569
modify_default(name, ref.type, opts),
1570-
collation(collation, ref.type)
1570+
collation_expr(collation, ref.type)
15711571
]
15721572
end
15731573

@@ -1582,7 +1582,7 @@ if Code.ensure_loaded?(Postgrex) do
15821582
column_type(type, opts),
15831583
modify_null(name, opts),
15841584
modify_default(name, type, opts),
1585-
collation(collation, type)
1585+
collation_expr(collation, type)
15861586
]
15871587
end
15881588

@@ -1632,19 +1632,19 @@ if Code.ensure_loaded?(Postgrex) do
16321632
null = Keyword.get(opts, :null)
16331633
collation = Keyword.fetch(opts, :collation)
16341634

1635-
[default_expr(default, type), null_expr(null), collation(collation, type)]
1635+
[default_expr(default, type), null_expr(null), collation_expr(collation, type)]
16361636
end
16371637

16381638
defp null_expr(false), do: " NOT NULL"
16391639
defp null_expr(true), do: " NULL"
16401640
defp null_expr(_), do: []
16411641

1642-
defp collation({:ok, collation_name}, text_type)
1642+
defp collation_expr({:ok, collation_name}, text_type)
16431643
when text_type in ~w/string text char varchar/a do
16441644
" COLLATE \"#{collation_name}\""
16451645
end
16461646

1647-
defp collation(_, _), do: []
1647+
defp collation_expr(_, _), do: []
16481648

16491649
defp new_constraint_expr(%Constraint{check: check} = constraint) when is_binary(check) do
16501650
[

lib/ecto/adapters/tds/connection.ex

+22-8
Original file line numberDiff line numberDiff line change
@@ -1369,13 +1369,13 @@ if Code.ensure_loaded?(Tds) do
13691369
quote_name(name),
13701370
" ",
13711371
reference_column_type(ref.type, opts),
1372-
column_options(table, name, opts),
1372+
column_options(table, name, ref.type, opts),
13731373
reference_expr(ref, table, name)
13741374
]
13751375
end
13761376

13771377
defp column_definition(table, {:add, name, type, opts}) do
1378-
[quote_name(name), " ", column_type(type, opts), column_options(table, name, opts)]
1378+
[quote_name(name), " ", column_type(type, opts), column_options(table, name, type, opts)]
13791379
end
13801380

13811381
defp column_changes(statement, table, columns) do
@@ -1396,7 +1396,7 @@ if Code.ensure_loaded?(Tds) do
13961396
quote_name(name),
13971397
" ",
13981398
reference_column_type(ref.type, opts),
1399-
column_options(table, name, opts),
1399+
column_options(table, name, ref.type, opts),
14001400
"; "
14011401
],
14021402
[statement_prefix, "ADD", constraint_expr(ref, table, name), "; "]
@@ -1411,7 +1411,7 @@ if Code.ensure_loaded?(Tds) do
14111411
quote_name(name),
14121412
" ",
14131413
column_type(type, opts),
1414-
column_options(table, name, opts),
1414+
column_options(table, name, type, opts),
14151415
"; "
14161416
]
14171417
]
@@ -1430,7 +1430,7 @@ if Code.ensure_loaded?(Tds) do
14301430
quote_name(column_name),
14311431
" ",
14321432
column_type(type, opts),
1433-
column_options(table, column_name, opts),
1433+
column_options(table, column_name, type, opts),
14341434
"; "
14351435
]
14361436
]
@@ -1446,7 +1446,7 @@ if Code.ensure_loaded?(Tds) do
14461446
quote_name(name),
14471447
" ",
14481448
reference_column_type(ref.type, opts),
1449-
column_options(table, name, opts),
1449+
column_options(table, name, ref.type, opts),
14501450
"; "
14511451
],
14521452
[statement_prefix, "ADD", constraint_expr(ref, table, name), "; "],
@@ -1455,6 +1455,8 @@ if Code.ensure_loaded?(Tds) do
14551455
end
14561456

14571457
defp column_change(statement_prefix, table, {:modify, name, type, opts}) do
1458+
collation = Keyword.fetch(opts, :collation)
1459+
14581460
[
14591461
drop_constraint_from_expr(opts[:from], table, name, statement_prefix),
14601462
maybe_drop_default_expr(statement_prefix, table, name, opts),
@@ -1465,6 +1467,7 @@ if Code.ensure_loaded?(Tds) do
14651467
" ",
14661468
column_type(type, opts),
14671469
null_expr(Keyword.get(opts, :null)),
1470+
collation_expr(collation, type),
14681471
"; "
14691472
],
14701473
[column_default_value(statement_prefix, table, name, opts)]
@@ -1497,10 +1500,14 @@ if Code.ensure_loaded?(Tds) do
14971500
]
14981501
end
14991502

1500-
defp column_options(table, name, opts) do
1503+
defp column_options(table, name, type, opts) do
15011504
default = Keyword.fetch(opts, :default)
15021505
null = Keyword.get(opts, :null)
1503-
[null_expr(null), default_expr(table, name, default)]
1506+
1507+
collation =
1508+
Keyword.fetch(opts, :collation)
1509+
1510+
[null_expr(null), default_expr(table, name, default), collation_expr(collation, type)]
15041511
end
15051512

15061513
defp column_default_value(statement_prefix, table, name, opts) do
@@ -1516,6 +1523,13 @@ if Code.ensure_loaded?(Tds) do
15161523
defp null_expr(true), do: [" NULL"]
15171524
defp null_expr(_), do: []
15181525

1526+
defp collation_expr({:ok, collation_name}, text_type)
1527+
when text_type in ~w/string char varchar nchar nvarchar text ntext/a do
1528+
" COLLATE #{collation_name}"
1529+
end
1530+
1531+
defp collation_expr(_, _), do: []
1532+
15191533
defp default_expr(_table, _name, {:ok, nil}),
15201534
do: []
15211535

lib/ecto/migration.ex

+8-1
Original file line numberDiff line numberDiff line change
@@ -282,12 +282,19 @@ defmodule Ecto.Migration do
282282
are not known by `ecto_sql` and specifying an incorrect collation might cause
283283
a migration to fail.
284284
285+
### N.B. be sure to match the collation on any columns that reference other text columns. See example below
286+
285287
def change do
288+
create table(:collate_reference) do
289+
add :name, :string, collation: "POSIX"
290+
end
291+
286292
create table(:collate) do
287293
add :string, :string, collation: "POSIX"
294+
add :name_ref, references(:collate_reference, type: :string, column: :name), collation: "POSIX"
288295
end
289296
end
290-
297+
291298
## Comments
292299
293300
Migrations where you create or alter a table support specifying table

0 commit comments

Comments
 (0)