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 prepared statements and array queries with certain syntax result in PG::InvalidTextRepresentation #51617

Closed
Earlopain opened this issue Apr 20, 2024 · 5 comments

Comments

@Earlopain
Copy link
Contributor

Expected behavior

I have the following testcase which passes on Rails 7.1 but errors on main:

# frozen_string_literal: true

require "bundler/inline"

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

  gem "rails", github: "rails/rails"
  gem "pg"
end

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

ActiveRecord::Base.establish_connection(adapter: "postgresql", host: "localhost", database: "rails")
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :posts, force: true do |t|
    t.integer :ratings, array: true
  end
end

class Post < ActiveRecord::Base
end

class BugTest < Minitest::Test
  def test_association_stuff
    Post.create(ratings: [1, 2, 3])
    p = Post.create(ratings: [4])
    Post.create(ratings: [5, 6, 7])

    Post.connection.unprepared_statement do
      # Passes
      assert_equal([p], Post.where("ratings @> '{?}'", 4))
    end
    # Also passes
    assert_equal([p], Post.where("ratings @> ARRAY[?]::integer[]", 4))

    # PG::InvalidTextRepresentation: ERROR:  invalid input syntax for type integer: "$1"
    # LINE 1: SELECT "posts".* FROM "posts" WHERE (ratings @> '{$1}')
    assert_equal([p], Post.where("ratings @> '{?}'", 4))
  end
end

System configuration

Rails version: master

Ruby version: 3.2.2

@fatkodima
Copy link
Member

This was changed in #51139 (cc @byroot).

In the meantime, this works:

    assert_equal([p], Post.where("ratings @> ?", '{4}'))

@matthewd
Copy link
Member

matthewd commented Apr 20, 2024

Frankly, I feel like this is a [largely accidental] feature -- interpolating ? values into a string literal seems scary and dangerous and deeply inadvisable.

I also don't think there's much we can do about it: it's not reasonable for us to guarantee the exact characters that will be interpolated... at that point you might as well use #{}.

@Earlopain
Copy link
Contributor Author

I won't be heartbroken if this doesn't work anymore. Rails only documents the ARRAY[?]::type[] notation at https://guides.rubyonrails.org/active_record_postgresql.html#array and its trivial to change the few occurrences to that.

A basic GitHub code search yields some results though, see here, so deprecation/better error message? I don't know if this warants that.

@byroot
Copy link
Member

byroot commented Apr 21, 2024

Yeah, I think we can close this. I don't think a specific deprecation would be helpful or needed, this seems extremely niche.

@byroot byroot closed this as completed Apr 21, 2024
Earlopain added a commit to e621ng/e621ng that referenced this issue Apr 21, 2024
@Earlopain
Copy link
Contributor Author

Just for posterity, a better replacement for my case above would be where("? = ANY(ratings)", 4) since only one element is being checked for. The array notation makes sense when checking again multiple elements.

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

4 participants