Skip to content

Commit e370b68

Browse files
committed
Do not auto parameterize PostgreSQL range and multirange types containing expressions
In general, you can only parameterize values, and ranges and multiranges can contain expressions. Check whether the start and end of the range, and whether the elements of the multirange, are themselves parameterizable before using auto parameterization. Note that even if auto parameterization is skipped, range literalization will only work correctly for ranges with types. If a range doesn't have a type, Sequel will try to literalize the value embedded in a string, which won't work for expressions. This doesn't appear to be fixable, because PostgreSQL doesn't offer a function to create an untyped range.
1 parent 0cabeef commit e370b68

File tree

5 files changed

+63
-6
lines changed

5 files changed

+63
-6
lines changed

CHANGELOG

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
=== master
22

3+
* Do not auto parameterize PostgreSQL range and multirange types containing expressions (jeremyevans)
4+
35
* Support sort and reverse methods in pg_array_ops extension on PostgreSQL 18+ (jeremyevans)
46

57
* Support :in_arrays option for Postgres::JSON{,B}#strip_nulls in pg_json_ops extension on PostgreSQL 18+ (jeremyevans)

lib/sequel/extensions/pg_multirange.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ def unquoted_literal(ds)
339339

340340
# Allow automatic parameterization.
341341
def sequel_auto_param_type(ds)
342-
"::#{db_type}"
342+
"::#{db_type}" if all?{|range| range.is_a?(Range) || ds.send(:auto_param_type, range)}
343343
end
344344
end
345345
end

lib/sequel/extensions/pg_range.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -481,9 +481,9 @@ def unquoted_literal(ds)
481481
end
482482
end
483483

484-
# Allow automatic parameterization for ranges with types.
484+
# Allow automatic parameterization for ranges with types, if both start .
485485
def sequel_auto_param_type(ds)
486-
"::#{db_type}" if db_type
486+
"::#{db_type}" if db_type && (!@begin || ds.send(:auto_param_type, @begin)) && (!@end || ds.send(:auto_param_type, @end))
487487
end
488488

489489
private

