changed
CHANGELOG.md
|
@@ -1,5 1,17 @@
|
1
1
|
# Changelog for v3.x
|
2
2
|
|
3
|
## v3.11.1 (2023-12-07)
|
4
|
|
5
|
### Enhancements
|
6
|
|
7
|
* [Ecto.Migration] Add `:generated` option to columns
|
8
|
* [Ecto.Migration] Add index storage parameters (via :options) for Postgres
|
9
|
|
10
|
### Bug fixes
|
11
|
|
12
|
* [Ecto.Migrations] Support `:prefix` on index rename
|
13
|
* [Ecto.Migrator] Stop runner if migration fails
|
14
|
|
3
15
|
## v3.11.0 (2023-11-14)
|
4
16
|
|
5
17
|
### Enhancements
|
changed
hex_metadata.config
|
@@ -1,10 1,7 @@
|
1
|
- {<<"links">>,[{<<"GitHub">>,<<"https://github.com/elixir-ecto/ecto_sql">>}]}.
|
2
|
- {<<"name">>,<<"ecto_sql">>}.
|
3
|
- {<<"version">>,<<"3.11.0">>}.
|
1
|
{<<"app">>,<<"ecto_sql">>}.
|
2
|
{<<"build_tools">>,[<<"mix">>]}.
|
4
3
|
{<<"description">>,<<"SQL-based adapters for Ecto and database migrations">>}.
|
5
4
|
{<<"elixir">>,<<"~> 1.11">>}.
|
6
|
- {<<"app">>,<<"ecto_sql">>}.
|
7
|
- {<<"licenses">>,[<<"Apache-2.0">>]}.
|
8
5
|
{<<"files">>,
|
9
6
|
[<<".formatter.exs">>,<<"mix.exs">>,<<"README.md">>,<<"CHANGELOG.md">>,
|
10
7
|
<<"lib">>,<<"lib/ecto">>,<<"lib/ecto/adapter">>,
|
|
@@ -40,35 37,38 @@
|
40
37
|
<<"integration_test/support/file_helpers.exs">>,
|
41
38
|
<<"integration_test/support/migration.exs">>,
|
42
39
|
<<"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
|
- [[{<<"name">>,<<"ecto">>},
|
45
|
- {<<"app">>,<<"ecto">>},
|
44
|
[[{<<"app">>,<<"ecto">>},
|
45
|
{<<"name">>,<<"ecto">>},
|
46
46
|
{<<"optional">>,false},
|
47
|
- {<<"requirement">>,<<"~> 3.11.0">>},
|
48
|
- {<<"repository">>,<<"hexpm">>}],
|
49
|
- [{<<"name">>,<<"telemetry">>},
|
50
|
- {<<"app">>,<<"telemetry">>},
|
47
|
{<<"repository">>,<<"hexpm">>},
|
48
|
{<<"requirement">>,<<"~> 3.11.0">>}],
|
49
|
[{<<"app">>,<<"telemetry">>},
|
50
|
{<<"name">>,<<"telemetry">>},
|
51
51
|
{<<"optional">>,false},
|
52
|
- {<<"requirement">>,<<"~> 0.4.0 or ~> 1.0">>},
|
53
|
- {<<"repository">>,<<"hexpm">>}],
|
54
|
- [{<<"name">>,<<"db_connection">>},
|
55
|
- {<<"app">>,<<"db_connection">>},
|
52
|
{<<"repository">>,<<"hexpm">>},
|
53
|
{<<"requirement">>,<<"~> 0.4.0 or ~> 1.0">>}],
|
54
|
[{<<"app">>,<<"db_connection">>},
|
55
|
{<<"name">>,<<"db_connection">>},
|
56
56
|
{<<"optional">>,false},
|
57
|
- {<<"requirement">>,<<"~> 2.5 or ~> 2.4.1">>},
|
58
|
- {<<"repository">>,<<"hexpm">>}],
|
59
|
- [{<<"name">>,<<"postgrex">>},
|
60
|
- {<<"app">>,<<"postgrex">>},
|
57
|
{<<"repository">>,<<"hexpm">>},
|
58
|
{<<"requirement">>,<<"~> 2.5 or ~> 2.4.1">>}],
|
59
|
[{<<"app">>,<<"postgrex">>},
|
60
|
{<<"name">>,<<"postgrex">>},
|
61
61
|
{<<"optional">>,true},
|
62
|
- {<<"requirement">>,<<"~> 0.16.0 or ~> 0.17.0 or ~> 1.0">>},
|
63
|
- {<<"repository">>,<<"hexpm">>}],
|
64
|
- [{<<"name">>,<<"myxql">>},
|
65
|
- {<<"app">>,<<"myxql">>},
|
62
|
{<<"repository">>,<<"hexpm">>},
|
63
|
{<<"requirement">>,<<"~> 0.16.0 or ~> 0.17.0 or ~> 1.0">>}],
|
64
|
[{<<"app">>,<<"myxql">>},
|
65
|
{<<"name">>,<<"myxql">>},
|
66
66
|
{<<"optional">>,true},
|
67
|
- {<<"requirement">>,<<"~> 0.6.0">>},
|
68
|
- {<<"repository">>,<<"hexpm">>}],
|
69
|
- [{<<"name">>,<<"tds">>},
|
70
|
- {<<"app">>,<<"tds">>},
|
67
|
{<<"repository">>,<<"hexpm">>},
|
68
|
{<<"requirement">>,<<"~> 0.6.0">>}],
|
69
|
[{<<"app">>,<<"tds">>},
|
70
|
{<<"name">>,<<"tds">>},
|
71
71
|
{<<"optional">>,true},
|
72
|
- {<<"requirement">>,<<"~> 2.1.1 or ~> 2.2">>},
|
73
|
- {<<"repository">>,<<"hexpm">>}]]}.
|
74
|
- {<<"build_tools">>,[<<"mix">>]}.
|
72
|
{<<"repository">>,<<"hexpm">>},
|
73
|
{<<"requirement">>,<<"~> 2.1.1 or ~> 2.2">>}]]}.
|
74
|
{<<"version">>,<<"3.11.1">>}.
|
changed
integration_test/sql/migrator.exs
|
@@ -112,6 112,7 @@ defmodule Ecto.Integration.MigratorTest do
|
112
112
|
|
113
113
|
test "bad execute migration" do
|
114
114
|
assert catch_error(up(PoolRepo, 31, BadMigration, log: false))
|
115
|
assert DynamicSupervisor.which_children(Ecto.MigratorSupervisor) == []
|
115
116
|
end
|
116
117
|
|
117
118
|
test "broken link migration" do
|
changed
lib/ecto/adapters/myxql/connection.ex
|
@@ -1256,28 1256,46 @@ if Code.ensure_loaded?(MyXQL) do
|
1256
1256
|
defp options_expr(options),
|
1257
1257
|
do: [?\s, to_string(options)]
|
1258
1258
|
|
1259
|
- defp column_type(type, _opts) when type in ~w(time utc_datetime naive_datetime)a,
|
1260
|
- do: ecto_to_db(type)
|
1259
|
defp column_type(type, opts) when type in ~w(time utc_datetime naive_datetime)a do
|
1260
|
generated = Keyword.get(opts, :generated)
|
1261
|
[ecto_to_db(type), generated_expr(generated)]
|
1262
|
end
|
1261
1263
|
|
1262
1264
|
defp column_type(type, opts)
|
1263
1265
|
when type in ~w(time_usec utc_datetime_usec naive_datetime_usec)a do
|
1264
1266
|
precision = Keyword.get(opts, :precision, 6)
|
1267
|
generated = Keyword.get(opts, :generated)
|
1265
1268
|
type_name = ecto_to_db(type)
|
1266
1269
|
|
1267
|
- [type_name, ?(, to_string(precision), ?)]
|
1270
|
[type_name, ?(, to_string(precision), ?), generated_expr(generated)]
|
1268
1271
|
end
|
1269
1272
|
|
1270
1273
|
defp column_type(type, opts) do
|
1271
1274
|
size = Keyword.get(opts, :size)
|
1272
1275
|
precision = Keyword.get(opts, :precision)
|
1276
|
generated = Keyword.get(opts, :generated)
|
1273
1277
|
scale = Keyword.get(opts, :scale)
|
1274
1278
|
|
1275
|
- cond do
|
1276
|
- size -> [ecto_size_to_db(type), ?(, to_string(size), ?)]
|
1277
|
- precision -> [ecto_to_db(type), ?(, to_string(precision), ?,, to_string(scale || 0), ?)]
|
1278
|
- type == :string -> ["varchar(255)"]
|
1279
|
- true -> ecto_to_db(type)
|
1280
|
- end
|
1279
|
type =
|
1280
|
cond do
|
1281
|
size -> [ecto_size_to_db(type), ?(, to_string(size), ?)]
|
1282
|
precision -> [ecto_to_db(type), ?(, to_string(precision), ?,, to_string(scale || 0), ?)]
|
1283
|
type == :string -> ["varchar(255)"]
|
1284
|
true -> ecto_to_db(type)
|
1285
|
end
|
1286
|
|
1287
|
[type, generated_expr(generated)]
|
1288
|
end
|
1289
|
|
1290
|
defp generated_expr(nil), do: []
|
1291
|
|
1292
|
defp generated_expr(expr) when is_binary(expr) do
|
1293
|
[" AS ", expr]
|
1294
|
end
|
1295
|
|
1296
|
defp generated_expr(other) do
|
1297
|
raise ArgumentError,
|
1298
|
"the `:generated` option only accepts strings, received: #{inspect(other)}"
|
1281
1299
|
end
|
1282
1300
|
|
1283
1301
|
defp reference_expr(type, ref, table, name) do
|
changed
lib/ecto/adapters/postgres/connection.ex
|
@@ -230,7 230,7 @@ if Code.ensure_loaded?(Postgrex) do
|
230
230
|
|
231
231
|
[
|
232
232
|
"INSERT INTO ",
|
233
|
- quote_table(prefix, table),
|
233
|
quote_name(prefix, table),
|
234
234
|
insert_as(on_conflict),
|
235
235
|
values,
|
236
236
|
on_conflict(on_conflict, header) | returning(returning)
|
|
@@ -332,7 332,7 @@ if Code.ensure_loaded?(Postgrex) do
|
332
332
|
|
333
333
|
[
|
334
334
|
"UPDATE ",
|
335
|
- quote_table(prefix, table),
|
335
|
quote_name(prefix, table),
|
336
336
|
" SET ",
|
337
337
|
fields,
|
338
338
|
" WHERE ",
|
|
@@ -351,7 351,7 @@ if Code.ensure_loaded?(Postgrex) do
|
351
351
|
{[quote_name(field), " = $" | Integer.to_string(acc)], acc 1}
|
352
352
|
end)
|
353
353
|
|
354
|
- ["DELETE FROM ", quote_table(prefix, table), " WHERE ", filters | returning(returning)]
|
354
|
["DELETE FROM ", quote_name(prefix, table), " WHERE ", filters | returning(returning)]
|
355
355
|
end
|
356
356
|
|
357
357
|
@impl true
|
|
@@ -1136,7 1136,7 @@ if Code.ensure_loaded?(Postgrex) do
|
1136
1136
|
|
1137
1137
|
{table, schema, prefix} ->
|
1138
1138
|
name = as_prefix [create_alias(table) | Integer.to_string(pos)]
|
1139
|
- {quote_table(prefix, table), name, schema}
|
1139
|
{quote_name(prefix, table), name, schema}
|
1140
1140
|
|
1141
1141
|
�to.SubQuery{} ->
|
1142
1142
|
{nil, as_prefix [?s | Integer.to_string(pos)], nil}
|
|
@@ -1160,7 1160,7 @@ if Code.ensure_loaded?(Postgrex) do
|
1160
1160
|
|
1161
1161
|
@impl true
|
1162
1162
|
def execute_ddl({command, %Table{} = table, columns}) when command in @creates do
|
1163
|
- table_name = quote_table(table.prefix, table.name)
|
1163
|
table_name = quote_name(table.prefix, table.name)
|
1164
1164
|
|
1165
1165
|
query = [
|
1166
1166
|
"CREATE TABLE ",
|
|
@@ -1184,14 1184,14 @@ if Code.ensure_loaded?(Postgrex) do
|
1184
1184
|
[
|
1185
1185
|
"DROP TABLE ",
|
1186
1186
|
if_do(command == :drop_if_exists, "IF EXISTS "),
|
1187
|
- quote_table(table.prefix, table.name),
|
1187
|
quote_name(table.prefix, table.name),
|
1188
1188
|
drop_mode(mode)
|
1189
1189
|
]
|
1190
1190
|
]
|
1191
1191
|
end
|
1192
1192
|
|
1193
1193
|
def execute_ddl({:alter, %Table{} = table, changes}) do
|
1194
|
- table_name = quote_table(table.prefix, table.name)
|
1194
|
table_name = quote_name(table.prefix, table.name)
|
1195
1195
|
|
1196
1196
|
query = [
|
1197
1197
|
"ALTER TABLE ",
|
|
@@ -1227,7 1227,7 @@ if Code.ensure_loaded?(Postgrex) do
|
1227
1227
|
quote_name(index.name),
|
1228
1228
|
" ON ",
|
1229
1229
|
if_do(index.only, "ONLY "),
|
1230
|
- quote_table(index.prefix, index.table),
|
1230
|
quote_name(index.prefix, index.table),
|
1231
1231
|
if_do(index.using, [" USING ", to_string(index.using)]),
|
1232
1232
|
?\s,
|
1233
1233
|
?(,
|
|
@@ -1235,11 1235,12 @@ if Code.ensure_loaded?(Postgrex) do
|
1235
1235
|
?),
|
1236
1236
|
if_do(include_fields != [], [" INCLUDE ", ?(, include_fields, ?)]),
|
1237
1237
|
maybe_nulls_distinct,
|
1238
|
if_do(index.options != nil, [" WITH ", ?(, index.options, ?)]),
|
1238
1239
|
if_do(index.where, [" WHERE ", to_string(index.where)])
|
1239
1240
|
]
|
1240
1241
|
]
|
1241
1242
|
|
1242
|
- queries comments_on("INDEX", quote_table(index.prefix, index.name), index.comment)
|
1243
|
queries comments_on("INDEX", quote_name(index.prefix, index.name), index.comment)
|
1243
1244
|
end
|
1244
1245
|
|
1245
1246
|
def execute_ddl({command, %Index{} = index, mode}) when command in @drops do
|
|
@@ -1248,23 1249,30 @@ if Code.ensure_loaded?(Postgrex) do
|
1248
1249
|
"DROP INDEX ",
|
1249
1250
|
if_do(index.concurrently, "CONCURRENTLY "),
|
1250
1251
|
if_do(command == :drop_if_exists, "IF EXISTS "),
|
1251
|
- quote_table(index.prefix, index.name),
|
1252
|
quote_name(index.prefix, index.name),
|
1252
1253
|
drop_mode(mode)
|
1253
1254
|
]
|
1254
1255
|
]
|
1255
1256
|
end
|
1256
1257
|
|
1257
1258
|
def execute_ddl({:rename, %Index{} = current_index, new_name}) do
|
1258
|
- [["ALTER INDEX ", quote_name(current_index.name), " RENAME TO ", quote_name(new_name)]]
|
1259
|
[
|
1260
|
[
|
1261
|
"ALTER INDEX ",
|
1262
|
quote_name(current_index.prefix, current_index.name),
|
1263
|
" RENAME TO ",
|
1264
|
quote_name(new_name)
|
1265
|
]
|
1266
|
]
|
1259
1267
|
end
|
1260
1268
|
|
1261
1269
|
def execute_ddl({:rename, %Table{} = current_table, %Table{} = new_table}) do
|
1262
1270
|
[
|
1263
1271
|
[
|
1264
1272
|
"ALTER TABLE ",
|
1265
|
- quote_table(current_table.prefix, current_table.name),
|
1273
|
quote_name(current_table.prefix, current_table.name),
|
1266
1274
|
" RENAME TO ",
|
1267
|
- quote_table(nil, new_table.name)
|
1275
|
quote_name(nil, new_table.name)
|
1268
1276
|
]
|
1269
1277
|
]
|
1270
1278
|
end
|
|
@@ -1273,7 1281,7 @@ if Code.ensure_loaded?(Postgrex) do
|
1273
1281
|
[
|
1274
1282
|
[
|
1275
1283
|
"ALTER TABLE ",
|
1276
|
- quote_table(table.prefix, table.name),
|
1284
|
quote_name(table.prefix, table.name),
|
1277
1285
|
" RENAME ",
|
1278
1286
|
quote_name(current_column),
|
1279
1287
|
" TO ",
|
|
@@ -1283,7 1291,7 @@ if Code.ensure_loaded?(Postgrex) do
|
1283
1291
|
end
|
1284
1292
|
|
1285
1293
|
def execute_ddl({:create, %Constraint{} = constraint}) do
|
1286
|
- table_name = quote_table(constraint.prefix, constraint.table)
|
1294
|
table_name = quote_name(constraint.prefix, constraint.table)
|
1287
1295
|
queries = [["ALTER TABLE ", table_name, " ADD ", new_constraint_expr(constraint)]]
|
1288
1296
|
|
1289
1297
|
queries comments_on("CONSTRAINT", constraint.name, constraint.comment, table_name)
|
|
@@ -1293,7 1301,7 @@ if Code.ensure_loaded?(Postgrex) do
|
1293
1301
|
[
|
1294
1302
|
[
|
1295
1303
|
"ALTER TABLE ",
|
1296
|
- quote_table(constraint.prefix, constraint.table),
|
1304
|
quote_name(constraint.prefix, constraint.table),
|
1297
1305
|
" DROP CONSTRAINT ",
|
1298
1306
|
if_do(command == :drop_if_exists, "IF EXISTS "),
|
1299
1307
|
quote_name(constraint.name),
|
|
@@ -1610,41 1618,52 @@ if Code.ensure_loaded?(Postgrex) do
|
1610
1618
|
defp column_type({:array, type}, opts),
|
1611
1619
|
do: [column_type(type, opts), "[]"]
|
1612
1620
|
|
1613
|
- defp column_type(type, _opts) when type in ~w(time utc_datetime naive_datetime)a,
|
1614
|
- do: [ecto_to_db(type), "(0)"]
|
1621
|
defp column_type(type, opts) when type in ~w(time utc_datetime naive_datetime)a do
|
1622
|
generated = Keyword.get(opts, :generated)
|
1623
|
[ecto_to_db(type), "(0)", generated_expr(generated)]
|
1624
|
end
|
1615
1625
|
|
1616
1626
|
defp column_type(type, opts)
|
1617
1627
|
when type in ~w(time_usec utc_datetime_usec naive_datetime_usec)a do
|
1618
1628
|
precision = Keyword.get(opts, :precision)
|
1629
|
generated = Keyword.get(opts, :generated)
|
1619
1630
|
type_name = ecto_to_db(type)
|
1620
1631
|
|
1621
|
- if precision do
|
1622
|
- [type_name, ?(, to_string(precision), ?)]
|
1623
|
- else
|
1624
|
- type_name
|
1625
|
- end
|
1632
|
type =
|
1633
|
if precision do
|
1634
|
[type_name, ?(, to_string(precision), ?)]
|
1635
|
else
|
1636
|
type_name
|
1637
|
end
|
1638
|
|
1639
|
[type, generated_expr(generated)]
|
1626
1640
|
end
|
1627
1641
|
|
1628
1642
|
defp column_type(:identity, opts) do
|
1629
1643
|
start_value = [Keyword.get(opts, :start_value)]
|
1630
1644
|
increment = [Keyword.get(opts, :increment)]
|
1645
|
generated = Keyword.get(opts, :generated)
|
1631
1646
|
type_name = ecto_to_db(:identity)
|
1632
1647
|
|
1633
|
- cleanup = fn v -> is_integer(v) and v > 0 end
|
1648
|
if generated do
|
1649
|
[type_name, generated_expr(generated)]
|
1650
|
else
|
1651
|
cleanup = fn v -> is_integer(v) and v > 0 end
|
1634
1652
|
|
1635
|
- sequence =
|
1636
|
- start_value
|
1637
|
- |> Enum.filter(cleanup)
|
1638
|
- |> Enum.map(&"START WITH #{&1}")
|
1639
|
- |> Kernel. (
|
1640
|
- increment
|
1653
|
sequence =
|
1654
|
start_value
|
1641
1655
|
|> Enum.filter(cleanup)
|
1642
|
- |> Enum.map(&"INCREMENT BY #{&1}")
|
1643
|
- )
|
1656
|
|> Enum.map(&"START WITH #{&1}")
|
1657
|
|> Kernel. (
|
1658
|
increment
|
1659
|
|> Enum.filter(cleanup)
|
1660
|
|> Enum.map(&"INCREMENT BY #{&1}")
|
1661
|
)
|
1644
1662
|
|
1645
|
- case sequence do
|
1646
|
- [] -> [type_name, " GENERATED BY DEFAULT AS IDENTITY"]
|
1647
|
- _ -> [type_name, " GENERATED BY DEFAULT AS IDENTITY(", Enum.join(sequence, " "), ") "]
|
1663
|
case sequence do
|
1664
|
[] -> [type_name, " GENERATED BY DEFAULT AS IDENTITY"]
|
1665
|
_ -> [type_name, " GENERATED BY DEFAULT AS IDENTITY(", Enum.join(sequence, " "), ") "]
|
1666
|
end
|
1648
1667
|
end
|
1649
1668
|
end
|
1650
1669
|
|
|
@@ -1652,14 1671,29 @@ if Code.ensure_loaded?(Postgrex) do
|
1652
1671
|
size = Keyword.get(opts, :size)
|
1653
1672
|
precision = Keyword.get(opts, :precision)
|
1654
1673
|
scale = Keyword.get(opts, :scale)
|
1674
|
generated = Keyword.get(opts, :generated)
|
1655
1675
|
type_name = ecto_to_db(type)
|
1656
1676
|
|
1657
|
- cond do
|
1658
|
- size -> [type_name, ?(, to_string(size), ?)]
|
1659
|
- precision -> [type_name, ?(, to_string(precision), ?,, to_string(scale || 0), ?)]
|
1660
|
- type == :string -> [type_name, "(255)"]
|
1661
|
- true -> type_name
|
1662
|
- end
|
1677
|
type =
|
1678
|
cond do
|
1679
|
size -> [type_name, ?(, to_string(size), ?)]
|
1680
|
precision -> [type_name, ?(, to_string(precision), ?,, to_string(scale || 0), ?)]
|
1681
|
type == :string -> [type_name, "(255)"]
|
1682
|
true -> type_name
|
1683
|
end
|
1684
|
|
1685
|
[type, generated_expr(generated)]
|
1686
|
end
|
1687
|
|
1688
|
defp generated_expr(nil), do: []
|
1689
|
|
1690
|
defp generated_expr(expr) when is_binary(expr) do
|
1691
|
[" GENERATED ", expr]
|
1692
|
end
|
1693
|
|
1694
|
defp generated_expr(other) do
|
1695
|
raise ArgumentError,
|
1696
|
"the `:generated` option only accepts strings, received: #{inspect(other)}"
|
1663
1697
|
end
|
1664
1698
|
|
1665
1699
|
defp reference_expr(%Reference{} = ref, table, name) do
|
|
@@ -1672,7 1706,7 @@ if Code.ensure_loaded?(Postgrex) do
|
1672
1706
|
"FOREIGN KEY (",
|
1673
1707
|
quote_names(current_columns),
|
1674
1708
|
") REFERENCES ",
|
1675
|
- quote_table(ref.prefix || table.prefix, ref.table),
|
1709
|
quote_name(ref.prefix || table.prefix, ref.table),
|
1676
1710
|
?(,
|
1677
1711
|
quote_names(reference_columns),
|
1678
1712
|
?),
|
|
@@ -1762,27 1796,17 @@ if Code.ensure_loaded?(Postgrex) do
|
1762
1796
|
Enum.map_intersperse(names, ?,, "e_name/1)
|
1763
1797
|
end
|
1764
1798
|
|
1799
|
defp quote_name(nil, name), do: quote_name(name)
|
1800
|
|
1801
|
defp quote_name(prefix, name), do: [quote_name(prefix), ?., quote_name(name)]
|
1802
|
|
1765
1803
|
defp quote_name(name) when is_atom(name) do
|
1766
1804
|
quote_name(Atom.to_string(name))
|
1767
1805
|
end
|
1768
1806
|
|
1769
1807
|
defp quote_name(name) when is_binary(name) do
|
1770
1808
|
if String.contains?(name, "\"") do
|
1771
|
- error!(nil, "bad literal/field/table name #{inspect(name)} (\" is not permitted)")
|
1772
|
- end
|
1773
|
-
|
1774
|
- [?", name, ?"]
|
1775
|
- end
|
1776
|
-
|
1777
|
- defp quote_table(nil, name), do: quote_table(name)
|
1778
|
- defp quote_table(prefix, name), do: [quote_table(prefix), ?., quote_table(name)]
|
1779
|
-
|
1780
|
- defp quote_table(name) when is_atom(name),
|
1781
|
- do: quote_table(Atom.to_string(name))
|
1782
|
-
|
1783
|
- defp quote_table(name) do
|
1784
|
- if String.contains?(name, "\"") do
|
1785
|
- error!(nil, "bad table name #{inspect(name)}")
|
1809
|
error!(nil, "bad literal/field/index/table name #{inspect(name)} (\" is not permitted)")
|
1786
1810
|
end
|
1787
1811
|
|
1788
1812
|
[?", name, ?"]
|
changed
lib/ecto/adapters/tds/connection.ex
|
@@ -1544,7 1544,19 @@ if Code.ensure_loaded?(Tds) do
|
1544
1544
|
size = Keyword.get(opts, :size)
|
1545
1545
|
precision = Keyword.get(opts, :precision)
|
1546
1546
|
scale = Keyword.get(opts, :scale)
|
1547
|
- ecto_to_db(type, size, precision, scale)
|
1547
|
generated = Keyword.get(opts, :generated)
|
1548
|
[ecto_to_db(type, size, precision, scale), generated_expr(generated)]
|
1549
|
end
|
1550
|
|
1551
|
defp generated_expr(nil), do: []
|
1552
|
|
1553
|
defp generated_expr(expr) when is_binary(expr) do
|
1554
|
[" AS ", expr]
|
1555
|
end
|
1556
|
|
1557
|
defp generated_expr(other) do
|
1558
|
raise ArgumentError,
|
1559
|
"the `:generated` option only accepts strings, received: #{inspect(other)}"
|
1548
1560
|
end
|
1549
1561
|
|
1550
1562
|
defp constraint_expr(%Reference{} = ref, table, name) do
|
changed
lib/ecto/migration.ex
|
@@ -771,25 771,29 @@ defmodule Ecto.Migration do
|
771
771
|
## Options
|
772
772
|
|
773
773
|
* `:name` - the name of the index. Defaults to "#{table}_#{column}_index".
|
774
|
- * `:unique` - indicates whether the index should be unique. Defaults to
|
775
|
- `false`.
|
776
|
- * `:concurrently` - indicates whether the index should be created/dropped
|
777
|
- concurrently.
|
778
|
- * `:using` - configures the index type.
|
779
774
|
* `:prefix` - specify an optional prefix for the index.
|
780
|
- * `:where` - specify conditions for a partial index.
|
781
|
- * `:include` - specify fields for a covering index. This is not supported
|
782
|
- by all databases. For more information on PostgreSQL support, please
|
783
|
- [read the official docs](https://www.postgresql.org/docs/current/indexes-index-only-scans.html).
|
775
|
* `:unique` - indicates whether the index should be unique. Defaults to `false`.
|
776
|
* `:comment` - adds a comment to the index.
|
777
|
* `:using` - configures the index type.
|
778
|
|
779
|
Some options are supported only by some databases:
|
780
|
|
781
|
* `:concurrently` - indicates whether the index should be created/dropped
|
782
|
concurrently in MSSQL and PostgreSQL.
|
783
|
* `:include` - specify fields for a covering index,
|
784
|
[supported by PostgreSQL only](https://www.postgresql.org/docs/current/indexes-index-only-scans.html).
|
784
785
|
* `:nulls_distinct` - specify whether null values should be considered
|
785
786
|
distinct for a unique index. Defaults to `nil`, which will not add the
|
786
787
|
parameter to the generated SQL and thus use the database default.
|
787
788
|
This option is currently only supported by PostgreSQL 15 .
|
788
789
|
For MySQL, it is always false. For MSSQL, it is always true.
|
789
790
|
See the dedicated section on this option for more information.
|
790
|
- * `:only` - Indicates not to recurse creating indexes on partitions, if the table is partitioned.
|
791
|
- This option is currently only supported by PostgreSQL 11 . Defaults to `false`.
|
792
|
- * `:comment` - adds a comment to the index.
|
791
|
* `:only` - indicates to not recurse creating indexes on partitions.
|
792
|
[supported by PostgreSQL only](https://www.postgresql.org/docs/current/ddl-partitioning.html#DDL-PARTITIONING-DECLARATIVE-MAINTENANCE).
|
793
|
* `:options` - configures index options (WITH clause) for both PostgreSQL
|
794
|
and MSSQL
|
795
|
* `:where` - specify conditions for a partial index (PostgreSQL) /
|
796
|
filtered index (MSSQL).
|
793
797
|
|
794
798
|
## Adding/dropping indexes concurrently
|
795
799
|
|
|
@@ -1088,6 1092,9 @@ defmodule Ecto.Migration do
|
1088
1092
|
add :summary, :text # Database type
|
1089
1093
|
add :object, :map # Elixir type which is handled by the database
|
1090
1094
|
add :custom, :'"UserDefinedType"' # A case-sensitive, user-defined type name
|
1095
|
add :identity, :integer, generated: "BY DEFAULT AS IDENTITY" # Postgres generated identity column
|
1096
|
add :generated_psql, :string, generated: "ALWAYS AS (id::text) STORED" # Postgres calculated column
|
1097
|
add :generated_other, :string, generated: "CAST(id AS char)" # MySQL and TDS calculated column
|
1091
1098
|
end
|
1092
1099
|
|
1093
1100
|
## Options
|
|
@@ -1108,6 1115,9 @@ defmodule Ecto.Migration do
|
1108
1115
|
* `:comment` - adds a comment to the added column.
|
1109
1116
|
* `:after` - positions field after the specified one. Only supported on MySQL,
|
1110
1117
|
it is ignored by other databases.
|
1118
|
* `:generated` - a string representing the expression for a generated column. See
|
1119
|
above for a comprehensive set of examples for each of the built-in adapters. If
|
1120
|
specified alongside `:start_value`/`:increment`, those options will be ignored.
|
1111
1121
|
* `:start_value` - option for `:identity` key, represents initial value in sequence
|
1112
1122
|
generation. Default is defined by the database.
|
1113
1123
|
* `:increment` - option for `:identity` key, represents increment value for
|
changed
lib/ecto/migration/runner.ex
|
@@ -24,7 24,7 @@ defmodule Ecto.Migration.Runner do
|
24
24
|
log(level, "== Running #{version} #{inspect(module)}.#{operation}/0 #{direction}")
|
25
25
|
{time, _} = :timer.tc(fn -> perform_operation(repo, module, operation) end)
|
26
26
|
log(level, "== Migrated #{version} in #{inspect(div(time, 100_000) / 10)}s")
|
27
|
-
|
27
|
after
|
28
28
|
stop()
|
29
29
|
end
|
30
30
|
|
|
@@ -460,7 460,7 @@ defmodule Ecto.Migration.Runner do
|
460
460
|
do: "drop index if exists #{quote_name(index.prefix, index.name)}#{drop_mode(mode)}"
|
461
461
|
|
462
462
|
defp command({:rename, %Index{} = index_current, new_name}),
|
463
|
- do: "rename index #{quote_name(index_current.name)} to #{new_name}"
|
463
|
do: "rename index #{quote_name(index_current.prefix, index_current.name)} to #{new_name}"
|
464
464
|
|
465
465
|
defp command({:rename, %Table{} = current_table, %Table{} = new_table}),
|
466
466
|
do:
|
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.11.0"
|
5
|
@version "3.11.1"
|
6
6
|
@adapters ~w(pg myxql tds)
|
7
7
|
|
8
8
|
def project do
|
|
@@ -68,8 68,7 @@ defmodule EctoSQL.MixProject do
|
68
68
|
{:ex_doc, "~> 0.21", only: :docs},
|
69
69
|
|
70
70
|
# Benchmarks
|
71
|
- {:benchee, "~> 0.11.0", only: :bench},
|
72
|
- {:benchee_json, "~> 0.4.0", only: :bench}
|
71
|
{:benchee, "~> 1.0", only: :bench}
|
73
72
|
]
|
74
73
|
end
|