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

Postgres adapter does not cast ActiveSupport::Duration to interval in array condition #44341

Open
aramgre opened this issue Feb 5, 2022 · 2 comments

Comments

@aramgre
Copy link
Contributor

aramgre commented Feb 5, 2022

When passing an ActiveSupport::Duration to an Active Record where query on Postgres, it will be converted to an ISO 8601 string in a hash condition on an interval column, but not in an array condition.

For interval columns, it would be convenient to be able to pass ActiveSupport::Duration in either hash or array conditions. However, some applications probably depend on the existing behavior to pass ActiveSupport::Duration as a numeric type so any change might have to be opt-in.

Steps to reproduce

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  gem "rails", github: "rails/rails", branch: "main"
  gem "pg"
  gem "byebug"
end

require "active_record"
require "minitest/autorun"
require "logger"

# docker run --rm -e POSTGRES_PASSWORD=postgres -p 9999:5432 postgres
ActiveRecord::Base.establish_connection("postgres://postgres:postgres@localhost:9999/postgres")
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :movies, force: true do |t|
    t.interval :runtime
  end
end

class Movie < ActiveRecord::Base
end

class BugTest < Minitest::Test
  def test_duration
    Movie.create!(runtime: 2.hours)
    Movie.create!(runtime: 90.minutes)
    Movie.create!(runtime: 3.hours + 30.minutes)

    # SELECT COUNT(*) FROM "movies" WHERE "movies"."runtime" < $1  [["runtime", "PT3H"]]
    assert_equal 2, Movie.where(runtime: ...3.hours).count

    # SELECT COUNT(*) FROM "movies" WHERE (runtime < 10800)
    # ActiveRecord::StatementInvalid: PG::UndefinedFunction: ERROR:  operator does not exist: interval < integer
    assert_equal 2, Movie.where('runtime < ?', 3.hours).count
  end
end

Expected behavior

When passing a duration in an array condition, it should be converted to a valid interval input (e.g., ISO 8601)

https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-INTERVAL-INPUT

Actual behavior

The duration get converted to an integer seconds which Postgres can't cast to an interval.

System configuration

Rails version:
Using rails 7.1.0.alpha from https://github.com/rails/rails.git (at main@6a55aff)
Ruby version:
ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin19]

@ghiculescu
Copy link
Member

@aramgre I see you made a commit for this aramgre@785f1a6 did you want to make a PR?

@aramgre
Copy link
Contributor Author

aramgre commented Feb 15, 2022

@ghiculescu opened a PR here #44438. However, I'm not sure of the process for breaking changes, please let me know if I need to include a deprecation notice in the PR or create a separate PR with the deprecation notice, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants