Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for :duration type #631

Merged
merged 6 commits into from
Aug 10, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
4 changes: 2 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,15 @@ defmodule EctoSQL.MixProject do
if path = System.get_env("ECTO_PATH") do
{:ecto, path: path}
else
{:ecto, github: "elixir-ecto/ecto"}
{:ecto, github: "greg-rychlewski/ecto", branch: "duration_type"}
end
end

defp postgrex_dep 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/greg-rychlewski/ecto.git", "b039e4aaf39b5ba14683ec961e9b1d3e8fb938c9", [branch: "duration_type"]},
"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
Loading