changed CHANGELOG.md
 
@@ -1,12 1,25 @@
1
1
# Changelog for v3.x
2
2
3
- ## v3.10.1 (2022-04-11)
3
## v3.10.2 (2023-08-21)
4
5
### Enhancements
6
7
* [migrations] Handle `from: {reference, opts}` in FK migrations
8
* [mysql] Support MariaDB versioned tables
9
10
### Bug fixes
11
12
* [migrations] Don't add comment to removed columns
13
* [migrations] Ensure module is loaded before checking for migration
14
* [mysql] Fix for casting boolean values in MySQL
15
16
## v3.10.1 (2023-04-11)
4
17
5
18
### Enhancements
6
19
7
20
* [postgres] Allow Postgrex v0.17.x
8
21
9
- ## v3.10.0 (2022-04-10)
22
## v3.10.0 (2023-04-10)
10
23
11
24
### Enhancements
changed hex_metadata.config
 
@@ -1,7 1,10 @@
1
- {<<"app">>,<<"ecto_sql">>}.
2
- {<<"build_tools">>,[<<"mix">>]}.
1
{<<"links">>,[{<<"GitHub">>,<<"https://github.com/elixir-ecto/ecto_sql">>}]}.
2
{<<"name">>,<<"ecto_sql">>}.
3
{<<"version">>,<<"3.10.2">>}.
3
4
{<<"description">>,<<"SQL-based adapters for Ecto and database migrations">>}.
4
5
{<<"elixir">>,<<"~> 1.10">>}.
6
{<<"app">>,<<"ecto_sql">>}.
7
{<<"licenses">>,[<<"Apache-2.0">>]}.
5
8
{<<"files">>,
6
9
[<<".formatter.exs">>,<<"mix.exs">>,<<"README.md">>,<<"CHANGELOG.md">>,
7
10
<<"lib">>,<<"lib/ecto">>,<<"lib/ecto/adapter">>,
 
@@ -37,38 40,35 @@
37
40
<<"integration_test/support/file_helpers.exs">>,
38
41
<<"integration_test/support/migration.exs">>,
39
42
<<"integration_test/support/repo.exs">>]}.
40
- {<<"licenses">>,[<<"Apache-2.0">>]}.
41
- {<<"links">>,[{<<"GitHub">>,<<"https://github.com/elixir-ecto/ecto_sql">>}]}.
42
- {<<"name">>,<<"ecto_sql">>}.
43
43
{<<"requirements">>,
44
- [[{<<"app">>,<<"ecto">>},
45
- {<<"name">>,<<"ecto">>},
44
[[{<<"name">>,<<"ecto">>},
45
{<<"app">>,<<"ecto">>},
46
46
{<<"optional">>,false},
47
- {<<"repository">>,<<"hexpm">>},
48
- {<<"requirement">>,<<"~> 3.10.0">>}],
49
- [{<<"app">>,<<"telemetry">>},
50
- {<<"name">>,<<"telemetry">>},
47
{<<"requirement">>,<<"~> 3.10.0">>},
48
{<<"repository">>,<<"hexpm">>}],
49
[{<<"name">>,<<"telemetry">>},
50
{<<"app">>,<<"telemetry">>},
51
51
{<<"optional">>,false},
52
- {<<"repository">>,<<"hexpm">>},
53
- {<<"requirement">>,<<"~> 0.4.0 or ~> 1.0">>}],
54
- [{<<"app">>,<<"db_connection">>},
55
- {<<"name">>,<<"db_connection">>},
52
{<<"requirement">>,<<"~> 0.4.0 or ~> 1.0">>},
53
{<<"repository">>,<<"hexpm">>}],
54
[{<<"name">>,<<"db_connection">>},
55
{<<"app">>,<<"db_connection">>},
56
56
{<<"optional">>,false},
57
- {<<"repository">>,<<"hexpm">>},
58
- {<<"requirement">>,<<"~> 2.5 or ~> 2.4.1">>}],
59
- [{<<"app">>,<<"postgrex">>},
60
- {<<"name">>,<<"postgrex">>},
57
{<<"requirement">>,<<"~> 2.5 or ~> 2.4.1">>},
58
{<<"repository">>,<<"hexpm">>}],
59
[{<<"name">>,<<"postgrex">>},
60
{<<"app">>,<<"postgrex">>},
61
61
{<<"optional">>,true},
62
- {<<"repository">>,<<"hexpm">>},
63
- {<<"requirement">>,<<"~> 0.16.0 or ~> 0.17.0 or ~> 1.0">>}],
64
- [{<<"app">>,<<"myxql">>},
65
- {<<"name">>,<<"myxql">>},
62
{<<"requirement">>,<<"~> 0.16.0 or ~> 0.17.0 or ~> 1.0">>},
63
{<<"repository">>,<<"hexpm">>}],
64
[{<<"name">>,<<"myxql">>},
65
{<<"app">>,<<"myxql">>},
66
66
{<<"optional">>,true},
67
- {<<"repository">>,<<"hexpm">>},
68
- {<<"requirement">>,<<"~> 0.6.0">>}],
69
- [{<<"app">>,<<"tds">>},
70
- {<<"name">>,<<"tds">>},
67
{<<"requirement">>,<<"~> 0.6.0">>},
68
{<<"repository">>,<<"hexpm">>}],
69
[{<<"name">>,<<"tds">>},
70
{<<"app">>,<<"tds">>},
71
71
{<<"optional">>,true},
72
- {<<"repository">>,<<"hexpm">>},
73
- {<<"requirement">>,<<"~> 2.1.1 or ~> 2.2">>}]]}.
74
- {<<"version">>,<<"3.10.1">>}.
72
{<<"requirement">>,<<"~> 2.1.1 or ~> 2.2">>},
73
{<<"repository">>,<<"hexpm">>}]]}.
74
{<<"build_tools">>,[<<"mix">>]}.
changed integration_test/sql/migration.exs
 
@@ -13,6 13,7 @@ defmodule Ecto.Integration.MigrationTest do
13
13
create @table do
14
14
add :value, :integer
15
15
end
16
16
17
create @index
17
18
end
18
19
 
@@ -81,7 82,7 @@ defmodule Ecto.Integration.MigrationTest do
81
82
82
83
if direction() == :up do
83
84
flush()
84
- PoolRepo.insert_all "modify_from_products", [[value: 1, nullable: 1]]
85
PoolRepo.insert_all("modify_from_products", [[value: 1, nullable: 1]])
85
86
end
86
87
87
88
alter table(:modify_from_products) do
 
@@ -98,23 99,26 @@ defmodule Ecto.Integration.MigrationTest do
98
99
create table(:modify_from_authors, primary_key: false) do
99
100
add :id, :integer, primary_key: true
100
101
end
102
101
103
create table(:modify_from_posts) do
102
104
add :author_id, references(:modify_from_authors, type: :integer)
103
105
end
104
106
105
107
if direction() == :up do
106
108
flush()
107
- PoolRepo.insert_all "modify_from_authors", [[id: 1]]
108
- PoolRepo.insert_all "modify_from_posts", [[author_id: 1]]
109
PoolRepo.insert_all("modify_from_authors", [[id: 1]])
110
PoolRepo.insert_all("modify_from_posts", [[author_id: 1]])
109
111
end
110
112
111
113
alter table(:modify_from_posts) do
112
114
# remove the constraints modify_from_posts_author_id_fkey
113
115
modify :author_id, :integer, from: references(:modify_from_authors, type: :integer)
114
116
end
117
115
118
alter table(:modify_from_authors) do
116
119
modify :id, :bigint, from: :integer
117
120
end
121
118
122
alter table(:modify_from_posts) do
119
123
# add the constraints modify_from_posts_author_id_fkey
120
124
modify :author_id, references(:modify_from_authors, type: :bigint), from: :integer
 
