Skip to content

Commit

Permalink
Add support for :duration type (#631)
Browse files Browse the repository at this point in the history
  • Loading branch information
greg-rychlewski authored Aug 10, 2024
1 parent 6350e02 commit ce0927b
Show file tree
Hide file tree
Showing 10 changed files with 51 additions and 10 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ jobs:
fail-fast: false
matrix:
include:
- elixir: 1.15.6
otp: 24.3.4.13
- elixir: 1.15.6
otp: 26.1.2
- elixir: 1.17.2
otp: 25.0.4
- elixir: 1.17.2
otp: 27.0.1
lint: lint
steps:
- name: Checkout
Expand Down
1 change: 1 addition & 0 deletions integration_test/myxql/test_helper.exs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ version =
excludes = [
# not sure how to support this yet
:bitstring_type,
:duration_type,
# MySQL does not have an array type
:array_type,
# The next two features rely on RETURNING, which MySQL does not support
Expand Down
7 changes: 6 additions & 1 deletion integration_test/pg/test_helper.exs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,19 @@ Application.put_env(:ecto_sql, :pg_test_url,

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

# Define type module
opts = if Code.ensure_loaded?(Duration), do: [interval_decode_type: Duration], else: []
Postgrex.Types.define(Postgrex.EctoTypes, [], opts)

# Pool repo for async, safe tests
alias Ecto.Integration.TestRepo

Application.put_env(:ecto_sql, TestRepo,
url: Application.get_env(:ecto_sql, :pg_test_url) <> "/ecto_test",
pool: Ecto.Adapters.SQL.Sandbox,
show_sensitive_data_on_connection_error: true,
log: false
log: false,
types: Postgrex.EctoTypes
)

defmodule Ecto.Integration.TestRepo do
Expand Down
12 changes: 12 additions & 0 deletions integration_test/support/migration.exs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,18 @@ defmodule Ecto.Integration.Migration do
end
end

if Code.ensure_loaded?(Duration) do
unless :duration_type in ExUnit.configuration()[:exclude] do
create table(:durations) do
add :dur, :duration
add :dur_with_fields, :duration, fields: "MONTH"
add :dur_with_precision, :duration, precision: 4
add :dur_with_fields_and_precision, :duration, fields: "HOUR TO SECOND", precision: 1
add :dur_with_default, :duration, default: "10 MONTH"
end
end
end

create table(:composite_pk, primary_key: false) do
add :a, :integer, primary_key: true
add :b, :integer, primary_key: true
Expand Down
1 change: 1 addition & 0 deletions integration_test/tds/test_helper.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ ExUnit.start(
# not sure how to support this yet
:aggregate_filters,
:bitstring_type,
:duration_type,
# subquery contains ORDER BY and that is not supported
:subquery_aggregates,
# sql don't have array type
Expand Down
18 changes: 18 additions & 0 deletions lib/ecto/adapters/postgres/connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1762,6 +1762,23 @@ if Code.ensure_loaded?(Postgrex) do
end
end

defp column_type(:duration, opts) do
precision = Keyword.get(opts, :precision)
fields = Keyword.get(opts, :fields)
generated = Keyword.get(opts, :generated)
type_name = ecto_to_db(:duration)

type =
cond do
fields && precision -> [type_name, " ", fields, ?(, to_string(precision), ?)]
precision -> [type_name, ?(, to_string(precision), ?)]
fields -> [type_name, " ", fields]
true -> [type_name]
end

[type, generated_expr(generated)]
end

defp column_type(type, opts) do
size = Keyword.get(opts, :size)
precision = Keyword.get(opts, :precision)
Expand Down Expand Up @@ -1981,6 +1998,7 @@ if Code.ensure_loaded?(Postgrex) do
defp ecto_to_db(:utc_datetime_usec), do: "timestamp"
defp ecto_to_db(:naive_datetime), do: "timestamp"
defp ecto_to_db(:naive_datetime_usec), do: "timestamp"
defp ecto_to_db(:duration), do: "interval"
defp ecto_to_db(atom) when is_atom(atom), do: Atom.to_string(atom)

defp ecto_to_db(type) do
Expand Down
2 changes: 2 additions & 0 deletions lib/ecto/migration.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1145,6 +1145,8 @@ defmodule Ecto.Migration do
generation. Default is defined by the database.
* `:increment` - option for `:identity` key, represents increment value for
sequence generation. Default is defined by the database.
* `:fields` - option for `:duration` type. Restricts the set of stored interval fields
in the database.
"""
def add(column, type, opts \\ []) when is_atom(column) and is_list(opts) do
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ defmodule EctoSQL.MixProject do
if path = System.get_env("POSTGREX_PATH") do
{:postgrex, path: path}
else
{:postgrex, "~> 0.16 or ~> 1.0", optional: true}
{:postgrex, "~> 0.19 or ~> 1.0", optional: true}
end
end

Expand Down
8 changes: 4 additions & 4 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
%{
"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"},
"db_connection": {:hex, :db_connection, "2.6.0", "77d835c472b5b67fc4f29556dee74bf511bbafecdcaf98c27d27fa5918152086", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c2f992d15725e721ec7fbc1189d4ecdb8afef76648c746a8e1cad35e3b8a35f3"},
"db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"},
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
"ecto": {:git, "https://github.com/elixir-ecto/ecto.git", "a898915d2f16dbf1257b8c83a11a1ae07193de42", []},
"ecto": {:git, "https://github.com/elixir-ecto/ecto.git", "9ae70db7946223f36e62c94fa8cf9124bf219bc6", []},
"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"},
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"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"},
"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"},
"makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"},
"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"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
"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"},
"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"},
"statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"},
"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"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
Expand Down
2 changes: 2 additions & 0 deletions test/ecto/adapters/postgres_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2048,6 +2048,7 @@ defmodule Ecto.Adapters.PostgresTest do
{:add, :flags, :bitstring, [null: false]},
{:add, :flags_with_default, :bitstring, [default: <<42::10>>]},
{:add, :flags_with_size, :bitstring, [size: 10]},
{:add, :dur, :duration, [fields: "YEAR TO MONTH", precision: 2, default: "1 MONTH"]},
{:add, :tags, {:array, :string}, [default: []]},
{:add, :languages, {:array, :string}, [default: ["pt", "es"]]},
{:add, :limits, {:array, :integer}, [default: [100, 30_000]]}
Expand All @@ -2063,6 +2064,7 @@ defmodule Ecto.Adapters.PostgresTest do
"flags" varbit NOT NULL,
"flags_with_default" varbit DEFAULT b'0000101010',
"flags_with_size" varbit(10),
"dur" interval YEAR TO MONTH(2) DEFAULT '1 MONTH',
"tags" varchar(255)[] DEFAULT ARRAY[]::varchar[],
"languages" varchar(255)[] DEFAULT ARRAY['pt','es']::varchar[],
"limits" integer[] DEFAULT ARRAY[100,30000]::integer[])
Expand Down

0 comments on commit ce0927b

Please sign in to comment.