Skip to content

Commit ce0927b

Browse files
Add support for :duration type (#631)
1 parent 6350e02 commit ce0927b

10 files changed

Lines changed: 51 additions & 10 deletions

File tree

.github/workflows/ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ jobs:
1010
fail-fast: false
1111
matrix:
1212
include:
13-
- elixir: 1.15.6
14-
otp: 24.3.4.13
15-
- elixir: 1.15.6
16-
otp: 26.1.2
13+
- elixir: 1.17.2
14+
otp: 25.0.4
15+
- elixir: 1.17.2
16+
otp: 27.0.1
1717
lint: lint
1818
steps:
1919
- name: Checkout

integration_test/myxql/test_helper.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ version =
9595
excludes = [
9696
# not sure how to support this yet
9797
:bitstring_type,
98+
:duration_type,
9899
# MySQL does not have an array type
99100
:array_type,
100101
# The next two features rely on RETURNING, which MySQL does not support

integration_test/pg/test_helper.exs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,19 @@ Application.put_env(:ecto_sql, :pg_test_url,
1212

1313
Code.require_file "../support/repo.exs", __DIR__
1414

15+
# Define type module
16+
opts = if Code.ensure_loaded?(Duration), do: [interval_decode_type: Duration], else: []
17+
Postgrex.Types.define(Postgrex.EctoTypes, [], opts)
18+
1519
# Pool repo for async, safe tests
1620
alias Ecto.Integration.TestRepo
1721

1822
Application.put_env(:ecto_sql, TestRepo,
1923
url: Application.get_env(:ecto_sql, :pg_test_url) <> "/ecto_test",
2024
pool: Ecto.Adapters.SQL.Sandbox,
2125
show_sensitive_data_on_connection_error: true,
22-
log: false
26+
log: false,
27+
types: Postgrex.EctoTypes
2328
)
2429

2530
defmodule Ecto.Integration.TestRepo do

integration_test/support/migration.exs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,18 @@ defmodule Ecto.Integration.Migration do
116116
end
117117
end
118118

119+
if Code.ensure_loaded?(Duration) do
120+
unless :duration_type in ExUnit.configuration()[:exclude] do
121+
create table(:durations) do
122+
add :dur, :duration
123+
add :dur_with_fields, :duration, fields: "MONTH"
124+
add :dur_with_precision, :duration, precision: 4
125+
add :dur_with_fields_and_precision, :duration, fields: "HOUR TO SECOND", precision: 1
126+
add :dur_with_default, :duration, default: "10 MONTH"
127+
end
128+
end
129+
end
130+
119131
create table(:composite_pk, primary_key: false) do
120132
add :a, :integer, primary_key: true
121133
add :b, :integer, primary_key: true

integration_test/tds/test_helper.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ ExUnit.start(
55
# not sure how to support this yet
66
:aggregate_filters,
77
:bitstring_type,
8+
:duration_type,
89
# subquery contains ORDER BY and that is not supported
910
:subquery_aggregates,
1011
# sql don't have array type

lib/ecto/adapters/postgres/connection.ex

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1762,6 +1762,23 @@ if Code.ensure_loaded?(Postgrex) do
17621762
end
17631763
end
17641764

1765+
defp column_type(:duration, opts) do
1766+
precision = Keyword.get(opts, :precision)
1767+
fields = Keyword.get(opts, :fields)
1768+
generated = Keyword.get(opts, :generated)
1769+
type_name = ecto_to_db(:duration)
1770+
1771+
type =
1772+
cond do
1773+
fields && precision -> [type_name, " ", fields, ?(, to_string(precision), ?)]
1774+
precision -> [type_name, ?(, to_string(precision), ?)]
1775+
fields -> [type_name, " ", fields]
1776+
true -> [type_name]
1777+
end
1778+
1779+
[type, generated_expr(generated)]
1780+
end
1781+
17651782
defp column_type(type, opts) do
17661783
size = Keyword.get(opts, :size)
17671784
precision = Keyword.get(opts, :precision)
@@ -1981,6 +1998,7 @@ if Code.ensure_loaded?(Postgrex) do
19811998
defp ecto_to_db(:utc_datetime_usec), do: "timestamp"
19821999
defp ecto_to_db(:naive_datetime), do: "timestamp"
19832000
defp ecto_to_db(:naive_datetime_usec), do: "timestamp"
2001+
defp ecto_to_db(:duration), do: "interval"
19842002
defp ecto_to_db(atom) when is_atom(atom), do: Atom.to_string(atom)
19852003

19862004
defp ecto_to_db(type) do

lib/ecto/migration.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,6 +1145,8 @@ defmodule Ecto.Migration do
11451145
generation. Default is defined by the database.
11461146
* `:increment` - option for `:identity` key, represents increment value for
11471147
sequence generation. Default is defined by the database.
1148+
* `:fields` - option for `:duration` type. Restricts the set of stored interval fields
1149+
in the database.
11481150
11491151
"""
11501152
def add(column, type, opts \\ []) when is_atom(column) and is_list(opts) do

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ defmodule EctoSQL.MixProject do
8484
if path = System.get_env("POSTGREX_PATH") do
8585
{:postgrex, path: path}
8686
else
87-
{:postgrex, "~> 0.16 or ~> 1.0", optional: true}
87+
{:postgrex, "~> 0.19 or ~> 1.0", optional: true}
8888
end
8989
end
9090

mix.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
%{
22
"benchee": {:hex, :benchee, "1.2.0", "afd2f0caec06ce3a70d9c91c514c0b58114636db9d83c2dc6bfd416656618353", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "ee729e53217898b8fd30aaad3cce61973dab61574ae6f48229fe7ff42d5e4457"},
3-
"db_connection": {:hex, :db_connection, "2.6.0", "77d835c472b5b67fc4f29556dee74bf511bbafecdcaf98c27d27fa5918152086", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c2f992d15725e721ec7fbc1189d4ecdb8afef76648c746a8e1cad35e3b8a35f3"},
3+
"db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"},
44
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
55
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
66
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
7-
"ecto": {:git, "https://github.com/elixir-ecto/ecto.git", "a898915d2f16dbf1257b8c83a11a1ae07193de42", []},
7+
"ecto": {:git, "https://github.com/elixir-ecto/ecto.git", "9ae70db7946223f36e62c94fa8cf9124bf219bc6", []},
88
"ex_doc": {:hex, :ex_doc, "0.34.0", "ab95e0775db3df71d30cf8d78728dd9261c355c81382bcd4cefdc74610bef13e", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "60734fb4c1353f270c3286df4a0d51e65a2c1d9fba66af3940847cc65a8066d7"},
9-
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
9+
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
1010
"makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"},
1111
"makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"},
1212
"makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"},
1313
"myxql": {:hex, :myxql, "0.7.1", "7c7b75aa82227cd2bc9b7fbd4de774fb19a1cdb309c219f411f82ca8860f8e01", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:geo, "~> 3.4", [hex: :geo, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "a491cdff53353a09b5850ac2d472816ebe19f76c30b0d36a43317a67c9004936"},
1414
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
15-
"postgrex": {:hex, :postgrex, "0.17.3", "c92cda8de2033a7585dae8c61b1d420a1a1322421df84da9a82a6764580c503d", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "946cf46935a4fdca7a81448be76ba3503cff082df42c6ec1ff16a4bdfbfb098d"},
15+
"postgrex": {:hex, :postgrex, "0.19.0", "f7d50e50cb42e0a185f5b9a6095125a9ab7e4abccfbe2ab820ab9aa92b71dbab", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "dba2d2a0a8637defbf2307e8629cb2526388ba7348f67d04ec77a5d6a72ecfae"},
1616
"statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"},
1717
"tds": {:hex, :tds, "2.3.4", "534749dd9ef61af960fcafa9cbb7186d6d7b9f92ea0133fb25da07b121c8295c", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "bb9a53d4688a85fd566f342f76b50d39adfc4b410062886ef908365ead24ba3f"},
1818
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},

test/ecto/adapters/postgres_test.exs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2048,6 +2048,7 @@ defmodule Ecto.Adapters.PostgresTest do
20482048
{:add, :flags, :bitstring, [null: false]},
20492049
{:add, :flags_with_default, :bitstring, [default: <<42::10>>]},
20502050
{:add, :flags_with_size, :bitstring, [size: 10]},
2051+
{:add, :dur, :duration, [fields: "YEAR TO MONTH", precision: 2, default: "1 MONTH"]},
20512052
{:add, :tags, {:array, :string}, [default: []]},
20522053
{:add, :languages, {:array, :string}, [default: ["pt", "es"]]},
20532054
{:add, :limits, {:array, :integer}, [default: [100, 30_000]]}
@@ -2063,6 +2064,7 @@ defmodule Ecto.Adapters.PostgresTest do
20632064
"flags" varbit NOT NULL,
20642065
"flags_with_default" varbit DEFAULT b'0000101010',
20652066
"flags_with_size" varbit(10),
2067+
"dur" interval YEAR TO MONTH(2) DEFAULT '1 MONTH',
20662068
"tags" varchar(255)[] DEFAULT ARRAY[]::varchar[],
20672069
"languages" varchar(255)[] DEFAULT ARRAY['pt','es']::varchar[],
20682070
"limits" integer[] DEFAULT ARRAY[100,30000]::integer[])

0 commit comments

Comments
 (0)