@@ -240,6 244,7 @@ defmodule Ecto.Integration.MigrationTest do
240
244
create table(:ref) do
241
245
add :col1, :integer
242
246
add :col2, :integer
247
243
248
add :parent_id,
244
249
references(:parent,
245
250
with: [col1: :col1, col2: :col2],
 
@@ -281,7 286,8 @@ defmodule Ecto.Integration.MigrationTest do
281
286
282
287
create unique_index(:composite_parent, [:id, :key_id], name: "old_index_name")
283
288
284
- rename index(:composite_parent, [:id, :key_id], name: "old_index_name"), to: "new_index_name"
289
rename index(:composite_parent, [:id, :key_id], name: "old_index_name"),
290
to: "new_index_name"
285
291
end
286
292
end
287
293
 
@@ -323,6 329,7 @@ defmodule Ecto.Integration.MigrationTest do
323
329
def up do
324
330
execute TestRepo.create_prefix(@prefix)
325
331
create table(:first, prefix: @prefix)
332
326
333
create table(:second, prefix: @prefix) do
327
334
add :first_id, references(:first)
328
335
end
 
@@ -396,13 403,13 @@ defmodule Ecto.Integration.MigrationTest do
396
403
create table(:no_pk, primary_key: false) do
397
404
add :dummy, :string
398
405
end
406
399
407
alter table(:no_pk) do
400
408
add :id, :serial, primary_key: true
401
409
end
402
410
end
403
411
end
404
412
405
-
406
413
defmodule AddColumnIfNotExistsMigration do
407
414
use Ecto.Migration
408
415
 
@@ -450,8 457,8 @@ defmodule Ecto.Integration.MigrationTest do
450
457
create table(:no_error_on_conditional_column_migration)
451
458
452
459
alter table(:no_error_on_conditional_column_migration) do
453
- add_if_not_exists :value, :integer
454
- add_if_not_exists :value, :integer
460
add_if_not_exists :value, :integer
461
add_if_not_exists :value, :integer
455
462
456
463
remove_if_exists :value, :integer
457
464
remove_if_exists :value, :integer
 
@@ -487,8 494,8 @@ defmodule Ecto.Integration.MigrationTest do
487
494
test "supports on delete", %{migration_number: num} do
488
495
assert :ok == up(PoolRepo, num, OnDeleteMigration, log: false)
489
496
490
- parent1 = PoolRepo.insert! Ecto.put_meta(%Parent{}, source: "parent1")
491
- parent2 = PoolRepo.insert! Ecto.put_meta(%Parent{}, source: "parent2")
497
parent1 = PoolRepo.insert!(Ecto.put_meta(%Parent{}, source: "parent1"))
498
parent2 = PoolRepo.insert!(Ecto.put_meta(%Parent{}, source: "parent2"))
492
499
493
500
writer = "INSERT INTO ref_migration (parent1, parent2) VALUES (#{parent1.id}, #{parent2.id})"
494
501
PoolRepo.query!(writer)
 
@@ -527,12 534,16 @@ defmodule Ecto.Integration.MigrationTest do
527
534
assert :ok == down(PoolRepo, num, ReferencesRollbackMigration, log: false)
528
535
end
529
536
530
- test "create table if not exists and drop table if exists does not raise on failure", %{migration_number: num} do
537
test "create table if not exists and drop table if exists does not raise on failure", %{
538
migration_number: num
539
} do
531
540
assert :ok == up(PoolRepo, num, NoErrorTableMigration, log: false)
532
541
end
533
542
534
543
@tag :create_index_if_not_exists
535
- test "create index if not exists and drop index if exists does not raise on failure", %{migration_number: num} do
544
test "create index if not exists and drop index if exists does not raise on failure", %{
545
migration_number: num
546
} do
536
547
assert :ok == up(PoolRepo, num, NoErrorIndexMigration, log: false)
537
548
end
538
549
 
@@ -545,7 556,7 @@ defmodule Ecto.Integration.MigrationTest do
545
556
@tag :add_column
546
557
test "add column", %{migration_number: num} do
547
558
assert :ok == up(PoolRepo, num, AddColumnMigration, log: false)
548
- assert [2] == PoolRepo.all from p in "add_col_migration", select: p.to_be_added
559
assert [2] == PoolRepo.all(from p in "add_col_migration", select: p.to_be_added)
549
560
:ok = down(PoolRepo, num, AddColumnMigration, log: false)
550
561
end
551
562
 
@@ -554,13 565,16 @@ defmodule Ecto.Integration.MigrationTest do
554
565
assert :ok == up(PoolRepo, num, AlterColumnMigration, log: false)
555
566
556
567
assert ["foo"] ==
557
- PoolRepo.all from p in "alter_col_migration", select: p.from_null_to_not_null
568
PoolRepo.all(from p in "alter_col_migration", select: p.from_null_to_not_null)
569
558
570
assert [nil] ==
559
- PoolRepo.all from p in "alter_col_migration", select: p.from_not_null_to_null
571
PoolRepo.all(from p in "alter_col_migration", select: p.from_not_null_to_null)
572
560
573
assert [nil] ==
561
- PoolRepo.all from p in "alter_col_migration", select: p.from_default_to_no_default
574
PoolRepo.all(from p in "alter_col_migration", select: p.from_default_to_no_default)
575
562
576
assert [0] ==
563
- PoolRepo.all from p in "alter_col_migration", select: p.from_no_default_to_default
577
PoolRepo.all(from p in "alter_col_migration", select: p.from_no_default_to_default)
564
578
565
579
query = "INSERT INTO `alter_col_migration` (\"from_not_null_to_null\") VALUES ('foo')"
566
580
assert catch_error(PoolRepo.query!(query))
 
@@ -573,7 587,7 @@ defmodule Ecto.Integration.MigrationTest do
573
587
assert :ok == up(PoolRepo, num, AlterColumnFromMigration, log: false)
574
588
575
589
assert [1] ==
576
- PoolRepo.all from p in "modify_from_products", select: p.value
590
PoolRepo.all(from p in "modify_from_products", select: p.value)
577
591
578
592
:ok = down(PoolRepo, num, AlterColumnFromMigration, log: false)
579
593
end
 
@@ -583,7 597,7 @@ defmodule Ecto.Integration.MigrationTest do
583
597
assert :ok == up(PoolRepo, num, AlterColumnFromPkeyMigration, log: false)
584
598
585
599
assert [1] ==
586
- PoolRepo.all from p in "modify_from_posts", select: p.author_id
600
PoolRepo.all(from p in "modify_from_posts", select: p.author_id)
587
601
588
602
:ok = down(PoolRepo, num, AlterColumnFromPkeyMigration, log: false)
589
603
end
 
@@ -593,11 607,11 @@ defmodule Ecto.Integration.MigrationTest do
593
607
assert :ok == up(PoolRepo, num, AlterForeignKeyOnDeleteMigration, log: false)
863
608
595
609
PoolRepo.insert_all("alter_fk_users", [[]])
596
- assert [id] = PoolRepo.all from p in "alter_fk_users", select: p.id
610
assert [id] = PoolRepo.all(from p in "alter_fk_users", select: p.id)
597
611
598
612
PoolRepo.insert_all("alter_fk_posts", [[alter_fk_user_id: id]])
599
613
PoolRepo.delete_all("alter_fk_users")
600
- assert [nil] == PoolRepo.all from p in "alter_fk_posts", select: p.alter_fk_user_id
614
assert [nil] == PoolRepo.all(from p in "alter_fk_posts", select: p.alter_fk_user_id)
601
615
602
616
:ok = down(PoolRepo, num, AlterForeignKeyOnDeleteMigration, log: false)
603
617
end
 
@@ -607,11 621,11 @@ defmodule Ecto.Integration.MigrationTest do
607
621
assert :ok == up(PoolRepo, num, AlterForeignKeyOnUpdateMigration, log: false)
608
622
609
623
PoolRepo.insert_all("alter_fk_users", [[]])
610
- assert [id] = PoolRepo.all from p in "alter_fk_users", select: p.id
624
assert [id] = PoolRepo.all(from p in "alter_fk_users", select: p.id)
611
625
612
626
PoolRepo.insert_all("alter_fk_posts", [[alter_fk_user_id: id]])
613
627
PoolRepo.update_all("alter_fk_users", set: [id: 12345])
614
- assert [12345] == PoolRepo.all from p in "alter_fk_posts", select: p.alter_fk_user_id
628
assert [12345] == PoolRepo.all(from p in "alter_fk_posts", select: p.alter_fk_user_id)
615
629
616
630
PoolRepo.delete_all("alter_fk_posts")
617
631
:ok = down(PoolRepo, num, AlterForeignKeyOnUpdateMigration, log: false)
 
@@ -620,14 634,14 @@ defmodule Ecto.Integration.MigrationTest do
620
634
@tag :remove_column
621
635
test "remove column", %{migration_number: num} do
622
636
assert :ok == up(PoolRepo, num, DropColumnMigration, log: false)
623
- assert catch_error(PoolRepo.all from p in "drop_col_migration", select: p.to_be_removed)
637
assert catch_error(PoolRepo.all(from p in "drop_col_migration", select: p.to_be_removed))
624
638
:ok = down(PoolRepo, num, DropColumnMigration, log: false)
625
639
end
626
640
627
641
@tag :rename_column
628
642
test "rename column", %{migration_number: num} do
629
643
assert :ok == up(PoolRepo, num, RenameColumnMigration, log: false)
630
- assert [1] == PoolRepo.all from p in "rename_col_migration", select: p.was_renamed
644
assert [1] == PoolRepo.all(from p in "rename_col_migration", select: p.was_renamed)
631
645
:ok = down(PoolRepo, num, RenameColumnMigration, log: false)
632
646
end
633
647
 
@@ -651,7 665,9 @@ defmodule Ecto.Integration.MigrationTest do
651
665
652
666
@tag :add_column_if_not_exists
653
667
@tag :remove_column_if_exists
654
- test "add if not exists and remove if exists does not raise on failure", %{migration_number: num} do
668
test "add if not exists and remove if exists does not raise on failure", %{
669
migration_number: num
670
} do
655
671
assert :ok == up(PoolRepo, num, NoErrorOnConditionalColumnMigration, log: false)
656
672
assert :ok == down(PoolRepo, num, NoErrorOnConditionalColumnMigration, log: false)
657
673
end
 
@@ -659,14 675,18 @@ defmodule Ecto.Integration.MigrationTest do
659
675
@tag :add_column_if_not_exists
660
676
test "add column if not exists", %{migration_number: num} do
661
677
assert :ok == up(PoolRepo, num, AddColumnIfNotExistsMigration, log: false)
662
- assert [2] == PoolRepo.all from p in "add_col_if_not_exists_migration", select: p.to_be_added
678
assert [2] == PoolRepo.all(from p in "add_col_if_not_exists_migration", select: p.to_be_added)
663
679
:ok = down(PoolRepo, num, AddColumnIfNotExistsMigration, log: false)
664
680
end
665
681
666
682
@tag :remove_column_if_exists
667
683
test "remove column when exists", %{migration_number: num} do
668
684
assert :ok == up(PoolRepo, num, DropColumnIfExistsMigration, log: false)
669
- assert catch_error(PoolRepo.all from p in "drop_col_if_exists_migration", select: p.to_be_removed)
685
686
assert catch_error(
687
PoolRepo.all(from p in "drop_col_if_exists_migration", select: p.to_be_removed)
688
)
689
670
690
:ok = down(PoolRepo, num, DropColumnIfExistsMigration, log: false)
671
691
end
672
692
 
@@ -675,11 695,13 @@ defmodule Ecto.Integration.MigrationTest do
675
695
assert :ok == up(PoolRepo, num, OnDeleteNilifyColumnsMigration, log: false)
676
696
677
697
PoolRepo.insert_all("parent", [%{col1: 1, col2: 2}])
678
- assert [{id, col1, col2}] = PoolRepo.all from p in "parent", select: {p.id, p.col1, p.col2}
698
assert [{id, col1, col2}] = PoolRepo.all(from p in "parent", select: {p.id, p.col1, p.col2})
679
699
680
700
PoolRepo.insert_all("ref", [[parent_id: id, col1: col1, col2: col2]])
681
701
PoolRepo.delete_all("parent")
682
- assert [{nil, col1, nil}] == PoolRepo.all from r in "ref", select: {r.parent_id, r.col1, r.col2}
702
703
assert [{nil, col1, nil}] ==
704
PoolRepo.all(from r in "ref", select: {r.parent_id, r.col1, r.col2})
683
705
684
706
:ok = down(PoolRepo, num, OnDeleteNilifyColumnsMigration, log: false)
685
707
end
changed integration_test/sql/sql.exs
 
@@ -12,7 12,10 @@ defmodule Ecto.Integration.SQLTest do
12
12
test "fragmented types" do
13
13
datetime = ~N[2014-01-16 20:26:51]
14
14
TestRepo.insert!(%Post{inserted_at: datetime})
15
- query = from p in Post, where: fragment("? >= ?", p.inserted_at, ^datetime), select: p.inserted_at
15
16
query =
17
from p in Post, where: fragment("? >= ?", p.inserted_at, ^datetime), select: p.inserted_at
18
16
19
assert [^datetime] = TestRepo.all(query)
17
20
end
18
21
 
@@ -159,7 162,8 @@ defmodule Ecto.Integration.SQLTest do
159
162
|> TestRepo.query!()
160
163
|> Ecto.Adapters.SQL.format_table()
161
164
162
- assert table == " --------------- --------- -------- \n| title | counter | public |\n --------------- --------- -------- \n| my post title | 1 | NULL |\n --------------- --------- -------- "
165
assert table ==
166
" --------------- --------- -------- \n| title | counter | public |\n --------------- --------- -------- \n| my post title | 1 | NULL |\n --------------- --------- -------- "
163
167
end
164
168
165
169
test "format_table edge cases" do
 
@@ -167,7 171,11 @@ defmodule Ecto.Integration.SQLTest do
167
171
assert Ecto.Adapters.SQL.format_table(%{columns: nil, rows: nil}) == ""
168
172
assert Ecto.Adapters.SQL.format_table(%{columns: [], rows: []}) == ""
169
173
assert Ecto.Adapters.SQL.format_table(%{columns: [], rows: [["test"]]}) == ""
170
- assert Ecto.Adapters.SQL.format_table(%{columns: ["test"], rows: []}) == " ------ \n| test |\n ------ \n ------ "
171
- assert Ecto.Adapters.SQL.format_table(%{columns: ["test"], rows: nil}) == " ------ \n| test |\n ------ \n ------ "
174
175
assert Ecto.Adapters.SQL.format_table(%{columns: ["test"], rows: []}) ==
176
" ------ \n| test |\n ------ \n ------ "
177
178
assert Ecto.Adapters.SQL.format_table(%{columns: ["test"], rows: nil}) ==
179
" ------ \n| test |\n ------ \n ------ "
172
180
end
173
181
end
changed lib/ecto/adapter/structure.ex
 
@@ -18,8 18,8 @@ defmodule Ecto.Adapter.Structure do
18
18
hostname: "localhost")
19
19
20
20
"""
21
- @callback structure_dump(default :: String.t, config :: Keyword.t) ::
22
- {:ok, String.t} | {:error, term}
21
@callback structure_dump(default :: String.t(), config :: Keyword.t()) ::
22
{:ok, String.t()} | {:error, term}
23
23
24
24
@doc """
25
25
Loads the given structure.
 
