From a1ef12c3afa707e88154c9582eb4aafcbde496de Mon Sep 17 00:00:00 2001 From: Greg Navis Date: Tue, 20 Feb 2024 12:54:04 +0100 Subject: [PATCH] Support IN queries on array columns This commit fixes a bug (or at least an inconsistency) that made it possible to submit IN queries on array columns. A non-array column can be queried via `where(name: [value1, value2])` which is automatically converted to `name IN (value1, value2)` in SQL. An array column would treat that array as the value to search for, so it'd be natural to expect an array of arrays would result in an `IN` query. Unfortunately, that wasn't the case as `where(name: [[value1], [value2]])` would return `name = {{value1}, {value2}}` in SQL, instead of `name IN ({value1}, {value2})`. That behavior stemmed from the fact that Active Record would force equality comparison for array columns if the value was an array. In order to allow `IN` queries this check was change to check whether it's a **one-dimensional** array by checking that the first item of is not an array. After these changes the following logic applies for querying array columns: 1. Using a one-dimensional array results in an ordinary equality query. 2. Using a two-dimensional array produces an `IN` query. --- activerecord/CHANGELOG.md | 7 +++++++ .../connection_adapters/postgresql/oid/array.rb | 2 +- activerecord/test/cases/adapters/postgresql/array_test.rb | 6 ++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index f820b41099fb2..ad6251a8166ca 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,10 @@ +* Support `IN` queries of array columns. + + Use `where(tags: [["tag1"], ["tag2"]])` to produce an `tags IN ({'tag1'}, {'tag2'})`, + similarly to how it works with non-array columns. + + *Greg Navis* + * Add ActiveRecord::Encryption::MessagePackMessageSerializer Serialize data to the MessagePack format, for efficient storage in binary columns. diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb index e46e47102b33f..8df6f434eae3a 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb @@ -73,7 +73,7 @@ def changed_in_place?(raw_old_value, new_value) end def force_equality?(value) - value.is_a?(::Array) + value.is_a?(::Array) && !value.first.is_a?(::Array) end private diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb index a1ac3f76ae79e..4842179bbb72b 100644 --- a/activerecord/test/cases/adapters/postgresql/array_test.rb +++ b/activerecord/test/cases/adapters/postgresql/array_test.rb @@ -347,6 +347,12 @@ def test_where_by_attribute_with_array assert_equal record, PgArray.where(tags: tags).take end + def test_where_by_attribute_with_array_of_arrays + PgArray.create!(tags: ["black"]) + PgArray.create!(tags: ["blue"]) + assert_equal 2, PgArray.where(tags: [["black"], ["blue"]]).count + end + def test_uniqueness_validation klass = Class.new(PgArray) do validates_uniqueness_of :tags