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
|