spec/adapters/postgres_spec.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5190,6 +5190,14 @@ def left_item_id
51905190
@db.get(Sequel.cast(eval('nil...nil'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, 1, :exclude_begin=>true, :db_type=>"int4range")
51915191
end if RUBY_VERSION >= '2.7'
51925192

5193+
it 'handle ranges with expressions' do
5194+
@db.create_table!(:items){Integer :b; Integer :e}
5195+
Sequel.extension :pg_range_ops
5196+
@db[:items].insert(1, 3)
5197+
@db[:items].get(Sequel::Postgres::PGRange.new(:b, :e, :db_type => :int4range).op.contains(2)).must_equal true
5198+
@db[:items].get(Sequel::Postgres::PGRange.new(:b, :e, :db_type => :int4range).op.contains(4)).must_equal false
5199+
end
5200+
51935201
it 'parse default values for schema' do
51945202
@db.create_table!(:items) do
51955203
Integer :j
@@ -5403,6 +5411,14 @@ def left_item_id
54035411
v.each{|k,v1| v1.must_be :==, @pgra[k].to_a}
54045412
end
54055413

5414+
it 'handle multiranges containing ranges with expressions' do
5415+
@db.create_table!(:items){Integer :b; Integer :e}
5416+
Sequel.extension :pg_range_ops
5417+
@db[:items].insert(1, 3)
5418+
@db[:items].get(Sequel.pg_multirange([Sequel::Postgres::PGRange.new(:b, :e, :db_type => :int4range)], :int4multirange).op.contains(2)).must_equal true
5419+
@db[:items].get(Sequel.pg_multirange([Sequel::Postgres::PGRange.new(:b, :e, :db_type => :int4range)], :int4multirange).op.contains(4)).must_equal false
5420+
end
5421+
54065422
it 'operations/functions with pg_range_ops' do
54075423
Sequel.extension :pg_range_ops
54085424
mr = lambda do |range|

spec/extensions/pg_auto_parameterize_spec.rb

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,16 +378,25 @@ def copy_table_sql(ds, *) "COPY TABLE #{ds.is_a?(Sequel::Dataset) ? ds.sql : ds}
378378
sql.args.must_equal [v]
379379
end
380380

381-
it "should automatically parameterize pg_multirange values" do
381+
it "should automatically parameterize pg_multirange values if they are parameterizable" do
382382
@db.extension :pg_multirange
383383
v = Sequel.pg_multirange([1..2, 5..6], :int4multirange)
384384
sql = @db[:table].insert_sql(v)
385385
sql.must_equal 'INSERT INTO "table" VALUES ($1::int4multirange)'
386-
sql.args.length.must_equal 1
387386
sql.args.must_equal [v]
387+
388+
v = Sequel.pg_multirange([Sequel::Postgres::PGRange.new(1, nil, :db_type => :int4range)], :int4multirange)
389+
sql = @db[:table].insert_sql(v)
390+
sql.must_equal 'INSERT INTO "table" VALUES ($1::int4multirange)'
391+
sql.args.must_equal [v]
392+
393+
v = Sequel.pg_multirange([Sequel::Postgres::PGRange.new(1, :en, :db_type => :int4range)], :int4multirange)
394+
sql = @db[:table].insert_sql(v)
395+
sql.must_equal 'INSERT INTO "table" VALUES (int4multirange(int4range($1::int4,"en",$2)))'
396+
sql.args.must_equal [1, "[]"]
388397
end
389398

390-
it "should automatically parameterize pg_range values" do
399+
it "should automatically parameterize pg_range values if they are parameterizable" do
391400
@db.extension :pg_range
392401
v = Sequel.pg_range(1..2, :int4range)
393402
sql = @db[:table].insert_sql(v)
@@ -398,6 +407,36 @@ def copy_table_sql(ds, *) "COPY TABLE #{ds.is_a?(Sequel::Dataset) ? ds.sql : ds}
398407
sql = @db[:table].insert_sql(v)
399408
sql.must_equal 'INSERT INTO "table" VALUES ($1)'
400409
sql.args.must_equal ['[1,2]']
410+
411+
v = Sequel::Postgres::PGRange.new(1, nil, :db_type => :int4range)
412+
sql = @db[:table].insert_sql(v)
413+
sql.must_equal 'INSERT INTO "table" VALUES ($1::int4range)'
414+
sql.args.must_equal [v]
415+
416+
v = Sequel::Postgres::PGRange.new(1, nil)
417+
sql = @db[:table].insert_sql(v)
418+
sql.must_equal 'INSERT INTO "table" VALUES ($1)'
419+
sql.args.must_equal ['[1,]']
420+
421+
v = Sequel::Postgres::PGRange.new(nil, 2, :db_type => :int4range)
422+
sql = @db[:table].insert_sql(v)
423+
sql.must_equal 'INSERT INTO "table" VALUES ($1::int4range)'
424+
sql.args.must_equal [v]
425+
426+
v = Sequel::Postgres::PGRange.new(nil, 2, :exclude_begin => true, :exclude_end => true)
427+
sql = @db[:table].insert_sql(v)
428+
sql.must_equal 'INSERT INTO "table" VALUES ($1)'
429+
sql.args.must_equal ['(,2)']
430+
431+
v = Sequel::Postgres::PGRange.new(:st, 2, :db_type => :int4range)
432+
sql = @db[:table].insert_sql(v)
433+
sql.must_equal 'INSERT INTO "table" VALUES (int4range("st",$1::int4,$2))'
434+
sql.args.must_equal [2, '[]']
435+
436+
v = Sequel::Postgres::PGRange.new(1, :en, :exclude_begin => true, :exclude_end=> true, :db_type => :int4range)
437+
sql = @db[:table].insert_sql(v)
438+
sql.must_equal 'INSERT INTO "table" VALUES (int4range($1::int4,"en",$2))'
439+
sql.args.must_equal [1, '()']
401440
end
402441

403442
it "should automatically parameterize pg_row values if parts are automatically parameterizable" do

0 commit comments

Comments
 (0)