@@ -36,8 36,8 @@ defmodule Ecto.Adapter.Structure do
36
36
hostname: "localhost")
37
37
38
38
"""
39
- @callback structure_load(default :: String.t, config :: Keyword.t) ::
40
- {:ok, String.t} | {:error, term}
39
@callback structure_load(default :: String.t(), config :: Keyword.t()) ::
40
{:ok, String.t()} | {:error, term}
41
41
42
42
@doc """
43
43
Runs the dump command for the given repo / config.
changed lib/ecto/adapters/myxql.ex
 
@@ -287,10 287,10 @@ defmodule Ecto.Adapters.MyXQL do
287
287
"This may happen if you have trigger or other database conditions rejecting operations. " <>
288
288
"The emitted SQL was: #{sql}"
289
289
290
- {:ok, %{num_rows: 1, last_insert_id: last_insert_id}} ->
291
- {:ok, last_insert_id(key, last_insert_id)}
292
-
293
- {:ok, %{num_rows: 2, last_insert_id: last_insert_id}} ->
290
# We were used to check if num_rows was 1 or 2 (in case of upserts)
291
# but MariaDB supports tables with System Versioning, and in those
292
# cases num_rows can be more than 2.
293
{:ok, %{last_insert_id: last_insert_id}} ->
294
294
{:ok, last_insert_id(key, last_insert_id)}
295
295
296
296
{:error, err} ->
changed lib/ecto/adapters/myxql/connection.ex
 
@@ -674,6 674,10 @@ if Code.ensure_loaded?(MyXQL) do
674
674
[expr(other, sources, query), " 0"]
675
675
end
676
676
677
defp expr(�to.Query.Tagged{value: other, type: :boolean}, sources, query) do
678
["IF(", expr(other, sources, query), ", TRUE, FALSE)"]
679
end
680
677
681
defp expr(�to.Query.Tagged{value: other, type: type}, sources, query) do
678
682
["CAST(", expr(other, sources, query), " AS ", ecto_cast_to_db(type, query), ?)]
679
683
end
 
@@ -1033,6 1037,8 @@ if Code.ensure_loaded?(MyXQL) do
1033
1037
defp constraint_if_not_exists_expr(%Reference{} = ref, table, name),
1034
1038
do: [", ADD " | reference_expr("FOREIGN KEY IF NOT EXISTS", ref, table, name)]
1035
1039
1040
defp drop_constraint_expr({%Reference{} = ref, _opts}, table, name),
1041
do: drop_constraint_expr(ref, table, name)
1036
1042
defp drop_constraint_expr(%Reference{} = ref, table, name),
1037
1043
do: ["DROP FOREIGN KEY ", reference_name(ref, table, name), ", "]
1038
1044
defp drop_constraint_expr(_, _, _),
changed lib/ecto/adapters/postgres/connection.ex
 
@@ -1091,6 1091,9 @@ if Code.ensure_loaded?(Postgrex) do
1091
1091
1092
1092
defp comments_for_columns(table_name, columns) do
1093
1093
Enum.flat_map(columns, fn
1094
{:remove, _column_name, _column_type, _opts} ->
1095
[]
1096
1094
1097
{_operation, column_name, _column_type, opts} ->
1095
1098
column_name = [table_name, ?. | quote_name(column_name)]
1096
1099
comments_on("COLUMN", column_name, opts[:comment])
 
@@ -1297,6 1300,8 @@ if Code.ensure_loaded?(Postgrex) do
1297
1300
reference_on_update(ref.on_update), validate(ref.validate)]
1298
1301
end
1299
1302
1303
defp drop_reference_expr({%Reference{} = ref, _opts}, table, name),
1304
do: drop_reference_expr(ref, table, name)
1300
1305
defp drop_reference_expr(%Reference{} = ref, table, name),
1301
1306
do: ["DROP CONSTRAINT ", reference_name(ref, table, name), ", "]
1302
1307
defp drop_reference_expr(_, _, _),
changed lib/ecto/adapters/sql.ex
 
@@ -135,14 135,14 @@ defmodule Ecto.Adapters.SQL do
135
135
end
136
136
137
137
@impl true
138
- def loaders({:map, _}, type), do: [&Ecto.Type.embedded_load(type, &1, :json)]
139
- def loaders(:binary_id, type), do: [Ecto.UUID, type]
140
- def loaders(_, type), do: [type]
138
def loaders({:map, _}, type), do: [&Ecto.Type.embedded_load(type, &1, :json)]
139
def loaders(:binary_id, type), do: [Ecto.UUID, type]
140
def loaders(_, type), do: [type]
141
141
142
142
@impl true
143
- def dumpers({:map, _}, type), do: [&Ecto.Type.embedded_dump(type, &1, :json)]
144
- def dumpers(:binary_id, type), do: [type, Ecto.UUID]
145
- def dumpers(_, type), do: [type]
143
def dumpers({:map, _}, type), do: [&Ecto.Type.embedded_dump(type, &1, :json)]
144
def dumpers(:binary_id, type), do: [type, Ecto.UUID]
145
def dumpers(_, type), do: [type]
146
146
147
147
## Query
148
148
 
@@ -152,11 152,13 @@ defmodule Ecto.Adapters.SQL do
152
152
end
153
153
154
154
def prepare(:update_all, query) do
155
- {:cache, {System.unique_integer([:positive]), IO.iodata_to_binary(@conn.update_all(query))}}
155
{:cache,
156
{System.unique_integer([:positive]), IO.iodata_to_binary(@conn.update_all(query))}}
156
157
end
157
158
158
159
def prepare(:delete_all, query) do
159
- {:cache, {System.unique_integer([:positive]), IO.iodata_to_binary(@conn.delete_all(query))}}
160
{:cache,
161
{System.unique_integer([:positive]), IO.iodata_to_binary(@conn.delete_all(query))}}
160
162
end
161
163
162
164
@impl true
 
@@ -172,21 174,58 @@ defmodule Ecto.Adapters.SQL do
172
174
## Schema
173
175
174
176
@impl true
175
- def autogenerate(:id), do: nil
176
- def autogenerate(:embed_id), do: Ecto.UUID.generate()
177
def autogenerate(:id), do: nil
178
def autogenerate(:embed_id), do: Ecto.UUID.generate()
177
179
def autogenerate(:binary_id), do: Ecto.UUID.bingenerate()
178
180
179
181
@impl true
180
- def insert_all(adapter_meta, schema_meta, header, rows, on_conflict, returning, placeholders, opts) do
181
- Ecto.Adapters.SQL.insert_all(adapter_meta, schema_meta, @conn, header, rows, on_conflict, returning, placeholders, opts)
182
def insert_all(
183
adapter_meta,
184
schema_meta,
185
header,
186
rows,
187
on_conflict,
188
returning,
189
placeholders,
190
opts
191
) do
192
Ecto.Adapters.SQL.insert_all(
193
adapter_meta,
194
schema_meta,
195
@conn,
196
header,
197
rows,
198
on_conflict,
199
returning,
200
placeholders,
201
opts
202
)
182
203
end
183
204
184
205
@impl true
185
- def insert(adapter_meta, %{source: source, prefix: prefix}, params,
186
- {kind, conflict_params, _} = on_conflict, returning, opts) do
206
def insert(
207
adapter_meta,
208
%{source: source, prefix: prefix},
209
params,
210
{kind, conflict_params, _} = on_conflict,
211
returning,
212
opts
213
) do
187
214
{fields, values} = :lists.unzip(params)
188
215
sql = @conn.insert(prefix, source, fields, [fields], on_conflict, returning, [])
189
- Ecto.Adapters.SQL.struct(adapter_meta, @conn, sql, :insert, source, [], values conflict_params, kind, returning, opts)
216
217
Ecto.Adapters.SQL.struct(
218
adapter_meta,
219
@conn,
220
sql,
221
:insert,
222
source,
223
[],
224
values conflict_params,
225
kind,
226
returning,
227
opts
228
)
190
229
end
191
230
192
231
@impl true
 
@@ -194,14 233,38 @@ defmodule Ecto.Adapters.SQL do
194
233
{fields, field_values} = :lists.unzip(fields)
195
234
filter_values = Keyword.values(params)
196
235
sql = @conn.update(prefix, source, fields, params, returning)
197
- Ecto.Adapters.SQL.struct(adapter_meta, @conn, sql, :update, source, params, field_values filter_values, :raise, returning, opts)
236
237
Ecto.Adapters.SQL.struct(
238
adapter_meta,
239
@conn,
240
sql,
241
:update,
242
source,
243
params,
244
field_values filter_values,
245
:raise,
246
returning,
247
opts
248
)
198
249
end
199
250
200
251
@impl true
201
252
def delete(adapter_meta, %{source: source, prefix: prefix}, params, opts) do
202
253
filter_values = Keyword.values(params)
203
254
sql = @conn.delete(prefix, source, params, [])
204
- Ecto.Adapters.SQL.struct(adapter_meta, @conn, sql, :delete, source, params, filter_values, :raise, [], opts)
255
256
Ecto.Adapters.SQL.struct(
257
adapter_meta,
258
@conn,
259
sql,
260
:delete,
261
source,
262
params,
263
filter_values,
264
:raise,
265
[],
266
opts
267
)
205
268
end
206
269
207
270
## Transaction
 
@@ -228,9 291,18 @@ defmodule Ecto.Adapters.SQL do
228
291
Ecto.Adapters.SQL.execute_ddl(meta, @conn, definition, opts)
229
292
end
230
293
231
- defoverridable [prepare: 2, execute: 5, insert: 6, update: 6, delete: 4, insert_all: 8,
232
- execute_ddl: 3, loaders: 2, dumpers: 2, autogenerate: 1,
233
- ensure_all_started: 2, __before_compile__: 1]
294
defoverridable prepare: 2,
295
execute: 5,
296
insert: 6,
297
update: 6,
298
delete: 4,
299
insert_all: 8,
300
execute_ddl: 3,
301
loaders: 2,
302
dumpers: 2,
303
autogenerate: 1,
304
ensure_all_started: 2,
305
__before_compile__: 1
234
306
end
235
307
end
236
308
 
@@ -258,8 330,8 @@ defmodule Ecto.Adapters.SQL do
258
330
{"SELECT p.id, p.title, p.inserted_at, p.created_at FROM posts as p", []}
259
331
260
332
"""
261
- @spec to_sql(:all | :update_all | :delete_all, Ecto.Repo.t, Ecto.Queryable.t) ::
262
- {String.t, [term]}
333
@spec to_sql(:all | :update_all | :delete_all, Ecto.Repo.t(), Ecto.Queryable.t()) ::
334
{String.t(), [term]}
263
335
def to_sql(kind, repo, queryable) do
264
336
case Ecto.Adapter.Queryable.prepare_query(kind, repo, queryable) do
265
337
{{:cached, _update, _reset, {_id, cached}}, params} ->
 
@@ -334,9 406,12 @@ defmodule Ecto.Adapters.SQL do
334
406
* _MyXQL_: [MySQL doc](https://dev.mysql.com/doc/refman/8.0/en/explain.html).
335
407
336
408
"""
337
- @spec explain(pid() | Ecto.Repo.t | Ecto.Adapter.adapter_meta,
338
- :all | :update_all | :delete_all,
339
- Ecto.Queryable.t, opts :: Keyword.t) :: String.t | Exception.t
409
@spec explain(
410
pid() | Ecto.Repo.t() | Ecto.Adapter.adapter_meta(),
411
:all | :update_all | :delete_all,
412
Ecto.Queryable.t(),
413
opts :: Keyword.t()
414
) :: String.t() | Exception.t()
340
415
def explain(repo, operation, queryable, opts \\ [])
341
416
342
417
def explain(repo, operation, queryable, opts) when is_atom(repo) or is_pid(repo) do
 
@@ -349,15 424,15 @@ defmodule Ecto.Adapters.SQL do
349
424
{prepared, prepared_params} = to_sql(operation, repo, queryable)
350
425
sql_call(adapter_meta, :explain_query, [prepared], prepared_params, opts)
351
426
end)
352
- |> Ecto.Multi.run(:rollback, fn _, _ ->
353
- {:error, :forced_rollback}
354
- end)
355
- |> repo.transaction(opts)
356
- |> case do
357
- {:error, :rollback, :forced_rollback, %{explain: result}} -> result
358
- {:error, :explain, error, _} -> raise error
359
- _ -> raise "unable to execute explain"
360
- end
427
|> Ecto.Multi.run(:rollback, fn _, _ ->
428
{:error, :forced_rollback}
429
end)
430
|> repo.transaction(opts)
431
|> case do
432
{:error, :rollback, :forced_rollback, %{explain: result}} -> result
433
{:error, :explain, error, _} -> raise error
434
_ -> raise "unable to execute explain"
435
end
361
436
end
362
437
363
438
@doc """
 
@@ -381,7 456,11 @@ defmodule Ecto.Adapters.SQL do
381
456
iex> MyRepo.disconnect_all(60_000)
382
457
:ok
383
458
"""
384
- @spec disconnect_all(pid | Ecto.Repo.t | Ecto.Adapter.adapter_meta, non_neg_integer, opts :: Keyword.t()) :: :ok
459
@spec disconnect_all(
460
pid | Ecto.Repo.t() | Ecto.Adapter.adapter_meta(),
461
non_neg_integer,
462
opts :: Keyword.t()
463
) :: :ok
385
464
def disconnect_all(repo, interval, opts \\ [])
386
465
387
466
def disconnect_all(repo, interval, opts) when is_atom(repo) or is_pid(repo) do
 
@@ -420,7 499,7 @@ defmodule Ecto.Adapters.SQL do
420
499
[%{rows: [[42]], num_rows: 1}]
421
500
422
501
"""
423
- @spec stream(Ecto.Repo.t, String.t, [term], Keyword.t) :: Enum.t
502
@spec stream(Ecto.Repo.t(), String.t(), [term], Keyword.t()) :: Enum.t()
424
503
def stream(repo, sql, params \\ [], opts \\ []) do
425
504
repo
426
505
|> Ecto.Adapter.lookup_meta()
 
@@ -430,14 509,12 @@ defmodule Ecto.Adapters.SQL do
430
509
@doc """
431
510
Same as `query/4` but raises on invalid queries.
432
511
"""
433
- @spec query!(pid() | Ecto.Repo.t | Ecto.Adapter.adapter_meta, iodata, [term], Keyword.t) ::
434
- %{:rows => nil | [[term] | binary],
435
- :num_rows => non_neg_integer,
436
- optional(atom) => any}
512
@spec query!(pid() | Ecto.Repo.t() | Ecto.Adapter.adapter_meta(), iodata, [term], Keyword.t()) ::
513
%{:rows => nil | [[term] | binary], :num_rows => non_neg_integer, optional(atom) => any}
437
514
def query!(repo, sql, params \\ [], opts \\ []) do
438
515
case query(repo, sql, params, opts) do
439
516
{:ok, result} -> result
440
- {:error, err} -> raise_sql_call_error err
517
{:error, err} -> raise_sql_call_error(err)
441
518
end
442
519
end
443
520
 
@@ -470,11 547,14 @@ defmodule Ecto.Adapters.SQL do
470
547
{:ok, %{rows: [[42]], num_rows: 1}}
471
548
472
549
"""
473
- @spec query(pid() | Ecto.Repo.t | Ecto.Adapter.adapter_meta, iodata, [term], Keyword.t) ::
474
- {:ok, %{:rows => nil | [[term] | binary],
475
- :num_rows => non_neg_integer,
476
- optional(atom) => any}}
477
- | {:error, Exception.t}
550
@spec query(pid() | Ecto.Repo.t() | Ecto.Adapter.adapter_meta(), iodata, [term], Keyword.t()) ::
551
{:ok,
552
%{
553
:rows => nil | [[term] | binary],
554
:num_rows => non_neg_integer,
555
optional(atom) => any
556
}}
557
| {:error, Exception.t()}
478
558
def query(repo, sql, params \\ [], opts \\ [])
479
559
480
560
def query(repo, sql, params, opts) when is_atom(repo) or is_pid(repo) do
 
@@ -488,14 568,18 @@ defmodule Ecto.Adapters.SQL do
488
568
@doc """
489
569
Same as `query_many/4` but raises on invalid queries.
490
570
"""
491
- @spec query_many!(Ecto.Repo.t | Ecto.Adapter.adapter_meta, iodata, [term], Keyword.t) ::
492
- [%{:rows => nil | [[term] | binary],
493
- :num_rows => non_neg_integer,
494
- optional(atom) => any}]
571
@spec query_many!(Ecto.Repo.t() | Ecto.Adapter.adapter_meta(), iodata, [term], Keyword.t()) ::
572
[
573
%{
574
:rows => nil | [[term] | binary],
575
:num_rows => non_neg_integer,
576
optional(atom) => any
577
}
578
]
495
579
def query_many!(repo, sql, params \\ [], opts \\ []) do
496
580
case query_many(repo, sql, params, opts) do
497
581
{:ok, result} -> result
498
- {:error, err} -> raise_sql_call_error err
582
{:error, err} -> raise_sql_call_error(err)
499
583
end
500
584
end
501
585
 
@@ -528,11 612,21 @@ defmodule Ecto.Adapters.SQL do
528
612
{:ok, [%{rows: [[40]], num_rows: 1}, %{rows: [[2]], num_rows: 1}]}
529
613
530
614
"""
531
- @spec query_many(pid() | Ecto.Repo.t | Ecto.Adapter.adapter_meta, iodata, [term], Keyword.t) ::
532
- {:ok, [%{:rows => nil | [[term] | binary],
533
- :num_rows => non_neg_integer,
534
- optional(atom) => any}]}
535
- | {:error, Exception.t}
615
@spec query_many(
616
pid() | Ecto.Repo.t() | Ecto.Adapter.adapter_meta(),
617
iodata,
618
[term],
619
Keyword.t()
620
) ::
621
{:ok,
622
[
623
%{
624
:rows => nil | [[term] | binary],
625
:num_rows => non_neg_integer,
626
optional(atom) => any
627
}
628
]}
629
| {:error, Exception.t()}
536
630
def query_many(repo, sql, params \\ [], opts \\ [])
537
631
538
632
def query_many(repo, sql, params, opts) when is_atom(repo) or is_pid(repo) do
 
@@ -566,7 660,7 @@ defmodule Ecto.Adapters.SQL do
566
660
Returns `true` if the `table` exists in the `repo`, otherwise `false`.
567
661
The table is checked against the current database/schema in the connection.
568
662
"""
569
- @spec table_exists?(Ecto.Repo.t, table :: String.t, opts :: Keyword.t) :: boolean
663
@spec table_exists?(Ecto.Repo.t(), table :: String.t(), opts :: Keyword.t()) :: boolean
570
664
def table_exists?(repo, table, opts \\ []) when is_atom(repo) do
571
665
%{sql: sql} = adapter_meta = Ecto.Adapter.lookup_meta(repo)
572
666
{query, params} = sql.table_exists_query(table)
 
@@ -584,13 678,19 @@ defmodule Ecto.Adapters.SQL do
584
678
# | My Post Title | 1 | NULL |
585
679
# --------------- --------- --------
586
680
@doc false
587
- @spec format_table(%{:columns => [String.t] | nil, :rows => [term()] | nil, optional(atom) => any()}) :: String.t
681
@spec format_table(%{
682
:columns => [String.t()] | nil,
683
:rows => [term()] | nil,
684
optional(atom) => any()
685
}) :: String.t()
588
686
def format_table(result)
589
687
590
688
def format_table(nil), do: ""
591
689
def format_table(%{columns: nil}), do: ""
592
690
def format_table(%{columns: []}), do: ""
593
- def format_table(%{columns: columns, rows: nil}), do: format_table(%{columns: columns, rows: []})
691
692
def format_table(%{columns: columns, rows: nil}),
693
do: format_table(%{columns: columns, rows: []})
863
694
595
695
def format_table(%{columns: columns, rows: rows}) do
596
696
column_widths =
 
@@ -608,32 708,36 @@ defmodule Ecto.Adapters.SQL do
608
708
"\n",
609
709
separator(column_widths),
610
710
"\n",
611
- Enum.map(rows, &cells(&1, column_widths) ["\n"]),
711
Enum.map(rows, &(cells(&1, column_widths) ["\n"])),
612
712
separator(column_widths)
613
713
]
614
714
|> IO.iodata_to_binary()
615
715
end
616
716
617
- defp binary_length(nil), do: 4 # NULL
717
# NULL
718
defp binary_length(nil), do: 4
618
719
defp binary_length(binary) when is_binary(binary), do: String.length(binary)
619
720
defp binary_length(other), do: other |> inspect() |> String.length()
620
721
621
722
defp separator(widths) do
622
- Enum.map(widths, & [? , ?-, String.duplicate("-", &1), ?-]) [? ]
723
Enum.map(widths, &[? , ?-, String.duplicate("-", &1), ?-]) [? ]
623
724
end
624
725
625
726
defp cells(items, widths) do
626
727
cell =
627
728
[items, widths]
628
729
|> List.zip()
629
- |> Enum.map(fn {item, width} -> [?|, " ", format_item(item, width) , " "] end)
730
|> Enum.map(fn {item, width} -> [?|, " ", format_item(item, width), " "] end)
630
731
631
732
[cell | [?|]]
632
733
end
633
734
634
735
defp format_item(nil, width), do: String.pad_trailing("NULL", width)
635
736
defp format_item(item, width) when is_binary(item), do: String.pad_trailing(item, width)
636
- defp format_item(item, width) when is_number(item), do: item |> inspect() |> String.pad_leading(width)
737
738
defp format_item(item, width) when is_number(item),
739
do: item |> inspect() |> String.pad_leading(width)
740
637
741
defp format_item(item, width), do: item |> inspect() |> String.pad_trailing(width)
638
742
639
743
## Callbacks
 
@@ -720,11 824,11 @@ defmodule Ecto.Adapters.SQL do
720
824
def init(connection, driver, config) do
721
825
unless Code.ensure_loaded?(connection) do
722
826
raise """
723
- could not find #{inspect connection}.
827
could not find #{inspect(connection)}.
724
828
725
- Please verify you have added #{inspect driver} as a dependency:
829
Please verify you have added #{inspect(driver)} as a dependency:
726
830
727
- {#{inspect driver}, ">= 0.0.0"}
831
{#{inspect(driver)}, ">= 0.0.0"}
728
832
729
833
And remember to recompile Ecto afterwards by cleaning the current build:
730
834
 
@@ -780,7 884,17 @@ defmodule Ecto.Adapters.SQL do
780
884
## Query
781
885
782
886
@doc false
783
- def insert_all(adapter_meta, schema_meta, conn, header, rows, on_conflict, returning, placeholders, opts) do
887
def insert_all(
888
adapter_meta,
889
schema_meta,
890
conn,
891
header,
892
rows,
893
on_conflict,
894
returning,
895
placeholders,
896
opts
897
) do
784
898
%{source: source, prefix: prefix} = schema_meta
785
899
{_, conflict_params, _} = on_conflict
786
900
 
@@ -792,11 906,12 @@ defmodule Ecto.Adapters.SQL do
792
906
793
907
sql = conn.insert(prefix, source, header, rows, on_conflict, returning, placeholders)
794
908
795
- opts = if is_nil(Keyword.get(opts, :cache_statement)) do
796
- [{:cache_statement, "ecto_insert_all_#{source}"} | opts]
797
- else
798
- opts
799
- end
909
opts =
910
if is_nil(Keyword.get(opts, :cache_statement)) do
911
[{:cache_statement, "ecto_insert_all_#{source}"} | opts]
912
else
913
opts
914
end
800
915
801
916
all_params = placeholders Enum.reverse(params, conflict_params)
802
917
 
@@ -805,8 920,8 @@ defmodule Ecto.Adapters.SQL do
805
920
end
806
921
807
922
defp unzip_inserts(header, rows) do
808
- Enum.map_reduce rows, [], fn fields, params ->
809
- Enum.map_reduce header, params, fn key, acc ->
923
Enum.map_reduce(rows, [], fn fields, params ->
924
Enum.map_reduce(header, params, fn key, acc ->
810
925
case :lists.keyfind(key, 1, fields) do
811
926
{^key, {�to.Query{} = query, query_params}} ->
812
927
{{query, length(query_params)}, Enum.reverse(query_params, acc)}
 
@@ -814,12 929,14 @@ defmodule Ecto.Adapters.SQL do
814
929
{^key, {:placeholder, placeholder_index}} ->
815
930
{{:placeholder, Integer.to_string(placeholder_index)}, acc}
816
931
817
- {^key, value} -> {key, [value | acc]}
932
{^key, value} ->
933
{key, [value | acc]}
818
934
819
- false -> {nil, acc}
935
false ->
936
{nil, acc}
820
937
end
821
- end
822
- end
938
end)
939
end)
823
940
end
824
941
825
942
@doc false
 
@@ -837,42 954,59 @@ defmodule Ecto.Adapters.SQL do
837
954
{:ok, query, result} ->
838
955
maybe_update_cache(prepare, update, {id, query})
839
956
result
957
840
958
{:error, err} ->
841
- raise_sql_call_error err
959
raise_sql_call_error(err)
842
960
end
843
961
end
844
962
845
- defp execute!(:unnamed = prepare, adapter_meta, {:cached, _update, _reset, {id, cached}}, params, opts) do
963
defp execute!(
964
:unnamed = prepare,
965
adapter_meta,
966
{:cached, _update, _reset, {id, cached}},
967
params,
968
opts
969
) do
846
970
name = prepare_name(prepare, id)
847
971
prepared = String.Chars.to_string(cached)
848
972
849
973
case sql_call(adapter_meta, :prepare_execute, [name, prepared], params, opts) do
850
974
{:ok, _query, result} ->
851
975
result
976
852
977
{:error, err} ->
853
- raise_sql_call_error err
978
raise_sql_call_error(err)
854
979
end
855
980
end
856
981
857
- defp execute!(:named = _prepare, adapter_meta, {:cached, update, reset, {id, cached}}, params, opts) do
982
defp execute!(
983
:named = _prepare,
984
adapter_meta,
985
{:cached, update, reset, {id, cached}},
986
params,
987
opts
988
) do
858
989
case sql_call(adapter_meta, :execute, [cached], params, opts) do
859
990
{:ok, query, result} ->
860
991
update.({id, query})
861
992
result
993
862
994
{:ok, result} ->
863
995
result
996
864
997
{:error, err} ->
865
- raise_sql_call_error err
998
raise_sql_call_error(err)
999
866
1000
{:reset, err} ->
867
1001
reset.({id, String.Chars.to_string(cached)})
868
- raise_sql_call_error err
1002
raise_sql_call_error(err)
869
1003
end
870
1004
end
871
1005
872
1006
defp execute!(_prepare, adapter_meta, {:nocache, {_id, prepared}}, params, opts) do
873
1007
case sql_call(adapter_meta, :query, [prepared], params, opts) do
874
1008
{:ok, res} -> res
875
- {:error, err} -> raise_sql_call_error err
1009
{:error, err} -> raise_sql_call_error(err)
876
1010
end
877
1011
end
878
1012
 
@@ -902,7 1036,7 @@ defmodule Ecto.Adapters.SQL do
902
1036
defp prepare_stream(adapter_meta, prepared, params, opts) do
903
1037
adapter_meta
904
1038
|> Ecto.Adapters.SQL.Stream.build(prepared, params, opts)
905
- |> Stream.map(fn(%{num_rows: nrows, rows: rows}) -> {nrows, rows} end)
1039
|> Stream.map(fn %{num_rows: nrows, rows: rows} -> {nrows, rows} end)
906
1040
end
907
1041
908
1042
defp raise_sql_call_error(�Connection.OwnershipError{} = err) do
 
@@ -910,7 1044,7 @@ defmodule Ecto.Adapters.SQL do
910
1044
raise %{err | message: message}
911
1045
end
912
1046
913
- defp raise_sql_call_error(err), do: raise err
1047
defp raise_sql_call_error(err), do: raise(err)
914
1048
915
1049
@doc false
916
1050
def reduce(adapter_meta, statement, params, opts, acc, fun) do
 
@@ -945,12 1079,24 @@ defmodule Ecto.Adapters.SQL do
945
1079
end
946
1080
947
1081
@doc false
948
- def struct(adapter_meta, conn, sql, operation, source, params, values, on_conflict, returning, opts) do
949
- opts = if is_nil(Keyword.get(opts, :cache_statement)) do
950
- [{:cache_statement, "ecto_#{operation}_#{source}_#{length(params)}"} | opts]
951
- else
952
- opts
953
- end
1082
def struct(
1083
adapter_meta,
1084
conn,
1085
sql,
1086
operation,
1087
source,
1088
params,
1089
values,
1090
on_conflict,
1091
returning,
1092
opts
1093
) do
1094
opts =
1095
if is_nil(Keyword.get(opts, :cache_statement)) do
1096
[{:cache_statement, "ecto_#{operation}_#{source}_#{length(params)}"} | opts]
1097
else
1098
opts
1099
end
954
1100
955
1101
case query(adapter_meta, sql, values, opts) do
956
1102
{:ok, %{rows: nil, num_rows: 1}} ->
 
@@ -964,11 1110,14 @@ defmodule Ecto.Adapters.SQL do
964
1110
965
1111
{:ok, %{num_rows: num_rows}} when num_rows > 1 ->
966
1112
raise Ecto.MultiplePrimaryKeyError,
967
- source: source, params: params, count: num_rows, operation: operation
1113
source: source,
1114
params: params,
1115
count: num_rows,
1116
operation: operation
968
1117
969
1118
{:error, err} ->
970
1119
case conn.to_constraints(err, source: source) do
971
- [] -> raise_sql_call_error err
1120
[] -> raise_sql_call_error(err)
972
1121
constraints -> {:invalid, constraints}
973
1122
end
974
1123
end
 
@@ -1163,7 1312,7 @@ defmodule Ecto.Adapters.SQL do
1163
1312
"↳ ",
1164
1313
Exception.format_mfa(module, function, arity),
1165
1314
log_stacktrace_info(info),
1166
- IO.ANSI.reset(),
1315
IO.ANSI.reset()
1167
1316
]
1168
1317
else
1169
1318
_ -> []
changed lib/ecto/adapters/tds/connection.ex
 
@@ -1443,6 1443,9 @@ if Code.ensure_loaded?(Tds) do
1443
1443
1444
1444
defp default_expr(_table, _name, :error), do: []
1445
1445
1446
defp drop_constraint_from_expr({%Reference{} = ref, _opts}, table, name, stm_prefix),
1447
do: drop_constraint_from_expr(ref, table, name, stm_prefix)
1448
1446
1449
defp drop_constraint_from_expr(%Reference{} = ref, table, name, stm_prefix) do
1447
1450
[stm_prefix, "DROP CONSTRAINT ", reference_name(ref, table, name), "; "]
1448
1451
end
changed lib/ecto/migrator.ex
 
@@ -190,10 190,10 @@ defmodule Ecto.Migrator do
190
190
migrations directory. This can be used to specify a custom migrations
191
191
path.
192
192
"""
193
- @spec migrations_path(Ecto.Repo.t, String.t) :: String.t
193
@spec migrations_path(Ecto.Repo.t(), String.t()) :: String.t()
194
194
def migrations_path(repo, directory \\ "migrations") do
195
195
config = repo.config()
196
- priv = config[:priv] || "priv/#{repo |> Module.split |> List.last |> Macro.underscore}"
196
priv = config[:priv] || "priv/#{repo |> Module.split() |> List.last() |> Macro.underscore()}"
197
197
app = Keyword.fetch!(config, :otp_app)
198
198
Application.app_dir(app, Path.join(priv, directory))
199
199
end
 
@@ -215,9 215,9 @@ defmodule Ecto.Migrator do
215
215
commands may fail if this is set to true. Defaults to `false`. Accepts a
216
216
boolean.
217
217
"""
218
- @spec migrated_versions(Ecto.Repo.t, Keyword.t) :: [integer]
218
@spec migrated_versions(Ecto.Repo.t(), Keyword.t()) :: [integer]
219
219
def migrated_versions(repo, opts \\ []) do
220
- lock_for_migrations true, repo, opts, fn _config, versions -> versions end
220
lock_for_migrations(true, repo, opts, fn _config, versions -> versions end)
221
221
end
222
222
223
223
@doc """
 
@@ -242,9 242,9 @@ defmodule Ecto.Migrator do
242
242
* `:strict_version_order` - abort when applying a migration with old timestamp
243
243
(otherwise it emits a warning)
244
244
"""
245
- @spec up(Ecto.Repo.t, integer, module, Keyword.t) :: :ok | :already_up
245
@spec up(Ecto.Repo.t(), integer, module, Keyword.t()) :: :ok | :already_up
246
246
def up(repo, version, module, opts \\ []) do
247
- conditional_lock_for_migrations module, version, repo, opts, fn config, versions ->
247
conditional_lock_for_migrations(module, version, repo, opts, fn config, versions ->
248
248
if version in versions do
249
249
:already_up
250
250
else
 
@@ -268,21 268,23 @@ defmodule Ecto.Migrator do
268
268
if opts[:strict_version_order] do
269
269
raise Ecto.MigrationError, message
270
270
else
271
- Logger.warn message
271
Logger.warn(message)
272
272
end
273
273
end
274
274
275
275
result
276
276
end
277
- end
277
end)
278
278
end
279
279
280
280
defp do_up(repo, config, version, module, opts) do
281
281
async_migrate_maybe_in_transaction(repo, config, version, module, :up, opts, fn ->
282
- attempt(repo, config, version, module, :forward, :up, :up, opts)
283
- || attempt(repo, config, version, module, :forward, :change, :up, opts)
284
- || {:error, Ecto.MigrationError.exception(
285
- "#{inspect module} does not implement a `up/0` or `change/0` function")}
282
attempt(repo, config, version, module, :forward, :up, :up, opts) ||
283
attempt(repo, config, version, module, :forward, :change, :up, opts) ||
284
{:error,
285
Ecto.MigrationError.exception(
286
"#{inspect(module)} does not implement a `up/0` or `change/0` function"
287
)}
286
288
end)
287
289
end
288
290
 
@@ -306,23 308,25 @@ defmodule Ecto.Migrator do
306
308
See `c:Ecto.Repo.put_dynamic_repo/1`.
307
309
308
310
"""
309
- @spec down(Ecto.Repo.t, integer, module) :: :ok | :already_down
311
@spec down(Ecto.Repo.t(), integer, module) :: :ok | :already_down
310
312
def down(repo, version, module, opts \\ []) do
311
- conditional_lock_for_migrations module, version, repo, opts, fn config, versions ->
313
conditional_lock_for_migrations(module, version, repo, opts, fn config, versions ->
312
314
if version in versions do
313
315
do_down(repo, config, version, module, opts)
314
316
else
315
317
:already_down
316
318
end
317
- end
319
end)
318
320
end
319
321
320
322
defp do_down(repo, config, version, module, opts) do
321
323
async_migrate_maybe_in_transaction(repo, config, version, module, :down, opts, fn ->
322
- attempt(repo, config, version, module, :forward, :down, :down, opts)
323
- || attempt(repo, config, version, module, :backward, :change, :down, opts)
324
- || {:error, Ecto.MigrationError.exception(
325
- "#{inspect module} does not implement a `down/0` or `change/0` function")}
324
attempt(repo, config, version, module, :forward, :down, :down, opts) ||
325
attempt(repo, config, version, module, :backward, :change, :down, opts) ||
326
{:error,
327
Ecto.MigrationError.exception(
328
"#{inspect(module)} does not implement a `down/0` or `change/0` function"
329
)}
326
330
end)
327
331
end
328
332
 
@@ -352,13 356,13 @@ defmodule Ecto.Migrator do
352
356
353
357
result
354
358
end
355
- catch kind, reason ->
356
- {kind, reason, __STACKTRACE__}
359
catch
360
kind, reason ->
361
{kind, reason, __STACKTRACE__}
357
362
end
358
363
359
364
defp attempt(repo, config, version, module, direction, operation, reference, opts) do
360
- if Code.ensure_loaded?(module) and
361
- function_exported?(module, operation, 0) do
365
if Code.ensure_loaded?(module) and function_exported?(module, operation, 0) do
362
366
Runner.run(repo, config, version, module, direction, operation, reference, opts)
363
367
:ok
364
368
end
 
@@ -373,7 377,7 @@ defmodule Ecto.Migrator do
373
377
374
378
See `run/4` for more information.
375
379
"""
376
- @spec run(Ecto.Repo.t, atom, Keyword.t) :: [integer]
380
@spec run(Ecto.Repo.t(), atom, Keyword.t()) :: [integer]
377
381
def run(repo, direction, opts) do
378
382
run(repo, [migrations_path(repo)], direction, opts)
379
383
end
 
@@ -420,25 424,33 @@ defmodule Ecto.Migrator do
420
424
421
425
Plus all other options described in `up/4`.
422
426
"""
423
- @spec run(Ecto.Repo.t, String.t | [String.t] | [{integer, module}], atom, Keyword.t) :: [integer]
427
@spec run(Ecto.Repo.t(), String.t() | [String.t()] | [{integer, module}], atom, Keyword.t()) ::
428
[integer]
424
429
def run(repo, migration_source, direction, opts) do
425
430
migration_source = List.wrap(migration_source)
426
431
427
432
pending =
428
- lock_for_migrations true, repo, opts, fn _config, versions ->
433
lock_for_migrations(true, repo, opts, fn _config, versions ->
429
434
cond do
430
435
opts[:all] ->
431
436
pending_all(versions, migration_source, direction)
437
432
438
to = opts[:to] ->
433
439
pending_to(versions, migration_source, direction, to)
440
434
441
to_exclusive = opts[:to_exclusive] ->
435
442
pending_to_exclusive(versions, migration_source, direction, to_exclusive)
443
436
444
step = opts[:step] ->
437
445
pending_step(versions, migration_source, direction, step)
446
438
447
true ->
439
- {:error, ArgumentError.exception("expected one of :all, :to, :to_exclusive, or :step strategies")}
448
{:error,
449
ArgumentError.exception(
450
"expected one of :all, :to, :to_exclusive, or :step strategies"
451
)}
440
452
end
441
- end
453
end)
442
454
443
455
# The lock above already created the table, so we can now skip it.
444
456
opts = Keyword.put(opts, :skip_table_creation, true)
 
@@ -456,7 468,7 @@ defmodule Ecto.Migrator do
456
468
Ecto.Migrator.migrations(repo, [Ecto.Migrator.migrations_path(repo)])
457
469
458
470
"""
459
- @spec migrations(Ecto.Repo.t) :: [{:up | :down, id :: integer(), name :: String.t}]
471
@spec migrations(Ecto.Repo.t()) :: [{:up | :down, id :: integer(), name :: String.t()}]
460
472
def migrations(repo) do
461
473
migrations(repo, [migrations_path(repo)])
462
474
end
 
@@ -465,8 477,8 @@ defmodule Ecto.Migrator do
465
477
Returns an array of tuples as the migration status of the given repo,
466
478
without actually running any migrations.
467
479
"""
468
- @spec migrations(Ecto.Repo.t, String.t | [String.t], Keyword.t) ::
469
- [{:up | :down, id :: integer(), name :: String.t}]
480
@spec migrations(Ecto.Repo.t(), String.t() | [String.t()], Keyword.t()) ::
481
[{:up | :down, id :: integer(), name :: String.t()}]
470
482
def migrations(repo, directories, opts \\ []) do
471
483
directories = List.wrap(directories)
472
484
 
@@ -549,9 561,9 @@ defmodule Ecto.Migrator do
549
561
config = repo.config()
550
562
551
563
unless skip_table_creation do
552
- verbose_schema_migration repo, "create schema migrations table", fn ->
564
verbose_schema_migration(repo, "create schema migrations table", fn ->
553
565
SchemaMigration.ensure_schema_migrations_table!(repo, config, opts)
554
- end
566
end)
555
567
end
556
568
557
569
{migration_repo, query, all_opts} = SchemaMigration.versions(repo, config, opts[:prefix])
 
@@ -602,24 614,27 @@ defmodule Ecto.Migrator do
602
614
within_target_version? = fn
603
615
{version, _, _}, target, :up ->
604
616
version <= target
617
605
618
{version, _, _}, target, :down ->
606
619
version >= target
607
620
end
608
621
609
622
pending_in_direction(versions, migration_source, direction)
610
- |> Enum.take_while(&(within_target_version?.(&1, target, direction)))
623
|> Enum.take_while(&within_target_version?.(&1, target, direction))
611
624
end
612
625
613
- defp pending_to_exclusive(versions, migration_source, direction, target) when is_integer(target) do
626
defp pending_to_exclusive(versions, migration_source, direction, target)
627
when is_integer(target) do
614
628
within_target_version? = fn
615
629
{version, _, _}, target, :up ->
616
630
version < target
631
617
632
{version, _, _}, target, :down ->
618
633
version > target
619
634
end
620
635
621
636
pending_in_direction(versions, migration_source, direction)
622
- |> Enum.take_while(&(within_target_version?.(&1, target, direction)))
637
|> Enum.take_while(&within_target_version?.(&1, target, direction))
623
638
end
624
639
625
640
defp pending_step(versions, migration_source, direction, count) do
 
@@ -634,14 649,14 @@ defmodule Ecto.Migrator do
634
649
defp pending_in_direction(versions, migration_source, :up) do
635
650
migration_source
636
651
|> migrations_for()
637
- |> Enum.filter(fn {version, _name, _file} -> not (version in versions) end)
652
|> Enum.filter(fn {version, _name, _file} -> version not in versions end)
638
653
end
639
654
640
655
defp pending_in_direction(versions, migration_source, :down) do
641
656
migration_source
642
657
|> migrations_for()
643
658
|> Enum.filter(fn {version, _name, _file} -> version in versions end)
644
- |> Enum.reverse
659
|> Enum.reverse()
645
660
end
646
661
647
662
defp migrations_for(migration_source) when is_list(migration_source) do
 
@@ -671,10 686,12 @@ defmodule Ecto.Migrator do
671
686
defp ensure_no_duplication!([{version, name, _} | t]) do
672
687
cond do
673
688
List.keyfind(t, version, 0) ->
674
- raise Ecto.MigrationError, "migrations can't be executed, migration version #{version} is duplicated"
689
raise Ecto.MigrationError,
690
"migrations can't be executed, migration version #{version} is duplicated"
675
691
676
692
List.keyfind(t, name, 1) ->
677
- raise Ecto.MigrationError, "migrations can't be executed, migration name #{name} is duplicated"
693
raise Ecto.MigrationError,
694
"migrations can't be executed, migration name #{name} is duplicated"
678
695
679
696
true ->
680
697
ensure_no_duplication!(t)
 
@@ -697,12 714,13 @@ defmodule Ecto.Migrator do
697
714
if mod = Enum.find(loaded_modules, &migration?/1) do
698
715
{version, mod}
699
716
else
700
- raise Ecto.MigrationError, "file #{Path.relative_to_cwd(file)} does not define an Ecto.Migration"
717
raise Ecto.MigrationError,
718
"file #{Path.relative_to_cwd(file)} does not define an Ecto.Migration"
701
719
end
702
720
end
703
721
704
722
defp migration?(mod) do
705
- function_exported?(mod, :__migration__, 0)
723
Code.ensure_loaded?(mod) and function_exported?(mod, :__migration__, 0)
706
724
end
707
725
708
726
defp migrate([], direction, _repo, opts) do
 
@@ -718,19 736,19 @@ defmodule Ecto.Migrator do
718
736
end
719
737
720
738
defp do_direction(:up, repo, version, mod, opts) do
721
- conditional_lock_for_migrations mod, version, repo, opts, fn config, versions ->
739
conditional_lock_for_migrations(mod, version, repo, opts, fn config, versions ->
722
740
unless version in versions do
723
741
do_up(repo, config, version, mod, opts)
724
742
end
725
- end
743
end)
726
744
end
727
745
728
746
defp do_direction(:down, repo, version, mod, opts) do
729
- conditional_lock_for_migrations mod, version, repo, opts, fn config, versions ->
747
conditional_lock_for_migrations(mod, version, repo, opts, fn config, versions ->
730
748
if version in versions do
731
749
do_down(repo, config, version, mod, opts)
732
750
end
733
- end
751
end)
734
752
end
735
753
736
754
defp verbose_schema_migration(repo, reason, fun) do
 
@@ -738,7 756,7 @@ defmodule Ecto.Migrator do
738
756
fun.()
739
757
rescue
740
758
error ->
741
- Logger.error """
759
Logger.error("""
742
760
Could not #{reason}. This error usually happens due to the following:
743
761
744
762
* The database does not exist
 
@@ -754,19 772,20 @@ defmodule Ecto.Migrator do
754
772
configure Ecto to use another table and/or repository for managing
755
773
migrations:
756
774
757
- config #{inspect repo.config[:otp_app]}, #{inspect repo},
775
config #{inspect(repo.config[:otp_app])}, #{inspect(repo)},
758
776
migration_source: "some_other_table_for_schema_migrations",
759
777
migration_repo: AnotherRepoForSchemaMigrations
760
778
761
779
The full error report is shown below.
762
- """
780
""")
781
763
782
reraise error, __STACKTRACE__
764
783
end
765
784
end
766
785
767
786
defp log(false, _msg), do: :ok
768
787
defp log(true, msg), do: Logger.info(msg)
769
- defp log(level, msg), do: Logger.log(level, msg)
788
defp log(level, msg), do: Logger.log(level, msg)
770
789
771
790
defp migrator_log(opts) do
772
791
Keyword.get(opts, :log_migrator_sql, false)
changed mix.exs
 
@@ -2,7 2,7 @@ defmodule EctoSQL.MixProject do
2
2
use Mix.Project
3
3
4
4
@source_url "https://github.com/elixir-ecto/ecto_sql"
5
- @version "3.10.1"
5
@version "3.10.2"
6
6
@adapters ~w(pg myxql tds)
7
7
8
8
def project do